Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @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)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             Roo.log(f.name);
7679             if(f.validate()){
7680                 return;
7681             }
7682             valid = false;
7683
7684             if(!target && f.el.isVisible(true)){
7685                 target = f;
7686             }
7687            
7688         });
7689         
7690         if(this.errorMask && !valid){
7691             Roo.bootstrap.Form.popover.mask(this, target);
7692         }
7693         
7694         return valid;
7695     },
7696     
7697     /**
7698      * Returns true if any fields in this form have changed since their original load.
7699      * @return Boolean
7700      */
7701     isDirty : function(){
7702         var dirty = false;
7703         var items = this.getItems();
7704         items.each(function(f){
7705            if(f.isDirty()){
7706                dirty = true;
7707                return false;
7708            }
7709            return true;
7710         });
7711         return dirty;
7712     },
7713      /**
7714      * Performs a predefined action (submit or load) or custom actions you define on this form.
7715      * @param {String} actionName The name of the action type
7716      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7717      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7718      * accept other config options):
7719      * <pre>
7720 Property          Type             Description
7721 ----------------  ---------------  ----------------------------------------------------------------------------------
7722 url               String           The url for the action (defaults to the form's url)
7723 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7724 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7725 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7726                                    validate the form on the client (defaults to false)
7727      * </pre>
7728      * @return {BasicForm} this
7729      */
7730     doAction : function(action, options){
7731         if(typeof action == 'string'){
7732             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7733         }
7734         if(this.fireEvent('beforeaction', this, action) !== false){
7735             this.beforeAction(action);
7736             action.run.defer(100, action);
7737         }
7738         return this;
7739     },
7740
7741     // private
7742     beforeAction : function(action){
7743         var o = action.options;
7744
7745         if(this.loadMask){
7746             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7747         }
7748         // not really supported yet.. ??
7749
7750         //if(this.waitMsgTarget === true){
7751         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7752         //}else if(this.waitMsgTarget){
7753         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7754         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7755         //}else {
7756         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7757        // }
7758
7759     },
7760
7761     // private
7762     afterAction : function(action, success){
7763         this.activeAction = null;
7764         var o = action.options;
7765
7766         //if(this.waitMsgTarget === true){
7767             this.el.unmask();
7768         //}else if(this.waitMsgTarget){
7769         //    this.waitMsgTarget.unmask();
7770         //}else{
7771         //    Roo.MessageBox.updateProgress(1);
7772         //    Roo.MessageBox.hide();
7773        // }
7774         //
7775         if(success){
7776             if(o.reset){
7777                 this.reset();
7778             }
7779             Roo.callback(o.success, o.scope, [this, action]);
7780             this.fireEvent('actioncomplete', this, action);
7781
7782         }else{
7783
7784             // failure condition..
7785             // we have a scenario where updates need confirming.
7786             // eg. if a locking scenario exists..
7787             // we look for { errors : { needs_confirm : true }} in the response.
7788             if (
7789                 (typeof(action.result) != 'undefined')  &&
7790                 (typeof(action.result.errors) != 'undefined')  &&
7791                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7792            ){
7793                 var _t = this;
7794                 Roo.log("not supported yet");
7795                  /*
7796
7797                 Roo.MessageBox.confirm(
7798                     "Change requires confirmation",
7799                     action.result.errorMsg,
7800                     function(r) {
7801                         if (r != 'yes') {
7802                             return;
7803                         }
7804                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7805                     }
7806
7807                 );
7808                 */
7809
7810
7811                 return;
7812             }
7813
7814             Roo.callback(o.failure, o.scope, [this, action]);
7815             // show an error message if no failed handler is set..
7816             if (!this.hasListener('actionfailed')) {
7817                 Roo.log("need to add dialog support");
7818                 /*
7819                 Roo.MessageBox.alert("Error",
7820                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7821                         action.result.errorMsg :
7822                         "Saving Failed, please check your entries or try again"
7823                 );
7824                 */
7825             }
7826
7827             this.fireEvent('actionfailed', this, action);
7828         }
7829
7830     },
7831     /**
7832      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7833      * @param {String} id The value to search for
7834      * @return Field
7835      */
7836     findField : function(id){
7837         var items = this.getItems();
7838         var field = items.get(id);
7839         if(!field){
7840              items.each(function(f){
7841                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7842                     field = f;
7843                     return false;
7844                 }
7845                 return true;
7846             });
7847         }
7848         return field || null;
7849     },
7850      /**
7851      * Mark fields in this form invalid in bulk.
7852      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7853      * @return {BasicForm} this
7854      */
7855     markInvalid : function(errors){
7856         if(errors instanceof Array){
7857             for(var i = 0, len = errors.length; i < len; i++){
7858                 var fieldError = errors[i];
7859                 var f = this.findField(fieldError.id);
7860                 if(f){
7861                     f.markInvalid(fieldError.msg);
7862                 }
7863             }
7864         }else{
7865             var field, id;
7866             for(id in errors){
7867                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7868                     field.markInvalid(errors[id]);
7869                 }
7870             }
7871         }
7872         //Roo.each(this.childForms || [], function (f) {
7873         //    f.markInvalid(errors);
7874         //});
7875
7876         return this;
7877     },
7878
7879     /**
7880      * Set values for fields in this form in bulk.
7881      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7882      * @return {BasicForm} this
7883      */
7884     setValues : function(values){
7885         if(values instanceof Array){ // array of objects
7886             for(var i = 0, len = values.length; i < len; i++){
7887                 var v = values[i];
7888                 var f = this.findField(v.id);
7889                 if(f){
7890                     f.setValue(v.value);
7891                     if(this.trackResetOnLoad){
7892                         f.originalValue = f.getValue();
7893                     }
7894                 }
7895             }
7896         }else{ // object hash
7897             var field, id;
7898             for(id in values){
7899                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7900
7901                     if (field.setFromData &&
7902                         field.valueField &&
7903                         field.displayField &&
7904                         // combos' with local stores can
7905                         // be queried via setValue()
7906                         // to set their value..
7907                         (field.store && !field.store.isLocal)
7908                         ) {
7909                         // it's a combo
7910                         var sd = { };
7911                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7912                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7913                         field.setFromData(sd);
7914
7915                     } else {
7916                         field.setValue(values[id]);
7917                     }
7918
7919
7920                     if(this.trackResetOnLoad){
7921                         field.originalValue = field.getValue();
7922                     }
7923                 }
7924             }
7925         }
7926
7927         //Roo.each(this.childForms || [], function (f) {
7928         //    f.setValues(values);
7929         //});
7930
7931         return this;
7932     },
7933
7934     /**
7935      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7936      * they are returned as an array.
7937      * @param {Boolean} asString
7938      * @return {Object}
7939      */
7940     getValues : function(asString){
7941         //if (this.childForms) {
7942             // copy values from the child forms
7943         //    Roo.each(this.childForms, function (f) {
7944         //        this.setValues(f.getValues());
7945         //    }, this);
7946         //}
7947
7948
7949
7950         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7951         if(asString === true){
7952             return fs;
7953         }
7954         return Roo.urlDecode(fs);
7955     },
7956
7957     /**
7958      * Returns the fields in this form as an object with key/value pairs.
7959      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7960      * @return {Object}
7961      */
7962     getFieldValues : function(with_hidden)
7963     {
7964         var items = this.getItems();
7965         var ret = {};
7966         items.each(function(f){
7967             if (!f.getName()) {
7968                 return;
7969             }
7970             var v = f.getValue();
7971             if (f.inputType =='radio') {
7972                 if (typeof(ret[f.getName()]) == 'undefined') {
7973                     ret[f.getName()] = ''; // empty..
7974                 }
7975
7976                 if (!f.el.dom.checked) {
7977                     return;
7978
7979                 }
7980                 v = f.el.dom.value;
7981
7982             }
7983
7984             // not sure if this supported any more..
7985             if ((typeof(v) == 'object') && f.getRawValue) {
7986                 v = f.getRawValue() ; // dates..
7987             }
7988             // combo boxes where name != hiddenName...
7989             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7990                 ret[f.name] = f.getRawValue();
7991             }
7992             ret[f.getName()] = v;
7993         });
7994
7995         return ret;
7996     },
7997
7998     /**
7999      * Clears all invalid messages in this form.
8000      * @return {BasicForm} this
8001      */
8002     clearInvalid : function(){
8003         var items = this.getItems();
8004
8005         items.each(function(f){
8006            f.clearInvalid();
8007         });
8008
8009
8010
8011         return this;
8012     },
8013
8014     /**
8015      * Resets this form.
8016      * @return {BasicForm} this
8017      */
8018     reset : function(){
8019         var items = this.getItems();
8020         items.each(function(f){
8021             f.reset();
8022         });
8023
8024         Roo.each(this.childForms || [], function (f) {
8025             f.reset();
8026         });
8027
8028
8029         return this;
8030     },
8031     getItems : function()
8032     {
8033         var r=new Roo.util.MixedCollection(false, function(o){
8034             return o.id || (o.id = Roo.id());
8035         });
8036         var iter = function(el) {
8037             if (el.inputEl) {
8038                 r.add(el);
8039             }
8040             if (!el.items) {
8041                 return;
8042             }
8043             Roo.each(el.items,function(e) {
8044                 iter(e);
8045             });
8046
8047
8048         };
8049
8050         iter(this);
8051         return r;
8052
8053
8054
8055
8056     }
8057
8058 });
8059
8060 Roo.apply(Roo.bootstrap.Form, {
8061     
8062     popover : {
8063         
8064         padding : 5,
8065         
8066         isApplied : false,
8067         
8068         isMasked : false,
8069         
8070         form : false,
8071         
8072         target : false,
8073         
8074         toolTip : false,
8075         
8076         intervalID : false,
8077         
8078         maskEl : false,
8079         
8080         apply : function()
8081         {
8082             if(this.isApplied){
8083                 return;
8084             }
8085             
8086             this.maskEl = {
8087                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8088                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8089                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8090                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8091             };
8092             
8093             this.maskEl.top.enableDisplayMode("block");
8094             this.maskEl.left.enableDisplayMode("block");
8095             this.maskEl.bottom.enableDisplayMode("block");
8096             this.maskEl.right.enableDisplayMode("block");
8097             
8098             this.toolTip = new Roo.bootstrap.Tooltip({
8099                 cls : 'roo-form-error-popover',
8100                 alignment : {
8101                     'left' : ['r-l', [-2,0], 'right'],
8102                     'right' : ['l-r', [2,0], 'left'],
8103                     'bottom' : ['tl-bl', [0,2], 'top'],
8104                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8105                 }
8106             });
8107             
8108             this.toolTip.render(Roo.get(document.body));
8109
8110             this.toolTip.el.enableDisplayMode("block");
8111             
8112             Roo.get(document.body).on('click', function(){
8113                 this.unmask();
8114             }, this);
8115             
8116             Roo.get(document.body).on('touchstart', function(){
8117                 this.unmask();
8118             }, this);
8119             
8120             this.isApplied = true
8121         },
8122         
8123         mask : function(form, target)
8124         {
8125             this.form = form;
8126             
8127             this.target = target;
8128             
8129             if(!this.form.errorMask || !target.el){
8130                 return;
8131             }
8132             
8133             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8134             
8135             Roo.log(scrollable);
8136             
8137             var ot = this.target.el.calcOffsetsTo(scrollable);
8138             
8139             var scrollTo = ot[1] - this.form.maskOffset;
8140             
8141             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8142             
8143             scrollable.scrollTo('top', scrollTo);
8144             
8145             var box = this.target.el.getBox();
8146             Roo.log(box);
8147             var zIndex = Roo.bootstrap.Modal.zIndex++;
8148
8149             
8150             this.maskEl.top.setStyle('position', 'absolute');
8151             this.maskEl.top.setStyle('z-index', zIndex);
8152             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8153             this.maskEl.top.setLeft(0);
8154             this.maskEl.top.setTop(0);
8155             this.maskEl.top.show();
8156             
8157             this.maskEl.left.setStyle('position', 'absolute');
8158             this.maskEl.left.setStyle('z-index', zIndex);
8159             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8160             this.maskEl.left.setLeft(0);
8161             this.maskEl.left.setTop(box.y - this.padding);
8162             this.maskEl.left.show();
8163
8164             this.maskEl.bottom.setStyle('position', 'absolute');
8165             this.maskEl.bottom.setStyle('z-index', zIndex);
8166             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8167             this.maskEl.bottom.setLeft(0);
8168             this.maskEl.bottom.setTop(box.bottom + this.padding);
8169             this.maskEl.bottom.show();
8170
8171             this.maskEl.right.setStyle('position', 'absolute');
8172             this.maskEl.right.setStyle('z-index', zIndex);
8173             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8174             this.maskEl.right.setLeft(box.right + this.padding);
8175             this.maskEl.right.setTop(box.y - this.padding);
8176             this.maskEl.right.show();
8177
8178             this.toolTip.bindEl = this.target.el;
8179
8180             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8181
8182             var tip = this.target.blankText;
8183
8184             if(this.target.getValue() !== '' ) {
8185                 
8186                 if (this.target.invalidText.length) {
8187                     tip = this.target.invalidText;
8188                 } else if (this.target.regexText.length){
8189                     tip = this.target.regexText;
8190                 }
8191             }
8192
8193             this.toolTip.show(tip);
8194
8195             this.intervalID = window.setInterval(function() {
8196                 Roo.bootstrap.Form.popover.unmask();
8197             }, 10000);
8198
8199             window.onwheel = function(){ return false;};
8200             
8201             (function(){ this.isMasked = true; }).defer(500, this);
8202             
8203         },
8204         
8205         unmask : function()
8206         {
8207             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8208                 return;
8209             }
8210             
8211             this.maskEl.top.setStyle('position', 'absolute');
8212             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8213             this.maskEl.top.hide();
8214
8215             this.maskEl.left.setStyle('position', 'absolute');
8216             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8217             this.maskEl.left.hide();
8218
8219             this.maskEl.bottom.setStyle('position', 'absolute');
8220             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.bottom.hide();
8222
8223             this.maskEl.right.setStyle('position', 'absolute');
8224             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.right.hide();
8226             
8227             this.toolTip.hide();
8228             
8229             this.toolTip.el.hide();
8230             
8231             window.onwheel = function(){ return true;};
8232             
8233             if(this.intervalID){
8234                 window.clearInterval(this.intervalID);
8235                 this.intervalID = false;
8236             }
8237             
8238             this.isMasked = false;
8239             
8240         }
8241         
8242     }
8243     
8244 });
8245
8246 /*
8247  * Based on:
8248  * Ext JS Library 1.1.1
8249  * Copyright(c) 2006-2007, Ext JS, LLC.
8250  *
8251  * Originally Released Under LGPL - original licence link has changed is not relivant.
8252  *
8253  * Fork - LGPL
8254  * <script type="text/javascript">
8255  */
8256 /**
8257  * @class Roo.form.VTypes
8258  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8259  * @singleton
8260  */
8261 Roo.form.VTypes = function(){
8262     // closure these in so they are only created once.
8263     var alpha = /^[a-zA-Z_]+$/;
8264     var alphanum = /^[a-zA-Z0-9_]+$/;
8265     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8266     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8267
8268     // All these messages and functions are configurable
8269     return {
8270         /**
8271          * The function used to validate email addresses
8272          * @param {String} value The email address
8273          */
8274         'email' : function(v){
8275             return email.test(v);
8276         },
8277         /**
8278          * The error text to display when the email validation function returns false
8279          * @type String
8280          */
8281         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8282         /**
8283          * The keystroke filter mask to be applied on email input
8284          * @type RegExp
8285          */
8286         'emailMask' : /[a-z0-9_\.\-@]/i,
8287
8288         /**
8289          * The function used to validate URLs
8290          * @param {String} value The URL
8291          */
8292         'url' : function(v){
8293             return url.test(v);
8294         },
8295         /**
8296          * The error text to display when the url validation function returns false
8297          * @type String
8298          */
8299         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8300         
8301         /**
8302          * The function used to validate alpha values
8303          * @param {String} value The value
8304          */
8305         'alpha' : function(v){
8306             return alpha.test(v);
8307         },
8308         /**
8309          * The error text to display when the alpha validation function returns false
8310          * @type String
8311          */
8312         'alphaText' : 'This field should only contain letters and _',
8313         /**
8314          * The keystroke filter mask to be applied on alpha input
8315          * @type RegExp
8316          */
8317         'alphaMask' : /[a-z_]/i,
8318
8319         /**
8320          * The function used to validate alphanumeric values
8321          * @param {String} value The value
8322          */
8323         'alphanum' : function(v){
8324             return alphanum.test(v);
8325         },
8326         /**
8327          * The error text to display when the alphanumeric validation function returns false
8328          * @type String
8329          */
8330         'alphanumText' : 'This field should only contain letters, numbers and _',
8331         /**
8332          * The keystroke filter mask to be applied on alphanumeric input
8333          * @type RegExp
8334          */
8335         'alphanumMask' : /[a-z0-9_]/i
8336     };
8337 }();/*
8338  * - LGPL
8339  *
8340  * Input
8341  * 
8342  */
8343
8344 /**
8345  * @class Roo.bootstrap.Input
8346  * @extends Roo.bootstrap.Component
8347  * Bootstrap Input class
8348  * @cfg {Boolean} disabled is it disabled
8349  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8350  * @cfg {String} name name of the input
8351  * @cfg {string} fieldLabel - the label associated
8352  * @cfg {string} placeholder - placeholder to put in text.
8353  * @cfg {string}  before - input group add on before
8354  * @cfg {string} after - input group add on after
8355  * @cfg {string} size - (lg|sm) or leave empty..
8356  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8357  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8358  * @cfg {Number} md colspan out of 12 for computer-sized screens
8359  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8360  * @cfg {string} value default value of the input
8361  * @cfg {Number} labelWidth set the width of label 
8362  * @cfg {Number} labellg set the width of label (1-12)
8363  * @cfg {Number} labelmd set the width of label (1-12)
8364  * @cfg {Number} labelsm set the width of label (1-12)
8365  * @cfg {Number} labelxs set the width of label (1-12)
8366  * @cfg {String} labelAlign (top|left)
8367  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8368  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8369  * @cfg {String} indicatorpos (left|right) default left
8370
8371  * @cfg {String} align (left|center|right) Default left
8372  * @cfg {Boolean} forceFeedback (true|false) Default false
8373  * 
8374  * 
8375  * 
8376  * 
8377  * @constructor
8378  * Create a new Input
8379  * @param {Object} config The config object
8380  */
8381
8382 Roo.bootstrap.Input = function(config){
8383     
8384     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8385     
8386     this.addEvents({
8387         /**
8388          * @event focus
8389          * Fires when this field receives input focus.
8390          * @param {Roo.form.Field} this
8391          */
8392         focus : true,
8393         /**
8394          * @event blur
8395          * Fires when this field loses input focus.
8396          * @param {Roo.form.Field} this
8397          */
8398         blur : true,
8399         /**
8400          * @event specialkey
8401          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8402          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8403          * @param {Roo.form.Field} this
8404          * @param {Roo.EventObject} e The event object
8405          */
8406         specialkey : true,
8407         /**
8408          * @event change
8409          * Fires just before the field blurs if the field value has changed.
8410          * @param {Roo.form.Field} this
8411          * @param {Mixed} newValue The new value
8412          * @param {Mixed} oldValue The original value
8413          */
8414         change : true,
8415         /**
8416          * @event invalid
8417          * Fires after the field has been marked as invalid.
8418          * @param {Roo.form.Field} this
8419          * @param {String} msg The validation message
8420          */
8421         invalid : true,
8422         /**
8423          * @event valid
8424          * Fires after the field has been validated with no errors.
8425          * @param {Roo.form.Field} this
8426          */
8427         valid : true,
8428          /**
8429          * @event keyup
8430          * Fires after the key up
8431          * @param {Roo.form.Field} this
8432          * @param {Roo.EventObject}  e The event Object
8433          */
8434         keyup : true
8435     });
8436 };
8437
8438 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8439      /**
8440      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8441       automatic validation (defaults to "keyup").
8442      */
8443     validationEvent : "keyup",
8444      /**
8445      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8446      */
8447     validateOnBlur : true,
8448     /**
8449      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8450      */
8451     validationDelay : 250,
8452      /**
8453      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8454      */
8455     focusClass : "x-form-focus",  // not needed???
8456     
8457        
8458     /**
8459      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8460      */
8461     invalidClass : "has-warning",
8462     
8463     /**
8464      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8465      */
8466     validClass : "has-success",
8467     
8468     /**
8469      * @cfg {Boolean} hasFeedback (true|false) default true
8470      */
8471     hasFeedback : true,
8472     
8473     /**
8474      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8475      */
8476     invalidFeedbackClass : "glyphicon-warning-sign",
8477     
8478     /**
8479      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8480      */
8481     validFeedbackClass : "glyphicon-ok",
8482     
8483     /**
8484      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8485      */
8486     selectOnFocus : false,
8487     
8488      /**
8489      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8490      */
8491     maskRe : null,
8492        /**
8493      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8494      */
8495     vtype : null,
8496     
8497       /**
8498      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8499      */
8500     disableKeyFilter : false,
8501     
8502        /**
8503      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8504      */
8505     disabled : false,
8506      /**
8507      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8508      */
8509     allowBlank : true,
8510     /**
8511      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8512      */
8513     blankText : "Please complete this mandatory field",
8514     
8515      /**
8516      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8517      */
8518     minLength : 0,
8519     /**
8520      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8521      */
8522     maxLength : Number.MAX_VALUE,
8523     /**
8524      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8525      */
8526     minLengthText : "The minimum length for this field is {0}",
8527     /**
8528      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8529      */
8530     maxLengthText : "The maximum length for this field is {0}",
8531   
8532     
8533     /**
8534      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8535      * If available, this function will be called only after the basic validators all return true, and will be passed the
8536      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8537      */
8538     validator : null,
8539     /**
8540      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8541      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8542      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8543      */
8544     regex : null,
8545     /**
8546      * @cfg {String} regexText -- Depricated - use Invalid Text
8547      */
8548     regexText : "",
8549     
8550     /**
8551      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8552      */
8553     invalidText : "",
8554     
8555     
8556     
8557     autocomplete: false,
8558     
8559     
8560     fieldLabel : '',
8561     inputType : 'text',
8562     
8563     name : false,
8564     placeholder: false,
8565     before : false,
8566     after : false,
8567     size : false,
8568     hasFocus : false,
8569     preventMark: false,
8570     isFormField : true,
8571     value : '',
8572     labelWidth : 2,
8573     labelAlign : false,
8574     readOnly : false,
8575     align : false,
8576     formatedValue : false,
8577     forceFeedback : false,
8578     
8579     indicatorpos : 'left',
8580     
8581     labellg : 0,
8582     labelmd : 0,
8583     labelsm : 0,
8584     labelxs : 0,
8585     
8586     parentLabelAlign : function()
8587     {
8588         var parent = this;
8589         while (parent.parent()) {
8590             parent = parent.parent();
8591             if (typeof(parent.labelAlign) !='undefined') {
8592                 return parent.labelAlign;
8593             }
8594         }
8595         return 'left';
8596         
8597     },
8598     
8599     getAutoCreate : function()
8600     {
8601         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8602         
8603         var id = Roo.id();
8604         
8605         var cfg = {};
8606         
8607         if(this.inputType != 'hidden'){
8608             cfg.cls = 'form-group' //input-group
8609         }
8610         
8611         var input =  {
8612             tag: 'input',
8613             id : id,
8614             type : this.inputType,
8615             value : this.value,
8616             cls : 'form-control',
8617             placeholder : this.placeholder || '',
8618             autocomplete : this.autocomplete || 'new-password'
8619         };
8620         
8621         if(this.align){
8622             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8623         }
8624         
8625         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8626             input.maxLength = this.maxLength;
8627         }
8628         
8629         if (this.disabled) {
8630             input.disabled=true;
8631         }
8632         
8633         if (this.readOnly) {
8634             input.readonly=true;
8635         }
8636         
8637         if (this.name) {
8638             input.name = this.name;
8639         }
8640         
8641         if (this.size) {
8642             input.cls += ' input-' + this.size;
8643         }
8644         
8645         var settings=this;
8646         ['xs','sm','md','lg'].map(function(size){
8647             if (settings[size]) {
8648                 cfg.cls += ' col-' + size + '-' + settings[size];
8649             }
8650         });
8651         
8652         var inputblock = input;
8653         
8654         var feedback = {
8655             tag: 'span',
8656             cls: 'glyphicon form-control-feedback'
8657         };
8658             
8659         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8660             
8661             inputblock = {
8662                 cls : 'has-feedback',
8663                 cn :  [
8664                     input,
8665                     feedback
8666                 ] 
8667             };  
8668         }
8669         
8670         if (this.before || this.after) {
8671             
8672             inputblock = {
8673                 cls : 'input-group',
8674                 cn :  [] 
8675             };
8676             
8677             if (this.before && typeof(this.before) == 'string') {
8678                 
8679                 inputblock.cn.push({
8680                     tag :'span',
8681                     cls : 'roo-input-before input-group-addon',
8682                     html : this.before
8683                 });
8684             }
8685             if (this.before && typeof(this.before) == 'object') {
8686                 this.before = Roo.factory(this.before);
8687                 
8688                 inputblock.cn.push({
8689                     tag :'span',
8690                     cls : 'roo-input-before input-group-' +
8691                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8692                 });
8693             }
8694             
8695             inputblock.cn.push(input);
8696             
8697             if (this.after && typeof(this.after) == 'string') {
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-after input-group-addon',
8701                     html : this.after
8702                 });
8703             }
8704             if (this.after && typeof(this.after) == 'object') {
8705                 this.after = Roo.factory(this.after);
8706                 
8707                 inputblock.cn.push({
8708                     tag :'span',
8709                     cls : 'roo-input-after input-group-' +
8710                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8711                 });
8712             }
8713             
8714             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8715                 inputblock.cls += ' has-feedback';
8716                 inputblock.cn.push(feedback);
8717             }
8718         };
8719         
8720         if (align ==='left' && this.fieldLabel.length) {
8721             
8722             cfg.cls += ' roo-form-group-label-left';
8723             
8724             cfg.cn = [
8725                 {
8726                     tag : 'i',
8727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8728                     tooltip : 'This field is required'
8729                 },
8730                 {
8731                     tag: 'label',
8732                     'for' :  id,
8733                     cls : 'control-label',
8734                     html : this.fieldLabel
8735
8736                 },
8737                 {
8738                     cls : "", 
8739                     cn: [
8740                         inputblock
8741                     ]
8742                 }
8743             ];
8744             
8745             var labelCfg = cfg.cn[1];
8746             var contentCfg = cfg.cn[2];
8747             
8748             if(this.indicatorpos == 'right'){
8749                 cfg.cn = [
8750                     {
8751                         tag: 'label',
8752                         'for' :  id,
8753                         cls : 'control-label',
8754                         cn : [
8755                             {
8756                                 tag : 'span',
8757                                 html : this.fieldLabel
8758                             },
8759                             {
8760                                 tag : 'i',
8761                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8762                                 tooltip : 'This field is required'
8763                             }
8764                         ]
8765                     },
8766                     {
8767                         cls : "",
8768                         cn: [
8769                             inputblock
8770                         ]
8771                     }
8772
8773                 ];
8774                 
8775                 labelCfg = cfg.cn[0];
8776                 contentCfg = cfg.cn[1];
8777             
8778             }
8779             
8780             if(this.labelWidth > 12){
8781                 labelCfg.style = "width: " + this.labelWidth + 'px';
8782             }
8783             
8784             if(this.labelWidth < 13 && this.labelmd == 0){
8785                 this.labelmd = this.labelWidth;
8786             }
8787             
8788             if(this.labellg > 0){
8789                 labelCfg.cls += ' col-lg-' + this.labellg;
8790                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8791             }
8792             
8793             if(this.labelmd > 0){
8794                 labelCfg.cls += ' col-md-' + this.labelmd;
8795                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8796             }
8797             
8798             if(this.labelsm > 0){
8799                 labelCfg.cls += ' col-sm-' + this.labelsm;
8800                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8801             }
8802             
8803             if(this.labelxs > 0){
8804                 labelCfg.cls += ' col-xs-' + this.labelxs;
8805                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8806             }
8807             
8808             
8809         } else if ( this.fieldLabel.length) {
8810                 
8811             cfg.cn = [
8812                 {
8813                     tag : 'i',
8814                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8815                     tooltip : 'This field is required'
8816                 },
8817                 {
8818                     tag: 'label',
8819                    //cls : 'input-group-addon',
8820                     html : this.fieldLabel
8821
8822                 },
8823
8824                inputblock
8825
8826            ];
8827            
8828            if(this.indicatorpos == 'right'){
8829                 
8830                 cfg.cn = [
8831                     {
8832                         tag: 'label',
8833                        //cls : 'input-group-addon',
8834                         html : this.fieldLabel
8835
8836                     },
8837                     {
8838                         tag : 'i',
8839                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8840                         tooltip : 'This field is required'
8841                     },
8842
8843                    inputblock
8844
8845                ];
8846
8847             }
8848
8849         } else {
8850             
8851             cfg.cn = [
8852
8853                     inputblock
8854
8855             ];
8856                 
8857                 
8858         };
8859         
8860         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8861            cfg.cls += ' navbar-form';
8862         }
8863         
8864         if (this.parentType === 'NavGroup') {
8865            cfg.cls += ' navbar-form';
8866            cfg.tag = 'li';
8867         }
8868         
8869         return cfg;
8870         
8871     },
8872     /**
8873      * return the real input element.
8874      */
8875     inputEl: function ()
8876     {
8877         return this.el.select('input.form-control',true).first();
8878     },
8879     
8880     tooltipEl : function()
8881     {
8882         return this.inputEl();
8883     },
8884     
8885     indicatorEl : function()
8886     {
8887         var indicator = this.el.select('i.roo-required-indicator',true).first();
8888         
8889         if(!indicator){
8890             return false;
8891         }
8892         
8893         return indicator;
8894         
8895     },
8896     
8897     setDisabled : function(v)
8898     {
8899         var i  = this.inputEl().dom;
8900         if (!v) {
8901             i.removeAttribute('disabled');
8902             return;
8903             
8904         }
8905         i.setAttribute('disabled','true');
8906     },
8907     initEvents : function()
8908     {
8909           
8910         this.inputEl().on("keydown" , this.fireKey,  this);
8911         this.inputEl().on("focus", this.onFocus,  this);
8912         this.inputEl().on("blur", this.onBlur,  this);
8913         
8914         this.inputEl().relayEvent('keyup', this);
8915         
8916         this.indicator = this.indicatorEl();
8917         
8918         if(this.indicator){
8919             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8920             this.indicator.hide();
8921         }
8922  
8923         // reference to original value for reset
8924         this.originalValue = this.getValue();
8925         //Roo.form.TextField.superclass.initEvents.call(this);
8926         if(this.validationEvent == 'keyup'){
8927             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8928             this.inputEl().on('keyup', this.filterValidation, this);
8929         }
8930         else if(this.validationEvent !== false){
8931             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8932         }
8933         
8934         if(this.selectOnFocus){
8935             this.on("focus", this.preFocus, this);
8936             
8937         }
8938         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8939             this.inputEl().on("keypress", this.filterKeys, this);
8940         } else {
8941             this.inputEl().relayEvent('keypress', this);
8942         }
8943        /* if(this.grow){
8944             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8945             this.el.on("click", this.autoSize,  this);
8946         }
8947         */
8948         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8949             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8950         }
8951         
8952         if (typeof(this.before) == 'object') {
8953             this.before.render(this.el.select('.roo-input-before',true).first());
8954         }
8955         if (typeof(this.after) == 'object') {
8956             this.after.render(this.el.select('.roo-input-after',true).first());
8957         }
8958         
8959         
8960     },
8961     filterValidation : function(e){
8962         if(!e.isNavKeyPress()){
8963             this.validationTask.delay(this.validationDelay);
8964         }
8965     },
8966      /**
8967      * Validates the field value
8968      * @return {Boolean} True if the value is valid, else false
8969      */
8970     validate : function(){
8971         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8972         if(this.disabled || this.validateValue(this.getRawValue())){
8973             this.markValid();
8974             return true;
8975         }
8976         
8977         this.markInvalid();
8978         return false;
8979     },
8980     
8981     
8982     /**
8983      * Validates a value according to the field's validation rules and marks the field as invalid
8984      * if the validation fails
8985      * @param {Mixed} value The value to validate
8986      * @return {Boolean} True if the value is valid, else false
8987      */
8988     validateValue : function(value){
8989         if(value.length < 1)  { // if it's blank
8990             if(this.allowBlank){
8991                 return true;
8992             }            
8993             return this.inputEl().hasClass('hide') ? true : false;
8994         }
8995         
8996         if(value.length < this.minLength){
8997             return false;
8998         }
8999         if(value.length > this.maxLength){
9000             return false;
9001         }
9002         if(this.vtype){
9003             var vt = Roo.form.VTypes;
9004             if(!vt[this.vtype](value, this)){
9005                 return false;
9006             }
9007         }
9008         if(typeof this.validator == "function"){
9009             var msg = this.validator(value);
9010             if(msg !== true){
9011                 return false;
9012             }
9013             if (typeof(msg) == 'string') {
9014                 this.invalidText = msg;
9015             }
9016         }
9017         
9018         if(this.regex && !this.regex.test(value)){
9019             return false;
9020         }
9021         
9022         return true;
9023     },
9024
9025     
9026     
9027      // private
9028     fireKey : function(e){
9029         //Roo.log('field ' + e.getKey());
9030         if(e.isNavKeyPress()){
9031             this.fireEvent("specialkey", this, e);
9032         }
9033     },
9034     focus : function (selectText){
9035         if(this.rendered){
9036             this.inputEl().focus();
9037             if(selectText === true){
9038                 this.inputEl().dom.select();
9039             }
9040         }
9041         return this;
9042     } ,
9043     
9044     onFocus : function(){
9045         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9046            // this.el.addClass(this.focusClass);
9047         }
9048         if(!this.hasFocus){
9049             this.hasFocus = true;
9050             this.startValue = this.getValue();
9051             this.fireEvent("focus", this);
9052         }
9053     },
9054     
9055     beforeBlur : Roo.emptyFn,
9056
9057     
9058     // private
9059     onBlur : function(){
9060         this.beforeBlur();
9061         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9062             //this.el.removeClass(this.focusClass);
9063         }
9064         this.hasFocus = false;
9065         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9066             this.validate();
9067         }
9068         var v = this.getValue();
9069         if(String(v) !== String(this.startValue)){
9070             this.fireEvent('change', this, v, this.startValue);
9071         }
9072         this.fireEvent("blur", this);
9073     },
9074     
9075     /**
9076      * Resets the current field value to the originally loaded value and clears any validation messages
9077      */
9078     reset : function(){
9079         this.setValue(this.originalValue);
9080         this.validate();
9081     },
9082      /**
9083      * Returns the name of the field
9084      * @return {Mixed} name The name field
9085      */
9086     getName: function(){
9087         return this.name;
9088     },
9089      /**
9090      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9091      * @return {Mixed} value The field value
9092      */
9093     getValue : function(){
9094         
9095         var v = this.inputEl().getValue();
9096         
9097         return v;
9098     },
9099     /**
9100      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getRawValue : function(){
9104         var v = this.inputEl().getValue();
9105         
9106         return v;
9107     },
9108     
9109     /**
9110      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9111      * @param {Mixed} value The value to set
9112      */
9113     setRawValue : function(v){
9114         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9115     },
9116     
9117     selectText : function(start, end){
9118         var v = this.getRawValue();
9119         if(v.length > 0){
9120             start = start === undefined ? 0 : start;
9121             end = end === undefined ? v.length : end;
9122             var d = this.inputEl().dom;
9123             if(d.setSelectionRange){
9124                 d.setSelectionRange(start, end);
9125             }else if(d.createTextRange){
9126                 var range = d.createTextRange();
9127                 range.moveStart("character", start);
9128                 range.moveEnd("character", v.length-end);
9129                 range.select();
9130             }
9131         }
9132     },
9133     
9134     /**
9135      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9136      * @param {Mixed} value The value to set
9137      */
9138     setValue : function(v){
9139         this.value = v;
9140         if(this.rendered){
9141             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9142             this.validate();
9143         }
9144     },
9145     
9146     /*
9147     processValue : function(value){
9148         if(this.stripCharsRe){
9149             var newValue = value.replace(this.stripCharsRe, '');
9150             if(newValue !== value){
9151                 this.setRawValue(newValue);
9152                 return newValue;
9153             }
9154         }
9155         return value;
9156     },
9157   */
9158     preFocus : function(){
9159         
9160         if(this.selectOnFocus){
9161             this.inputEl().dom.select();
9162         }
9163     },
9164     filterKeys : function(e){
9165         var k = e.getKey();
9166         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9167             return;
9168         }
9169         var c = e.getCharCode(), cc = String.fromCharCode(c);
9170         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9171             return;
9172         }
9173         if(!this.maskRe.test(cc)){
9174             e.stopEvent();
9175         }
9176     },
9177      /**
9178      * Clear any invalid styles/messages for this field
9179      */
9180     clearInvalid : function(){
9181         
9182         if(!this.el || this.preventMark){ // not rendered
9183             return;
9184         }
9185         
9186      
9187         this.el.removeClass(this.invalidClass);
9188         
9189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9190             
9191             var feedback = this.el.select('.form-control-feedback', true).first();
9192             
9193             if(feedback){
9194                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9195             }
9196             
9197         }
9198         
9199         this.fireEvent('valid', this);
9200     },
9201     
9202      /**
9203      * Mark this field as valid
9204      */
9205     markValid : function()
9206     {
9207         if(!this.el  || this.preventMark){ // not rendered...
9208             return;
9209         }
9210         
9211         this.el.removeClass([this.invalidClass, this.validClass]);
9212         
9213         var feedback = this.el.select('.form-control-feedback', true).first();
9214             
9215         if(feedback){
9216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9217         }
9218
9219         if(this.disabled){
9220             return;
9221         }
9222         
9223         if(this.allowBlank && !this.getRawValue().length){
9224             return;
9225         }
9226         
9227         if(this.indicator){
9228             this.indicator.hide();
9229         }
9230         
9231         this.el.addClass(this.validClass);
9232         
9233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9234             
9235             var feedback = this.el.select('.form-control-feedback', true).first();
9236             
9237             if(feedback){
9238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9239                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9240             }
9241             
9242         }
9243         
9244         this.fireEvent('valid', this);
9245     },
9246     
9247      /**
9248      * Mark this field as invalid
9249      * @param {String} msg The validation message
9250      */
9251     markInvalid : function(msg)
9252     {
9253         if(!this.el  || this.preventMark){ // not rendered
9254             return;
9255         }
9256         
9257         this.el.removeClass([this.invalidClass, this.validClass]);
9258         
9259         var feedback = this.el.select('.form-control-feedback', true).first();
9260             
9261         if(feedback){
9262             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9263         }
9264
9265         if(this.disabled){
9266             return;
9267         }
9268         
9269         if(this.allowBlank && !this.getRawValue().length){
9270             return;
9271         }
9272         
9273         if(this.indicator){
9274             this.indicator.show();
9275         }
9276         
9277         this.el.addClass(this.invalidClass);
9278         
9279         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9280             
9281             var feedback = this.el.select('.form-control-feedback', true).first();
9282             
9283             if(feedback){
9284                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9285                 
9286                 if(this.getValue().length || this.forceFeedback){
9287                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9288                 }
9289                 
9290             }
9291             
9292         }
9293         
9294         this.fireEvent('invalid', this, msg);
9295     },
9296     // private
9297     SafariOnKeyDown : function(event)
9298     {
9299         // this is a workaround for a password hang bug on chrome/ webkit.
9300         if (this.inputEl().dom.type != 'password') {
9301             return;
9302         }
9303         
9304         var isSelectAll = false;
9305         
9306         if(this.inputEl().dom.selectionEnd > 0){
9307             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9308         }
9309         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9310             event.preventDefault();
9311             this.setValue('');
9312             return;
9313         }
9314         
9315         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9316             
9317             event.preventDefault();
9318             // this is very hacky as keydown always get's upper case.
9319             //
9320             var cc = String.fromCharCode(event.getCharCode());
9321             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9322             
9323         }
9324     },
9325     adjustWidth : function(tag, w){
9326         tag = tag.toLowerCase();
9327         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9328             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9329                 if(tag == 'input'){
9330                     return w + 2;
9331                 }
9332                 if(tag == 'textarea'){
9333                     return w-2;
9334                 }
9335             }else if(Roo.isOpera){
9336                 if(tag == 'input'){
9337                     return w + 2;
9338                 }
9339                 if(tag == 'textarea'){
9340                     return w-2;
9341                 }
9342             }
9343         }
9344         return w;
9345     },
9346     
9347     setFieldLabel : function(v)
9348     {
9349         this.fieldLabel = v;
9350         
9351         if(this.rendered){
9352             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9353         }
9354     }
9355 });
9356
9357  
9358 /*
9359  * - LGPL
9360  *
9361  * Input
9362  * 
9363  */
9364
9365 /**
9366  * @class Roo.bootstrap.TextArea
9367  * @extends Roo.bootstrap.Input
9368  * Bootstrap TextArea class
9369  * @cfg {Number} cols Specifies the visible width of a text area
9370  * @cfg {Number} rows Specifies the visible number of lines in a text area
9371  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9372  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9373  * @cfg {string} html text
9374  * 
9375  * @constructor
9376  * Create a new TextArea
9377  * @param {Object} config The config object
9378  */
9379
9380 Roo.bootstrap.TextArea = function(config){
9381     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9382    
9383 };
9384
9385 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9386      
9387     cols : false,
9388     rows : 5,
9389     readOnly : false,
9390     warp : 'soft',
9391     resize : false,
9392     value: false,
9393     html: false,
9394     
9395     getAutoCreate : function(){
9396         
9397         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9398         
9399         var id = Roo.id();
9400         
9401         var cfg = {};
9402         
9403         if(this.inputType != 'hidden'){
9404             cfg.cls = 'form-group' //input-group
9405         }
9406         
9407         var input =  {
9408             tag: 'textarea',
9409             id : id,
9410             warp : this.warp,
9411             rows : this.rows,
9412             value : this.value || '',
9413             html: this.html || '',
9414             cls : 'form-control',
9415             placeholder : this.placeholder || '' 
9416             
9417         };
9418         
9419         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9420             input.maxLength = this.maxLength;
9421         }
9422         
9423         if(this.resize){
9424             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9425         }
9426         
9427         if(this.cols){
9428             input.cols = this.cols;
9429         }
9430         
9431         if (this.readOnly) {
9432             input.readonly = true;
9433         }
9434         
9435         if (this.name) {
9436             input.name = this.name;
9437         }
9438         
9439         if (this.size) {
9440             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9441         }
9442         
9443         var settings=this;
9444         ['xs','sm','md','lg'].map(function(size){
9445             if (settings[size]) {
9446                 cfg.cls += ' col-' + size + '-' + settings[size];
9447             }
9448         });
9449         
9450         var inputblock = input;
9451         
9452         if(this.hasFeedback && !this.allowBlank){
9453             
9454             var feedback = {
9455                 tag: 'span',
9456                 cls: 'glyphicon form-control-feedback'
9457             };
9458
9459             inputblock = {
9460                 cls : 'has-feedback',
9461                 cn :  [
9462                     input,
9463                     feedback
9464                 ] 
9465             };  
9466         }
9467         
9468         
9469         if (this.before || this.after) {
9470             
9471             inputblock = {
9472                 cls : 'input-group',
9473                 cn :  [] 
9474             };
9475             if (this.before) {
9476                 inputblock.cn.push({
9477                     tag :'span',
9478                     cls : 'input-group-addon',
9479                     html : this.before
9480                 });
9481             }
9482             
9483             inputblock.cn.push(input);
9484             
9485             if(this.hasFeedback && !this.allowBlank){
9486                 inputblock.cls += ' has-feedback';
9487                 inputblock.cn.push(feedback);
9488             }
9489             
9490             if (this.after) {
9491                 inputblock.cn.push({
9492                     tag :'span',
9493                     cls : 'input-group-addon',
9494                     html : this.after
9495                 });
9496             }
9497             
9498         }
9499         
9500         if (align ==='left' && this.fieldLabel.length) {
9501             cfg.cn = [
9502                 {
9503                     tag: 'label',
9504                     'for' :  id,
9505                     cls : 'control-label',
9506                     html : this.fieldLabel
9507                 },
9508                 {
9509                     cls : "",
9510                     cn: [
9511                         inputblock
9512                     ]
9513                 }
9514
9515             ];
9516             
9517             if(this.labelWidth > 12){
9518                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9519             }
9520
9521             if(this.labelWidth < 13 && this.labelmd == 0){
9522                 this.labelmd = this.labelWidth;
9523             }
9524
9525             if(this.labellg > 0){
9526                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9527                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9528             }
9529
9530             if(this.labelmd > 0){
9531                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9532                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9533             }
9534
9535             if(this.labelsm > 0){
9536                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9537                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9538             }
9539
9540             if(this.labelxs > 0){
9541                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9542                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9543             }
9544             
9545         } else if ( this.fieldLabel.length) {
9546             cfg.cn = [
9547
9548                {
9549                    tag: 'label',
9550                    //cls : 'input-group-addon',
9551                    html : this.fieldLabel
9552
9553                },
9554
9555                inputblock
9556
9557            ];
9558
9559         } else {
9560
9561             cfg.cn = [
9562
9563                 inputblock
9564
9565             ];
9566                 
9567         }
9568         
9569         if (this.disabled) {
9570             input.disabled=true;
9571         }
9572         
9573         return cfg;
9574         
9575     },
9576     /**
9577      * return the real textarea element.
9578      */
9579     inputEl: function ()
9580     {
9581         return this.el.select('textarea.form-control',true).first();
9582     },
9583     
9584     /**
9585      * Clear any invalid styles/messages for this field
9586      */
9587     clearInvalid : function()
9588     {
9589         
9590         if(!this.el || this.preventMark){ // not rendered
9591             return;
9592         }
9593         
9594         var label = this.el.select('label', true).first();
9595         var icon = this.el.select('i.fa-star', true).first();
9596         
9597         if(label && icon){
9598             icon.remove();
9599         }
9600         
9601         this.el.removeClass(this.invalidClass);
9602         
9603         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9604             
9605             var feedback = this.el.select('.form-control-feedback', true).first();
9606             
9607             if(feedback){
9608                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9609             }
9610             
9611         }
9612         
9613         this.fireEvent('valid', this);
9614     },
9615     
9616      /**
9617      * Mark this field as valid
9618      */
9619     markValid : function()
9620     {
9621         if(!this.el  || this.preventMark){ // not rendered
9622             return;
9623         }
9624         
9625         this.el.removeClass([this.invalidClass, this.validClass]);
9626         
9627         var feedback = this.el.select('.form-control-feedback', true).first();
9628             
9629         if(feedback){
9630             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9631         }
9632
9633         if(this.disabled || this.allowBlank){
9634             return;
9635         }
9636         
9637         var label = this.el.select('label', true).first();
9638         var icon = this.el.select('i.fa-star', true).first();
9639         
9640         if(label && icon){
9641             icon.remove();
9642         }
9643         
9644         this.el.addClass(this.validClass);
9645         
9646         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9647             
9648             var feedback = this.el.select('.form-control-feedback', true).first();
9649             
9650             if(feedback){
9651                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9652                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9653             }
9654             
9655         }
9656         
9657         this.fireEvent('valid', this);
9658     },
9659     
9660      /**
9661      * Mark this field as invalid
9662      * @param {String} msg The validation message
9663      */
9664     markInvalid : function(msg)
9665     {
9666         if(!this.el  || this.preventMark){ // not rendered
9667             return;
9668         }
9669         
9670         this.el.removeClass([this.invalidClass, this.validClass]);
9671         
9672         var feedback = this.el.select('.form-control-feedback', true).first();
9673             
9674         if(feedback){
9675             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9676         }
9677
9678         if(this.disabled || this.allowBlank){
9679             return;
9680         }
9681         
9682         var label = this.el.select('label', true).first();
9683         var icon = this.el.select('i.fa-star', true).first();
9684         
9685         if(!this.getValue().length && label && !icon){
9686             this.el.createChild({
9687                 tag : 'i',
9688                 cls : 'text-danger fa fa-lg fa-star',
9689                 tooltip : 'This field is required',
9690                 style : 'margin-right:5px;'
9691             }, label, true);
9692         }
9693
9694         this.el.addClass(this.invalidClass);
9695         
9696         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9697             
9698             var feedback = this.el.select('.form-control-feedback', true).first();
9699             
9700             if(feedback){
9701                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9702                 
9703                 if(this.getValue().length || this.forceFeedback){
9704                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9705                 }
9706                 
9707             }
9708             
9709         }
9710         
9711         this.fireEvent('invalid', this, msg);
9712     }
9713 });
9714
9715  
9716 /*
9717  * - LGPL
9718  *
9719  * trigger field - base class for combo..
9720  * 
9721  */
9722  
9723 /**
9724  * @class Roo.bootstrap.TriggerField
9725  * @extends Roo.bootstrap.Input
9726  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9727  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9728  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9729  * for which you can provide a custom implementation.  For example:
9730  * <pre><code>
9731 var trigger = new Roo.bootstrap.TriggerField();
9732 trigger.onTriggerClick = myTriggerFn;
9733 trigger.applyTo('my-field');
9734 </code></pre>
9735  *
9736  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9737  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9738  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9739  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9740  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9741
9742  * @constructor
9743  * Create a new TriggerField.
9744  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9745  * to the base TextField)
9746  */
9747 Roo.bootstrap.TriggerField = function(config){
9748     this.mimicing = false;
9749     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9750 };
9751
9752 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9753     /**
9754      * @cfg {String} triggerClass A CSS class to apply to the trigger
9755      */
9756      /**
9757      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9758      */
9759     hideTrigger:false,
9760
9761     /**
9762      * @cfg {Boolean} removable (true|false) special filter default false
9763      */
9764     removable : false,
9765     
9766     /** @cfg {Boolean} grow @hide */
9767     /** @cfg {Number} growMin @hide */
9768     /** @cfg {Number} growMax @hide */
9769
9770     /**
9771      * @hide 
9772      * @method
9773      */
9774     autoSize: Roo.emptyFn,
9775     // private
9776     monitorTab : true,
9777     // private
9778     deferHeight : true,
9779
9780     
9781     actionMode : 'wrap',
9782     
9783     caret : false,
9784     
9785     
9786     getAutoCreate : function(){
9787        
9788         var align = this.labelAlign || this.parentLabelAlign();
9789         
9790         var id = Roo.id();
9791         
9792         var cfg = {
9793             cls: 'form-group' //input-group
9794         };
9795         
9796         
9797         var input =  {
9798             tag: 'input',
9799             id : id,
9800             type : this.inputType,
9801             cls : 'form-control',
9802             autocomplete: 'new-password',
9803             placeholder : this.placeholder || '' 
9804         };
9805         if (this.name) {
9806             input.name = this.name;
9807         }
9808         if (this.size) {
9809             input.cls += ' input-' + this.size;
9810         }
9811         
9812         if (this.disabled) {
9813             input.disabled=true;
9814         }
9815         
9816         var inputblock = input;
9817         
9818         if(this.hasFeedback && !this.allowBlank){
9819             
9820             var feedback = {
9821                 tag: 'span',
9822                 cls: 'glyphicon form-control-feedback'
9823             };
9824             
9825             if(this.removable && !this.editable && !this.tickable){
9826                 inputblock = {
9827                     cls : 'has-feedback',
9828                     cn :  [
9829                         inputblock,
9830                         {
9831                             tag: 'button',
9832                             html : 'x',
9833                             cls : 'roo-combo-removable-btn close'
9834                         },
9835                         feedback
9836                     ] 
9837                 };
9838             } else {
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         feedback
9844                     ] 
9845                 };
9846             }
9847
9848         } else {
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'roo-removable',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         }
9859                     ] 
9860                 };
9861             }
9862         }
9863         
9864         if (this.before || this.after) {
9865             
9866             inputblock = {
9867                 cls : 'input-group',
9868                 cn :  [] 
9869             };
9870             if (this.before) {
9871                 inputblock.cn.push({
9872                     tag :'span',
9873                     cls : 'input-group-addon',
9874                     html : this.before
9875                 });
9876             }
9877             
9878             inputblock.cn.push(input);
9879             
9880             if(this.hasFeedback && !this.allowBlank){
9881                 inputblock.cls += ' has-feedback';
9882                 inputblock.cn.push(feedback);
9883             }
9884             
9885             if (this.after) {
9886                 inputblock.cn.push({
9887                     tag :'span',
9888                     cls : 'input-group-addon',
9889                     html : this.after
9890                 });
9891             }
9892             
9893         };
9894         
9895         var box = {
9896             tag: 'div',
9897             cn: [
9898                 {
9899                     tag: 'input',
9900                     type : 'hidden',
9901                     cls: 'form-hidden-field'
9902                 },
9903                 inputblock
9904             ]
9905             
9906         };
9907         
9908         if(this.multiple){
9909             box = {
9910                 tag: 'div',
9911                 cn: [
9912                     {
9913                         tag: 'input',
9914                         type : 'hidden',
9915                         cls: 'form-hidden-field'
9916                     },
9917                     {
9918                         tag: 'ul',
9919                         cls: 'roo-select2-choices',
9920                         cn:[
9921                             {
9922                                 tag: 'li',
9923                                 cls: 'roo-select2-search-field',
9924                                 cn: [
9925
9926                                     inputblock
9927                                 ]
9928                             }
9929                         ]
9930                     }
9931                 ]
9932             }
9933         };
9934         
9935         var combobox = {
9936             cls: 'roo-select2-container input-group',
9937             cn: [
9938                 box
9939 //                {
9940 //                    tag: 'ul',
9941 //                    cls: 'typeahead typeahead-long dropdown-menu',
9942 //                    style: 'display:none'
9943 //                }
9944             ]
9945         };
9946         
9947         if(!this.multiple && this.showToggleBtn){
9948             
9949             var caret = {
9950                         tag: 'span',
9951                         cls: 'caret'
9952              };
9953             if (this.caret != false) {
9954                 caret = {
9955                      tag: 'i',
9956                      cls: 'fa fa-' + this.caret
9957                 };
9958                 
9959             }
9960             
9961             combobox.cn.push({
9962                 tag :'span',
9963                 cls : 'input-group-addon btn dropdown-toggle',
9964                 cn : [
9965                     caret,
9966                     {
9967                         tag: 'span',
9968                         cls: 'combobox-clear',
9969                         cn  : [
9970                             {
9971                                 tag : 'i',
9972                                 cls: 'icon-remove'
9973                             }
9974                         ]
9975                     }
9976                 ]
9977
9978             })
9979         }
9980         
9981         if(this.multiple){
9982             combobox.cls += ' roo-select2-container-multi';
9983         }
9984         
9985         if (align ==='left' && this.fieldLabel.length) {
9986             
9987             cfg.cls += ' roo-form-group-label-left';
9988
9989             cfg.cn = [
9990                 {
9991                     tag : 'i',
9992                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9993                     tooltip : 'This field is required'
9994                 },
9995                 {
9996                     tag: 'label',
9997                     'for' :  id,
9998                     cls : 'control-label',
9999                     html : this.fieldLabel
10000
10001                 },
10002                 {
10003                     cls : "", 
10004                     cn: [
10005                         combobox
10006                     ]
10007                 }
10008
10009             ];
10010             
10011             var labelCfg = cfg.cn[1];
10012             var contentCfg = cfg.cn[2];
10013             
10014             if(this.indicatorpos == 'right'){
10015                 cfg.cn = [
10016                     {
10017                         tag: 'label',
10018                         'for' :  id,
10019                         cls : 'control-label',
10020                         cn : [
10021                             {
10022                                 tag : 'span',
10023                                 html : this.fieldLabel
10024                             },
10025                             {
10026                                 tag : 'i',
10027                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10028                                 tooltip : 'This field is required'
10029                             }
10030                         ]
10031                     },
10032                     {
10033                         cls : "", 
10034                         cn: [
10035                             combobox
10036                         ]
10037                     }
10038
10039                 ];
10040                 
10041                 labelCfg = cfg.cn[0];
10042                 contentCfg = cfg.cn[1];
10043             }
10044             
10045             if(this.labelWidth > 12){
10046                 labelCfg.style = "width: " + this.labelWidth + 'px';
10047             }
10048             
10049             if(this.labelWidth < 13 && this.labelmd == 0){
10050                 this.labelmd = this.labelWidth;
10051             }
10052             
10053             if(this.labellg > 0){
10054                 labelCfg.cls += ' col-lg-' + this.labellg;
10055                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10056             }
10057             
10058             if(this.labelmd > 0){
10059                 labelCfg.cls += ' col-md-' + this.labelmd;
10060                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10061             }
10062             
10063             if(this.labelsm > 0){
10064                 labelCfg.cls += ' col-sm-' + this.labelsm;
10065                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10066             }
10067             
10068             if(this.labelxs > 0){
10069                 labelCfg.cls += ' col-xs-' + this.labelxs;
10070                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10071             }
10072             
10073         } else if ( this.fieldLabel.length) {
10074 //                Roo.log(" label");
10075             cfg.cn = [
10076                 {
10077                    tag : 'i',
10078                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10079                    tooltip : 'This field is required'
10080                },
10081                {
10082                    tag: 'label',
10083                    //cls : 'input-group-addon',
10084                    html : this.fieldLabel
10085
10086                },
10087
10088                combobox
10089
10090             ];
10091             
10092             if(this.indicatorpos == 'right'){
10093                 
10094                 cfg.cn = [
10095                     {
10096                        tag: 'label',
10097                        cn : [
10098                            {
10099                                tag : 'span',
10100                                html : this.fieldLabel
10101                            },
10102                            {
10103                               tag : 'i',
10104                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10105                               tooltip : 'This field is required'
10106                            }
10107                        ]
10108
10109                     },
10110                     combobox
10111
10112                 ];
10113
10114             }
10115
10116         } else {
10117             
10118 //                Roo.log(" no label && no align");
10119                 cfg = combobox
10120                      
10121                 
10122         }
10123         
10124         var settings=this;
10125         ['xs','sm','md','lg'].map(function(size){
10126             if (settings[size]) {
10127                 cfg.cls += ' col-' + size + '-' + settings[size];
10128             }
10129         });
10130         
10131         return cfg;
10132         
10133     },
10134     
10135     
10136     
10137     // private
10138     onResize : function(w, h){
10139 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10140 //        if(typeof w == 'number'){
10141 //            var x = w - this.trigger.getWidth();
10142 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10143 //            this.trigger.setStyle('left', x+'px');
10144 //        }
10145     },
10146
10147     // private
10148     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10149
10150     // private
10151     getResizeEl : function(){
10152         return this.inputEl();
10153     },
10154
10155     // private
10156     getPositionEl : function(){
10157         return this.inputEl();
10158     },
10159
10160     // private
10161     alignErrorIcon : function(){
10162         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10163     },
10164
10165     // private
10166     initEvents : function(){
10167         
10168         this.createList();
10169         
10170         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10171         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10172         if(!this.multiple && this.showToggleBtn){
10173             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10174             if(this.hideTrigger){
10175                 this.trigger.setDisplayed(false);
10176             }
10177             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10178         }
10179         
10180         if(this.multiple){
10181             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10182         }
10183         
10184         if(this.removable && !this.editable && !this.tickable){
10185             var close = this.closeTriggerEl();
10186             
10187             if(close){
10188                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10189                 close.on('click', this.removeBtnClick, this, close);
10190             }
10191         }
10192         
10193         //this.trigger.addClassOnOver('x-form-trigger-over');
10194         //this.trigger.addClassOnClick('x-form-trigger-click');
10195         
10196         //if(!this.width){
10197         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10198         //}
10199     },
10200     
10201     closeTriggerEl : function()
10202     {
10203         var close = this.el.select('.roo-combo-removable-btn', true).first();
10204         return close ? close : false;
10205     },
10206     
10207     removeBtnClick : function(e, h, el)
10208     {
10209         e.preventDefault();
10210         
10211         if(this.fireEvent("remove", this) !== false){
10212             this.reset();
10213             this.fireEvent("afterremove", this)
10214         }
10215     },
10216     
10217     createList : function()
10218     {
10219         this.list = Roo.get(document.body).createChild({
10220             tag: 'ul',
10221             cls: 'typeahead typeahead-long dropdown-menu',
10222             style: 'display:none'
10223         });
10224         
10225         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10226         
10227     },
10228
10229     // private
10230     initTrigger : function(){
10231        
10232     },
10233
10234     // private
10235     onDestroy : function(){
10236         if(this.trigger){
10237             this.trigger.removeAllListeners();
10238           //  this.trigger.remove();
10239         }
10240         //if(this.wrap){
10241         //    this.wrap.remove();
10242         //}
10243         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10244     },
10245
10246     // private
10247     onFocus : function(){
10248         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10249         /*
10250         if(!this.mimicing){
10251             this.wrap.addClass('x-trigger-wrap-focus');
10252             this.mimicing = true;
10253             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10254             if(this.monitorTab){
10255                 this.el.on("keydown", this.checkTab, this);
10256             }
10257         }
10258         */
10259     },
10260
10261     // private
10262     checkTab : function(e){
10263         if(e.getKey() == e.TAB){
10264             this.triggerBlur();
10265         }
10266     },
10267
10268     // private
10269     onBlur : function(){
10270         // do nothing
10271     },
10272
10273     // private
10274     mimicBlur : function(e, t){
10275         /*
10276         if(!this.wrap.contains(t) && this.validateBlur()){
10277             this.triggerBlur();
10278         }
10279         */
10280     },
10281
10282     // private
10283     triggerBlur : function(){
10284         this.mimicing = false;
10285         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10286         if(this.monitorTab){
10287             this.el.un("keydown", this.checkTab, this);
10288         }
10289         //this.wrap.removeClass('x-trigger-wrap-focus');
10290         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10291     },
10292
10293     // private
10294     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10295     validateBlur : function(e, t){
10296         return true;
10297     },
10298
10299     // private
10300     onDisable : function(){
10301         this.inputEl().dom.disabled = true;
10302         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10303         //if(this.wrap){
10304         //    this.wrap.addClass('x-item-disabled');
10305         //}
10306     },
10307
10308     // private
10309     onEnable : function(){
10310         this.inputEl().dom.disabled = false;
10311         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10312         //if(this.wrap){
10313         //    this.el.removeClass('x-item-disabled');
10314         //}
10315     },
10316
10317     // private
10318     onShow : function(){
10319         var ae = this.getActionEl();
10320         
10321         if(ae){
10322             ae.dom.style.display = '';
10323             ae.dom.style.visibility = 'visible';
10324         }
10325     },
10326
10327     // private
10328     
10329     onHide : function(){
10330         var ae = this.getActionEl();
10331         ae.dom.style.display = 'none';
10332     },
10333
10334     /**
10335      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10336      * by an implementing function.
10337      * @method
10338      * @param {EventObject} e
10339      */
10340     onTriggerClick : Roo.emptyFn
10341 });
10342  /*
10343  * Based on:
10344  * Ext JS Library 1.1.1
10345  * Copyright(c) 2006-2007, Ext JS, LLC.
10346  *
10347  * Originally Released Under LGPL - original licence link has changed is not relivant.
10348  *
10349  * Fork - LGPL
10350  * <script type="text/javascript">
10351  */
10352
10353
10354 /**
10355  * @class Roo.data.SortTypes
10356  * @singleton
10357  * Defines the default sorting (casting?) comparison functions used when sorting data.
10358  */
10359 Roo.data.SortTypes = {
10360     /**
10361      * Default sort that does nothing
10362      * @param {Mixed} s The value being converted
10363      * @return {Mixed} The comparison value
10364      */
10365     none : function(s){
10366         return s;
10367     },
10368     
10369     /**
10370      * The regular expression used to strip tags
10371      * @type {RegExp}
10372      * @property
10373      */
10374     stripTagsRE : /<\/?[^>]+>/gi,
10375     
10376     /**
10377      * Strips all HTML tags to sort on text only
10378      * @param {Mixed} s The value being converted
10379      * @return {String} The comparison value
10380      */
10381     asText : function(s){
10382         return String(s).replace(this.stripTagsRE, "");
10383     },
10384     
10385     /**
10386      * Strips all HTML tags to sort on text only - Case insensitive
10387      * @param {Mixed} s The value being converted
10388      * @return {String} The comparison value
10389      */
10390     asUCText : function(s){
10391         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10392     },
10393     
10394     /**
10395      * Case insensitive string
10396      * @param {Mixed} s The value being converted
10397      * @return {String} The comparison value
10398      */
10399     asUCString : function(s) {
10400         return String(s).toUpperCase();
10401     },
10402     
10403     /**
10404      * Date sorting
10405      * @param {Mixed} s The value being converted
10406      * @return {Number} The comparison value
10407      */
10408     asDate : function(s) {
10409         if(!s){
10410             return 0;
10411         }
10412         if(s instanceof Date){
10413             return s.getTime();
10414         }
10415         return Date.parse(String(s));
10416     },
10417     
10418     /**
10419      * Float sorting
10420      * @param {Mixed} s The value being converted
10421      * @return {Float} The comparison value
10422      */
10423     asFloat : function(s) {
10424         var val = parseFloat(String(s).replace(/,/g, ""));
10425         if(isNaN(val)) {
10426             val = 0;
10427         }
10428         return val;
10429     },
10430     
10431     /**
10432      * Integer sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Number} The comparison value
10435      */
10436     asInt : function(s) {
10437         var val = parseInt(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     }
10443 };/*
10444  * Based on:
10445  * Ext JS Library 1.1.1
10446  * Copyright(c) 2006-2007, Ext JS, LLC.
10447  *
10448  * Originally Released Under LGPL - original licence link has changed is not relivant.
10449  *
10450  * Fork - LGPL
10451  * <script type="text/javascript">
10452  */
10453
10454 /**
10455 * @class Roo.data.Record
10456  * Instances of this class encapsulate both record <em>definition</em> information, and record
10457  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10458  * to access Records cached in an {@link Roo.data.Store} object.<br>
10459  * <p>
10460  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10461  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10462  * objects.<br>
10463  * <p>
10464  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10465  * @constructor
10466  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10467  * {@link #create}. The parameters are the same.
10468  * @param {Array} data An associative Array of data values keyed by the field name.
10469  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10470  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10471  * not specified an integer id is generated.
10472  */
10473 Roo.data.Record = function(data, id){
10474     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10475     this.data = data;
10476 };
10477
10478 /**
10479  * Generate a constructor for a specific record layout.
10480  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10481  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10482  * Each field definition object may contain the following properties: <ul>
10483  * <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,
10484  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10485  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10486  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10487  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10488  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10489  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10490  * this may be omitted.</p></li>
10491  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10492  * <ul><li>auto (Default, implies no conversion)</li>
10493  * <li>string</li>
10494  * <li>int</li>
10495  * <li>float</li>
10496  * <li>boolean</li>
10497  * <li>date</li></ul></p></li>
10498  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10499  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10500  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10501  * by the Reader into an object that will be stored in the Record. It is passed the
10502  * following parameters:<ul>
10503  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10504  * </ul></p></li>
10505  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10506  * </ul>
10507  * <br>usage:<br><pre><code>
10508 var TopicRecord = Roo.data.Record.create(
10509     {name: 'title', mapping: 'topic_title'},
10510     {name: 'author', mapping: 'username'},
10511     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10512     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10513     {name: 'lastPoster', mapping: 'user2'},
10514     {name: 'excerpt', mapping: 'post_text'}
10515 );
10516
10517 var myNewRecord = new TopicRecord({
10518     title: 'Do my job please',
10519     author: 'noobie',
10520     totalPosts: 1,
10521     lastPost: new Date(),
10522     lastPoster: 'Animal',
10523     excerpt: 'No way dude!'
10524 });
10525 myStore.add(myNewRecord);
10526 </code></pre>
10527  * @method create
10528  * @static
10529  */
10530 Roo.data.Record.create = function(o){
10531     var f = function(){
10532         f.superclass.constructor.apply(this, arguments);
10533     };
10534     Roo.extend(f, Roo.data.Record);
10535     var p = f.prototype;
10536     p.fields = new Roo.util.MixedCollection(false, function(field){
10537         return field.name;
10538     });
10539     for(var i = 0, len = o.length; i < len; i++){
10540         p.fields.add(new Roo.data.Field(o[i]));
10541     }
10542     f.getField = function(name){
10543         return p.fields.get(name);  
10544     };
10545     return f;
10546 };
10547
10548 Roo.data.Record.AUTO_ID = 1000;
10549 Roo.data.Record.EDIT = 'edit';
10550 Roo.data.Record.REJECT = 'reject';
10551 Roo.data.Record.COMMIT = 'commit';
10552
10553 Roo.data.Record.prototype = {
10554     /**
10555      * Readonly flag - true if this record has been modified.
10556      * @type Boolean
10557      */
10558     dirty : false,
10559     editing : false,
10560     error: null,
10561     modified: null,
10562
10563     // private
10564     join : function(store){
10565         this.store = store;
10566     },
10567
10568     /**
10569      * Set the named field to the specified value.
10570      * @param {String} name The name of the field to set.
10571      * @param {Object} value The value to set the field to.
10572      */
10573     set : function(name, value){
10574         if(this.data[name] == value){
10575             return;
10576         }
10577         this.dirty = true;
10578         if(!this.modified){
10579             this.modified = {};
10580         }
10581         if(typeof this.modified[name] == 'undefined'){
10582             this.modified[name] = this.data[name];
10583         }
10584         this.data[name] = value;
10585         if(!this.editing && this.store){
10586             this.store.afterEdit(this);
10587         }       
10588     },
10589
10590     /**
10591      * Get the value of the named field.
10592      * @param {String} name The name of the field to get the value of.
10593      * @return {Object} The value of the field.
10594      */
10595     get : function(name){
10596         return this.data[name]; 
10597     },
10598
10599     // private
10600     beginEdit : function(){
10601         this.editing = true;
10602         this.modified = {}; 
10603     },
10604
10605     // private
10606     cancelEdit : function(){
10607         this.editing = false;
10608         delete this.modified;
10609     },
10610
10611     // private
10612     endEdit : function(){
10613         this.editing = false;
10614         if(this.dirty && this.store){
10615             this.store.afterEdit(this);
10616         }
10617     },
10618
10619     /**
10620      * Usually called by the {@link Roo.data.Store} which owns the Record.
10621      * Rejects all changes made to the Record since either creation, or the last commit operation.
10622      * Modified fields are reverted to their original values.
10623      * <p>
10624      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10625      * of reject operations.
10626      */
10627     reject : function(){
10628         var m = this.modified;
10629         for(var n in m){
10630             if(typeof m[n] != "function"){
10631                 this.data[n] = m[n];
10632             }
10633         }
10634         this.dirty = false;
10635         delete this.modified;
10636         this.editing = false;
10637         if(this.store){
10638             this.store.afterReject(this);
10639         }
10640     },
10641
10642     /**
10643      * Usually called by the {@link Roo.data.Store} which owns the Record.
10644      * Commits all changes made to the Record since either creation, or the last commit operation.
10645      * <p>
10646      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10647      * of commit operations.
10648      */
10649     commit : function(){
10650         this.dirty = false;
10651         delete this.modified;
10652         this.editing = false;
10653         if(this.store){
10654             this.store.afterCommit(this);
10655         }
10656     },
10657
10658     // private
10659     hasError : function(){
10660         return this.error != null;
10661     },
10662
10663     // private
10664     clearError : function(){
10665         this.error = null;
10666     },
10667
10668     /**
10669      * Creates a copy of this record.
10670      * @param {String} id (optional) A new record id if you don't want to use this record's id
10671      * @return {Record}
10672      */
10673     copy : function(newId) {
10674         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10675     }
10676 };/*
10677  * Based on:
10678  * Ext JS Library 1.1.1
10679  * Copyright(c) 2006-2007, Ext JS, LLC.
10680  *
10681  * Originally Released Under LGPL - original licence link has changed is not relivant.
10682  *
10683  * Fork - LGPL
10684  * <script type="text/javascript">
10685  */
10686
10687
10688
10689 /**
10690  * @class Roo.data.Store
10691  * @extends Roo.util.Observable
10692  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10693  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10694  * <p>
10695  * 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
10696  * has no knowledge of the format of the data returned by the Proxy.<br>
10697  * <p>
10698  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10699  * instances from the data object. These records are cached and made available through accessor functions.
10700  * @constructor
10701  * Creates a new Store.
10702  * @param {Object} config A config object containing the objects needed for the Store to access data,
10703  * and read the data into Records.
10704  */
10705 Roo.data.Store = function(config){
10706     this.data = new Roo.util.MixedCollection(false);
10707     this.data.getKey = function(o){
10708         return o.id;
10709     };
10710     this.baseParams = {};
10711     // private
10712     this.paramNames = {
10713         "start" : "start",
10714         "limit" : "limit",
10715         "sort" : "sort",
10716         "dir" : "dir",
10717         "multisort" : "_multisort"
10718     };
10719
10720     if(config && config.data){
10721         this.inlineData = config.data;
10722         delete config.data;
10723     }
10724
10725     Roo.apply(this, config);
10726     
10727     if(this.reader){ // reader passed
10728         this.reader = Roo.factory(this.reader, Roo.data);
10729         this.reader.xmodule = this.xmodule || false;
10730         if(!this.recordType){
10731             this.recordType = this.reader.recordType;
10732         }
10733         if(this.reader.onMetaChange){
10734             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10735         }
10736     }
10737
10738     if(this.recordType){
10739         this.fields = this.recordType.prototype.fields;
10740     }
10741     this.modified = [];
10742
10743     this.addEvents({
10744         /**
10745          * @event datachanged
10746          * Fires when the data cache has changed, and a widget which is using this Store
10747          * as a Record cache should refresh its view.
10748          * @param {Store} this
10749          */
10750         datachanged : true,
10751         /**
10752          * @event metachange
10753          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10754          * @param {Store} this
10755          * @param {Object} meta The JSON metadata
10756          */
10757         metachange : true,
10758         /**
10759          * @event add
10760          * Fires when Records have been added to the Store
10761          * @param {Store} this
10762          * @param {Roo.data.Record[]} records The array of Records added
10763          * @param {Number} index The index at which the record(s) were added
10764          */
10765         add : true,
10766         /**
10767          * @event remove
10768          * Fires when a Record has been removed from the Store
10769          * @param {Store} this
10770          * @param {Roo.data.Record} record The Record that was removed
10771          * @param {Number} index The index at which the record was removed
10772          */
10773         remove : true,
10774         /**
10775          * @event update
10776          * Fires when a Record has been updated
10777          * @param {Store} this
10778          * @param {Roo.data.Record} record The Record that was updated
10779          * @param {String} operation The update operation being performed.  Value may be one of:
10780          * <pre><code>
10781  Roo.data.Record.EDIT
10782  Roo.data.Record.REJECT
10783  Roo.data.Record.COMMIT
10784          * </code></pre>
10785          */
10786         update : true,
10787         /**
10788          * @event clear
10789          * Fires when the data cache has been cleared.
10790          * @param {Store} this
10791          */
10792         clear : true,
10793         /**
10794          * @event beforeload
10795          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10796          * the load action will be canceled.
10797          * @param {Store} this
10798          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10799          */
10800         beforeload : true,
10801         /**
10802          * @event beforeloadadd
10803          * Fires after a new set of Records has been loaded.
10804          * @param {Store} this
10805          * @param {Roo.data.Record[]} records The Records that were loaded
10806          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10807          */
10808         beforeloadadd : true,
10809         /**
10810          * @event load
10811          * Fires after a new set of Records has been loaded, before they are added to the store.
10812          * @param {Store} this
10813          * @param {Roo.data.Record[]} records The Records that were loaded
10814          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10815          * @params {Object} return from reader
10816          */
10817         load : true,
10818         /**
10819          * @event loadexception
10820          * Fires if an exception occurs in the Proxy during loading.
10821          * Called with the signature of the Proxy's "loadexception" event.
10822          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10823          * 
10824          * @param {Proxy} 
10825          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10826          * @param {Object} load options 
10827          * @param {Object} jsonData from your request (normally this contains the Exception)
10828          */
10829         loadexception : true
10830     });
10831     
10832     if(this.proxy){
10833         this.proxy = Roo.factory(this.proxy, Roo.data);
10834         this.proxy.xmodule = this.xmodule || false;
10835         this.relayEvents(this.proxy,  ["loadexception"]);
10836     }
10837     this.sortToggle = {};
10838     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10839
10840     Roo.data.Store.superclass.constructor.call(this);
10841
10842     if(this.inlineData){
10843         this.loadData(this.inlineData);
10844         delete this.inlineData;
10845     }
10846 };
10847
10848 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10849      /**
10850     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10851     * without a remote query - used by combo/forms at present.
10852     */
10853     
10854     /**
10855     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10856     */
10857     /**
10858     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10859     */
10860     /**
10861     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10862     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10863     */
10864     /**
10865     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10866     * on any HTTP request
10867     */
10868     /**
10869     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10870     */
10871     /**
10872     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10873     */
10874     multiSort: false,
10875     /**
10876     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10877     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10878     */
10879     remoteSort : false,
10880
10881     /**
10882     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10883      * loaded or when a record is removed. (defaults to false).
10884     */
10885     pruneModifiedRecords : false,
10886
10887     // private
10888     lastOptions : null,
10889
10890     /**
10891      * Add Records to the Store and fires the add event.
10892      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10893      */
10894     add : function(records){
10895         records = [].concat(records);
10896         for(var i = 0, len = records.length; i < len; i++){
10897             records[i].join(this);
10898         }
10899         var index = this.data.length;
10900         this.data.addAll(records);
10901         this.fireEvent("add", this, records, index);
10902     },
10903
10904     /**
10905      * Remove a Record from the Store and fires the remove event.
10906      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10907      */
10908     remove : function(record){
10909         var index = this.data.indexOf(record);
10910         this.data.removeAt(index);
10911         if(this.pruneModifiedRecords){
10912             this.modified.remove(record);
10913         }
10914         this.fireEvent("remove", this, record, index);
10915     },
10916
10917     /**
10918      * Remove all Records from the Store and fires the clear event.
10919      */
10920     removeAll : function(){
10921         this.data.clear();
10922         if(this.pruneModifiedRecords){
10923             this.modified = [];
10924         }
10925         this.fireEvent("clear", this);
10926     },
10927
10928     /**
10929      * Inserts Records to the Store at the given index and fires the add event.
10930      * @param {Number} index The start index at which to insert the passed Records.
10931      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10932      */
10933     insert : function(index, records){
10934         records = [].concat(records);
10935         for(var i = 0, len = records.length; i < len; i++){
10936             this.data.insert(index, records[i]);
10937             records[i].join(this);
10938         }
10939         this.fireEvent("add", this, records, index);
10940     },
10941
10942     /**
10943      * Get the index within the cache of the passed Record.
10944      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10945      * @return {Number} The index of the passed Record. Returns -1 if not found.
10946      */
10947     indexOf : function(record){
10948         return this.data.indexOf(record);
10949     },
10950
10951     /**
10952      * Get the index within the cache of the Record with the passed id.
10953      * @param {String} id The id of the Record to find.
10954      * @return {Number} The index of the Record. Returns -1 if not found.
10955      */
10956     indexOfId : function(id){
10957         return this.data.indexOfKey(id);
10958     },
10959
10960     /**
10961      * Get the Record with the specified id.
10962      * @param {String} id The id of the Record to find.
10963      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10964      */
10965     getById : function(id){
10966         return this.data.key(id);
10967     },
10968
10969     /**
10970      * Get the Record at the specified index.
10971      * @param {Number} index The index of the Record to find.
10972      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10973      */
10974     getAt : function(index){
10975         return this.data.itemAt(index);
10976     },
10977
10978     /**
10979      * Returns a range of Records between specified indices.
10980      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10981      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10982      * @return {Roo.data.Record[]} An array of Records
10983      */
10984     getRange : function(start, end){
10985         return this.data.getRange(start, end);
10986     },
10987
10988     // private
10989     storeOptions : function(o){
10990         o = Roo.apply({}, o);
10991         delete o.callback;
10992         delete o.scope;
10993         this.lastOptions = o;
10994     },
10995
10996     /**
10997      * Loads the Record cache from the configured Proxy using the configured Reader.
10998      * <p>
10999      * If using remote paging, then the first load call must specify the <em>start</em>
11000      * and <em>limit</em> properties in the options.params property to establish the initial
11001      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11002      * <p>
11003      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11004      * and this call will return before the new data has been loaded. Perform any post-processing
11005      * in a callback function, or in a "load" event handler.</strong>
11006      * <p>
11007      * @param {Object} options An object containing properties which control loading options:<ul>
11008      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11009      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11010      * passed the following arguments:<ul>
11011      * <li>r : Roo.data.Record[]</li>
11012      * <li>options: Options object from the load call</li>
11013      * <li>success: Boolean success indicator</li></ul></li>
11014      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11015      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11016      * </ul>
11017      */
11018     load : function(options){
11019         options = options || {};
11020         if(this.fireEvent("beforeload", this, options) !== false){
11021             this.storeOptions(options);
11022             var p = Roo.apply(options.params || {}, this.baseParams);
11023             // if meta was not loaded from remote source.. try requesting it.
11024             if (!this.reader.metaFromRemote) {
11025                 p._requestMeta = 1;
11026             }
11027             if(this.sortInfo && this.remoteSort){
11028                 var pn = this.paramNames;
11029                 p[pn["sort"]] = this.sortInfo.field;
11030                 p[pn["dir"]] = this.sortInfo.direction;
11031             }
11032             if (this.multiSort) {
11033                 var pn = this.paramNames;
11034                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11035             }
11036             
11037             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11038         }
11039     },
11040
11041     /**
11042      * Reloads the Record cache from the configured Proxy using the configured Reader and
11043      * the options from the last load operation performed.
11044      * @param {Object} options (optional) An object containing properties which may override the options
11045      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11046      * the most recently used options are reused).
11047      */
11048     reload : function(options){
11049         this.load(Roo.applyIf(options||{}, this.lastOptions));
11050     },
11051
11052     // private
11053     // Called as a callback by the Reader during a load operation.
11054     loadRecords : function(o, options, success){
11055         if(!o || success === false){
11056             if(success !== false){
11057                 this.fireEvent("load", this, [], options, o);
11058             }
11059             if(options.callback){
11060                 options.callback.call(options.scope || this, [], options, false);
11061             }
11062             return;
11063         }
11064         // if data returned failure - throw an exception.
11065         if (o.success === false) {
11066             // show a message if no listener is registered.
11067             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11068                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11069             }
11070             // loadmask wil be hooked into this..
11071             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11072             return;
11073         }
11074         var r = o.records, t = o.totalRecords || r.length;
11075         
11076         this.fireEvent("beforeloadadd", this, r, options, o);
11077         
11078         if(!options || options.add !== true){
11079             if(this.pruneModifiedRecords){
11080                 this.modified = [];
11081             }
11082             for(var i = 0, len = r.length; i < len; i++){
11083                 r[i].join(this);
11084             }
11085             if(this.snapshot){
11086                 this.data = this.snapshot;
11087                 delete this.snapshot;
11088             }
11089             this.data.clear();
11090             this.data.addAll(r);
11091             this.totalLength = t;
11092             this.applySort();
11093             this.fireEvent("datachanged", this);
11094         }else{
11095             this.totalLength = Math.max(t, this.data.length+r.length);
11096             this.add(r);
11097         }
11098         
11099         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11100                 
11101             var e = new Roo.data.Record({});
11102
11103             e.set(this.parent.displayField, this.parent.emptyTitle);
11104             e.set(this.parent.valueField, '');
11105
11106             this.insert(0, e);
11107         }
11108             
11109         this.fireEvent("load", this, r, options, o);
11110         if(options.callback){
11111             options.callback.call(options.scope || this, r, options, true);
11112         }
11113     },
11114
11115
11116     /**
11117      * Loads data from a passed data block. A Reader which understands the format of the data
11118      * must have been configured in the constructor.
11119      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11120      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11121      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11122      */
11123     loadData : function(o, append){
11124         var r = this.reader.readRecords(o);
11125         this.loadRecords(r, {add: append}, true);
11126     },
11127
11128     /**
11129      * Gets the number of cached records.
11130      * <p>
11131      * <em>If using paging, this may not be the total size of the dataset. If the data object
11132      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11133      * the data set size</em>
11134      */
11135     getCount : function(){
11136         return this.data.length || 0;
11137     },
11138
11139     /**
11140      * Gets the total number of records in the dataset as returned by the server.
11141      * <p>
11142      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11143      * the dataset size</em>
11144      */
11145     getTotalCount : function(){
11146         return this.totalLength || 0;
11147     },
11148
11149     /**
11150      * Returns the sort state of the Store as an object with two properties:
11151      * <pre><code>
11152  field {String} The name of the field by which the Records are sorted
11153  direction {String} The sort order, "ASC" or "DESC"
11154      * </code></pre>
11155      */
11156     getSortState : function(){
11157         return this.sortInfo;
11158     },
11159
11160     // private
11161     applySort : function(){
11162         if(this.sortInfo && !this.remoteSort){
11163             var s = this.sortInfo, f = s.field;
11164             var st = this.fields.get(f).sortType;
11165             var fn = function(r1, r2){
11166                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11167                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11168             };
11169             this.data.sort(s.direction, fn);
11170             if(this.snapshot && this.snapshot != this.data){
11171                 this.snapshot.sort(s.direction, fn);
11172             }
11173         }
11174     },
11175
11176     /**
11177      * Sets the default sort column and order to be used by the next load operation.
11178      * @param {String} fieldName The name of the field to sort by.
11179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11180      */
11181     setDefaultSort : function(field, dir){
11182         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11183     },
11184
11185     /**
11186      * Sort the Records.
11187      * If remote sorting is used, the sort is performed on the server, and the cache is
11188      * reloaded. If local sorting is used, the cache is sorted internally.
11189      * @param {String} fieldName The name of the field to sort by.
11190      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11191      */
11192     sort : function(fieldName, dir){
11193         var f = this.fields.get(fieldName);
11194         if(!dir){
11195             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11196             
11197             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11198                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11199             }else{
11200                 dir = f.sortDir;
11201             }
11202         }
11203         this.sortToggle[f.name] = dir;
11204         this.sortInfo = {field: f.name, direction: dir};
11205         if(!this.remoteSort){
11206             this.applySort();
11207             this.fireEvent("datachanged", this);
11208         }else{
11209             this.load(this.lastOptions);
11210         }
11211     },
11212
11213     /**
11214      * Calls the specified function for each of the Records in the cache.
11215      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11216      * Returning <em>false</em> aborts and exits the iteration.
11217      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11218      */
11219     each : function(fn, scope){
11220         this.data.each(fn, scope);
11221     },
11222
11223     /**
11224      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11225      * (e.g., during paging).
11226      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11227      */
11228     getModifiedRecords : function(){
11229         return this.modified;
11230     },
11231
11232     // private
11233     createFilterFn : function(property, value, anyMatch){
11234         if(!value.exec){ // not a regex
11235             value = String(value);
11236             if(value.length == 0){
11237                 return false;
11238             }
11239             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11240         }
11241         return function(r){
11242             return value.test(r.data[property]);
11243         };
11244     },
11245
11246     /**
11247      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11248      * @param {String} property A field on your records
11249      * @param {Number} start The record index to start at (defaults to 0)
11250      * @param {Number} end The last record index to include (defaults to length - 1)
11251      * @return {Number} The sum
11252      */
11253     sum : function(property, start, end){
11254         var rs = this.data.items, v = 0;
11255         start = start || 0;
11256         end = (end || end === 0) ? end : rs.length-1;
11257
11258         for(var i = start; i <= end; i++){
11259             v += (rs[i].data[property] || 0);
11260         }
11261         return v;
11262     },
11263
11264     /**
11265      * Filter the records by a specified property.
11266      * @param {String} field A field on your records
11267      * @param {String/RegExp} value Either a string that the field
11268      * should start with or a RegExp to test against the field
11269      * @param {Boolean} anyMatch True to match any part not just the beginning
11270      */
11271     filter : function(property, value, anyMatch){
11272         var fn = this.createFilterFn(property, value, anyMatch);
11273         return fn ? this.filterBy(fn) : this.clearFilter();
11274     },
11275
11276     /**
11277      * Filter by a function. The specified function will be called with each
11278      * record in this data source. If the function returns true the record is included,
11279      * otherwise it is filtered.
11280      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11281      * @param {Object} scope (optional) The scope of the function (defaults to this)
11282      */
11283     filterBy : function(fn, scope){
11284         this.snapshot = this.snapshot || this.data;
11285         this.data = this.queryBy(fn, scope||this);
11286         this.fireEvent("datachanged", this);
11287     },
11288
11289     /**
11290      * Query the records by a specified property.
11291      * @param {String} field A field on your records
11292      * @param {String/RegExp} value Either a string that the field
11293      * should start with or a RegExp to test against the field
11294      * @param {Boolean} anyMatch True to match any part not just the beginning
11295      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11296      */
11297     query : function(property, value, anyMatch){
11298         var fn = this.createFilterFn(property, value, anyMatch);
11299         return fn ? this.queryBy(fn) : this.data.clone();
11300     },
11301
11302     /**
11303      * Query by a function. The specified function will be called with each
11304      * record in this data source. If the function returns true the record is included
11305      * in the results.
11306      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11307      * @param {Object} scope (optional) The scope of the function (defaults to this)
11308       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      **/
11310     queryBy : function(fn, scope){
11311         var data = this.snapshot || this.data;
11312         return data.filterBy(fn, scope||this);
11313     },
11314
11315     /**
11316      * Collects unique values for a particular dataIndex from this store.
11317      * @param {String} dataIndex The property to collect
11318      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11319      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11320      * @return {Array} An array of the unique values
11321      **/
11322     collect : function(dataIndex, allowNull, bypassFilter){
11323         var d = (bypassFilter === true && this.snapshot) ?
11324                 this.snapshot.items : this.data.items;
11325         var v, sv, r = [], l = {};
11326         for(var i = 0, len = d.length; i < len; i++){
11327             v = d[i].data[dataIndex];
11328             sv = String(v);
11329             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11330                 l[sv] = true;
11331                 r[r.length] = v;
11332             }
11333         }
11334         return r;
11335     },
11336
11337     /**
11338      * Revert to a view of the Record cache with no filtering applied.
11339      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11340      */
11341     clearFilter : function(suppressEvent){
11342         if(this.snapshot && this.snapshot != this.data){
11343             this.data = this.snapshot;
11344             delete this.snapshot;
11345             if(suppressEvent !== true){
11346                 this.fireEvent("datachanged", this);
11347             }
11348         }
11349     },
11350
11351     // private
11352     afterEdit : function(record){
11353         if(this.modified.indexOf(record) == -1){
11354             this.modified.push(record);
11355         }
11356         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11357     },
11358     
11359     // private
11360     afterReject : function(record){
11361         this.modified.remove(record);
11362         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11363     },
11364
11365     // private
11366     afterCommit : function(record){
11367         this.modified.remove(record);
11368         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11369     },
11370
11371     /**
11372      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11373      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11374      */
11375     commitChanges : function(){
11376         var m = this.modified.slice(0);
11377         this.modified = [];
11378         for(var i = 0, len = m.length; i < len; i++){
11379             m[i].commit();
11380         }
11381     },
11382
11383     /**
11384      * Cancel outstanding changes on all changed records.
11385      */
11386     rejectChanges : function(){
11387         var m = this.modified.slice(0);
11388         this.modified = [];
11389         for(var i = 0, len = m.length; i < len; i++){
11390             m[i].reject();
11391         }
11392     },
11393
11394     onMetaChange : function(meta, rtype, o){
11395         this.recordType = rtype;
11396         this.fields = rtype.prototype.fields;
11397         delete this.snapshot;
11398         this.sortInfo = meta.sortInfo || this.sortInfo;
11399         this.modified = [];
11400         this.fireEvent('metachange', this, this.reader.meta);
11401     },
11402     
11403     moveIndex : function(data, type)
11404     {
11405         var index = this.indexOf(data);
11406         
11407         var newIndex = index + type;
11408         
11409         this.remove(data);
11410         
11411         this.insert(newIndex, data);
11412         
11413     }
11414 });/*
11415  * Based on:
11416  * Ext JS Library 1.1.1
11417  * Copyright(c) 2006-2007, Ext JS, LLC.
11418  *
11419  * Originally Released Under LGPL - original licence link has changed is not relivant.
11420  *
11421  * Fork - LGPL
11422  * <script type="text/javascript">
11423  */
11424
11425 /**
11426  * @class Roo.data.SimpleStore
11427  * @extends Roo.data.Store
11428  * Small helper class to make creating Stores from Array data easier.
11429  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11430  * @cfg {Array} fields An array of field definition objects, or field name strings.
11431  * @cfg {Array} data The multi-dimensional array of data
11432  * @constructor
11433  * @param {Object} config
11434  */
11435 Roo.data.SimpleStore = function(config){
11436     Roo.data.SimpleStore.superclass.constructor.call(this, {
11437         isLocal : true,
11438         reader: new Roo.data.ArrayReader({
11439                 id: config.id
11440             },
11441             Roo.data.Record.create(config.fields)
11442         ),
11443         proxy : new Roo.data.MemoryProxy(config.data)
11444     });
11445     this.load();
11446 };
11447 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458 /**
11459 /**
11460  * @extends Roo.data.Store
11461  * @class Roo.data.JsonStore
11462  * Small helper class to make creating Stores for JSON data easier. <br/>
11463 <pre><code>
11464 var store = new Roo.data.JsonStore({
11465     url: 'get-images.php',
11466     root: 'images',
11467     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11468 });
11469 </code></pre>
11470  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11471  * JsonReader and HttpProxy (unless inline data is provided).</b>
11472  * @cfg {Array} fields An array of field definition objects, or field name strings.
11473  * @constructor
11474  * @param {Object} config
11475  */
11476 Roo.data.JsonStore = function(c){
11477     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11478         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11479         reader: new Roo.data.JsonReader(c, c.fields)
11480     }));
11481 };
11482 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493  
11494 Roo.data.Field = function(config){
11495     if(typeof config == "string"){
11496         config = {name: config};
11497     }
11498     Roo.apply(this, config);
11499     
11500     if(!this.type){
11501         this.type = "auto";
11502     }
11503     
11504     var st = Roo.data.SortTypes;
11505     // named sortTypes are supported, here we look them up
11506     if(typeof this.sortType == "string"){
11507         this.sortType = st[this.sortType];
11508     }
11509     
11510     // set default sortType for strings and dates
11511     if(!this.sortType){
11512         switch(this.type){
11513             case "string":
11514                 this.sortType = st.asUCString;
11515                 break;
11516             case "date":
11517                 this.sortType = st.asDate;
11518                 break;
11519             default:
11520                 this.sortType = st.none;
11521         }
11522     }
11523
11524     // define once
11525     var stripRe = /[\$,%]/g;
11526
11527     // prebuilt conversion function for this field, instead of
11528     // switching every time we're reading a value
11529     if(!this.convert){
11530         var cv, dateFormat = this.dateFormat;
11531         switch(this.type){
11532             case "":
11533             case "auto":
11534             case undefined:
11535                 cv = function(v){ return v; };
11536                 break;
11537             case "string":
11538                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11539                 break;
11540             case "int":
11541                 cv = function(v){
11542                     return v !== undefined && v !== null && v !== '' ?
11543                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11544                     };
11545                 break;
11546             case "float":
11547                 cv = function(v){
11548                     return v !== undefined && v !== null && v !== '' ?
11549                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11550                     };
11551                 break;
11552             case "bool":
11553             case "boolean":
11554                 cv = function(v){ return v === true || v === "true" || v == 1; };
11555                 break;
11556             case "date":
11557                 cv = function(v){
11558                     if(!v){
11559                         return '';
11560                     }
11561                     if(v instanceof Date){
11562                         return v;
11563                     }
11564                     if(dateFormat){
11565                         if(dateFormat == "timestamp"){
11566                             return new Date(v*1000);
11567                         }
11568                         return Date.parseDate(v, dateFormat);
11569                     }
11570                     var parsed = Date.parse(v);
11571                     return parsed ? new Date(parsed) : null;
11572                 };
11573              break;
11574             
11575         }
11576         this.convert = cv;
11577     }
11578 };
11579
11580 Roo.data.Field.prototype = {
11581     dateFormat: null,
11582     defaultValue: "",
11583     mapping: null,
11584     sortType : null,
11585     sortDir : "ASC"
11586 };/*
11587  * Based on:
11588  * Ext JS Library 1.1.1
11589  * Copyright(c) 2006-2007, Ext JS, LLC.
11590  *
11591  * Originally Released Under LGPL - original licence link has changed is not relivant.
11592  *
11593  * Fork - LGPL
11594  * <script type="text/javascript">
11595  */
11596  
11597 // Base class for reading structured data from a data source.  This class is intended to be
11598 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11599
11600 /**
11601  * @class Roo.data.DataReader
11602  * Base class for reading structured data from a data source.  This class is intended to be
11603  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11604  */
11605
11606 Roo.data.DataReader = function(meta, recordType){
11607     
11608     this.meta = meta;
11609     
11610     this.recordType = recordType instanceof Array ? 
11611         Roo.data.Record.create(recordType) : recordType;
11612 };
11613
11614 Roo.data.DataReader.prototype = {
11615      /**
11616      * Create an empty record
11617      * @param {Object} data (optional) - overlay some values
11618      * @return {Roo.data.Record} record created.
11619      */
11620     newRow :  function(d) {
11621         var da =  {};
11622         this.recordType.prototype.fields.each(function(c) {
11623             switch( c.type) {
11624                 case 'int' : da[c.name] = 0; break;
11625                 case 'date' : da[c.name] = new Date(); break;
11626                 case 'float' : da[c.name] = 0.0; break;
11627                 case 'boolean' : da[c.name] = false; break;
11628                 default : da[c.name] = ""; break;
11629             }
11630             
11631         });
11632         return new this.recordType(Roo.apply(da, d));
11633     }
11634     
11635 };/*
11636  * Based on:
11637  * Ext JS Library 1.1.1
11638  * Copyright(c) 2006-2007, Ext JS, LLC.
11639  *
11640  * Originally Released Under LGPL - original licence link has changed is not relivant.
11641  *
11642  * Fork - LGPL
11643  * <script type="text/javascript">
11644  */
11645
11646 /**
11647  * @class Roo.data.DataProxy
11648  * @extends Roo.data.Observable
11649  * This class is an abstract base class for implementations which provide retrieval of
11650  * unformatted data objects.<br>
11651  * <p>
11652  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11653  * (of the appropriate type which knows how to parse the data object) to provide a block of
11654  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11655  * <p>
11656  * Custom implementations must implement the load method as described in
11657  * {@link Roo.data.HttpProxy#load}.
11658  */
11659 Roo.data.DataProxy = function(){
11660     this.addEvents({
11661         /**
11662          * @event beforeload
11663          * Fires before a network request is made to retrieve a data object.
11664          * @param {Object} This DataProxy object.
11665          * @param {Object} params The params parameter to the load function.
11666          */
11667         beforeload : true,
11668         /**
11669          * @event load
11670          * Fires before the load method's callback is called.
11671          * @param {Object} This DataProxy object.
11672          * @param {Object} o The data object.
11673          * @param {Object} arg The callback argument object passed to the load function.
11674          */
11675         load : true,
11676         /**
11677          * @event loadexception
11678          * Fires if an Exception occurs during data retrieval.
11679          * @param {Object} This DataProxy object.
11680          * @param {Object} o The data object.
11681          * @param {Object} arg The callback argument object passed to the load function.
11682          * @param {Object} e The Exception.
11683          */
11684         loadexception : true
11685     });
11686     Roo.data.DataProxy.superclass.constructor.call(this);
11687 };
11688
11689 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11690
11691     /**
11692      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11693      */
11694 /*
11695  * Based on:
11696  * Ext JS Library 1.1.1
11697  * Copyright(c) 2006-2007, Ext JS, LLC.
11698  *
11699  * Originally Released Under LGPL - original licence link has changed is not relivant.
11700  *
11701  * Fork - LGPL
11702  * <script type="text/javascript">
11703  */
11704 /**
11705  * @class Roo.data.MemoryProxy
11706  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11707  * to the Reader when its load method is called.
11708  * @constructor
11709  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11710  */
11711 Roo.data.MemoryProxy = function(data){
11712     if (data.data) {
11713         data = data.data;
11714     }
11715     Roo.data.MemoryProxy.superclass.constructor.call(this);
11716     this.data = data;
11717 };
11718
11719 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11720     
11721     /**
11722      * Load data from the requested source (in this case an in-memory
11723      * data object passed to the constructor), read the data object into
11724      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11725      * process that block using the passed callback.
11726      * @param {Object} params This parameter is not used by the MemoryProxy class.
11727      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11728      * object into a block of Roo.data.Records.
11729      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11730      * The function must be passed <ul>
11731      * <li>The Record block object</li>
11732      * <li>The "arg" argument from the load function</li>
11733      * <li>A boolean success indicator</li>
11734      * </ul>
11735      * @param {Object} scope The scope in which to call the callback
11736      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11737      */
11738     load : function(params, reader, callback, scope, arg){
11739         params = params || {};
11740         var result;
11741         try {
11742             result = reader.readRecords(this.data);
11743         }catch(e){
11744             this.fireEvent("loadexception", this, arg, null, e);
11745             callback.call(scope, null, arg, false);
11746             return;
11747         }
11748         callback.call(scope, result, arg, true);
11749     },
11750     
11751     // private
11752     update : function(params, records){
11753         
11754     }
11755 });/*
11756  * Based on:
11757  * Ext JS Library 1.1.1
11758  * Copyright(c) 2006-2007, Ext JS, LLC.
11759  *
11760  * Originally Released Under LGPL - original licence link has changed is not relivant.
11761  *
11762  * Fork - LGPL
11763  * <script type="text/javascript">
11764  */
11765 /**
11766  * @class Roo.data.HttpProxy
11767  * @extends Roo.data.DataProxy
11768  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11769  * configured to reference a certain URL.<br><br>
11770  * <p>
11771  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11772  * from which the running page was served.<br><br>
11773  * <p>
11774  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11775  * <p>
11776  * Be aware that to enable the browser to parse an XML document, the server must set
11777  * the Content-Type header in the HTTP response to "text/xml".
11778  * @constructor
11779  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11780  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11781  * will be used to make the request.
11782  */
11783 Roo.data.HttpProxy = function(conn){
11784     Roo.data.HttpProxy.superclass.constructor.call(this);
11785     // is conn a conn config or a real conn?
11786     this.conn = conn;
11787     this.useAjax = !conn || !conn.events;
11788   
11789 };
11790
11791 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11792     // thse are take from connection...
11793     
11794     /**
11795      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11796      */
11797     /**
11798      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11799      * extra parameters to each request made by this object. (defaults to undefined)
11800      */
11801     /**
11802      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11803      *  to each request made by this object. (defaults to undefined)
11804      */
11805     /**
11806      * @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)
11807      */
11808     /**
11809      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11810      */
11811      /**
11812      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11813      * @type Boolean
11814      */
11815   
11816
11817     /**
11818      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11819      * @type Boolean
11820      */
11821     /**
11822      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11823      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11824      * a finer-grained basis than the DataProxy events.
11825      */
11826     getConnection : function(){
11827         return this.useAjax ? Roo.Ajax : this.conn;
11828     },
11829
11830     /**
11831      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11832      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11833      * process that block using the passed callback.
11834      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11835      * for the request to the remote server.
11836      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11837      * object into a block of Roo.data.Records.
11838      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11839      * The function must be passed <ul>
11840      * <li>The Record block object</li>
11841      * <li>The "arg" argument from the load function</li>
11842      * <li>A boolean success indicator</li>
11843      * </ul>
11844      * @param {Object} scope The scope in which to call the callback
11845      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11846      */
11847     load : function(params, reader, callback, scope, arg){
11848         if(this.fireEvent("beforeload", this, params) !== false){
11849             var  o = {
11850                 params : params || {},
11851                 request: {
11852                     callback : callback,
11853                     scope : scope,
11854                     arg : arg
11855                 },
11856                 reader: reader,
11857                 callback : this.loadResponse,
11858                 scope: this
11859             };
11860             if(this.useAjax){
11861                 Roo.applyIf(o, this.conn);
11862                 if(this.activeRequest){
11863                     Roo.Ajax.abort(this.activeRequest);
11864                 }
11865                 this.activeRequest = Roo.Ajax.request(o);
11866             }else{
11867                 this.conn.request(o);
11868             }
11869         }else{
11870             callback.call(scope||this, null, arg, false);
11871         }
11872     },
11873
11874     // private
11875     loadResponse : function(o, success, response){
11876         delete this.activeRequest;
11877         if(!success){
11878             this.fireEvent("loadexception", this, o, response);
11879             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11880             return;
11881         }
11882         var result;
11883         try {
11884             result = o.reader.read(response);
11885         }catch(e){
11886             this.fireEvent("loadexception", this, o, response, e);
11887             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11888             return;
11889         }
11890         
11891         this.fireEvent("load", this, o, o.request.arg);
11892         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11893     },
11894
11895     // private
11896     update : function(dataSet){
11897
11898     },
11899
11900     // private
11901     updateResponse : function(dataSet){
11902
11903     }
11904 });/*
11905  * Based on:
11906  * Ext JS Library 1.1.1
11907  * Copyright(c) 2006-2007, Ext JS, LLC.
11908  *
11909  * Originally Released Under LGPL - original licence link has changed is not relivant.
11910  *
11911  * Fork - LGPL
11912  * <script type="text/javascript">
11913  */
11914
11915 /**
11916  * @class Roo.data.ScriptTagProxy
11917  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11918  * other than the originating domain of the running page.<br><br>
11919  * <p>
11920  * <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
11921  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11922  * <p>
11923  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11924  * source code that is used as the source inside a &lt;script> tag.<br><br>
11925  * <p>
11926  * In order for the browser to process the returned data, the server must wrap the data object
11927  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11928  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11929  * depending on whether the callback name was passed:
11930  * <p>
11931  * <pre><code>
11932 boolean scriptTag = false;
11933 String cb = request.getParameter("callback");
11934 if (cb != null) {
11935     scriptTag = true;
11936     response.setContentType("text/javascript");
11937 } else {
11938     response.setContentType("application/x-json");
11939 }
11940 Writer out = response.getWriter();
11941 if (scriptTag) {
11942     out.write(cb + "(");
11943 }
11944 out.print(dataBlock.toJsonString());
11945 if (scriptTag) {
11946     out.write(");");
11947 }
11948 </pre></code>
11949  *
11950  * @constructor
11951  * @param {Object} config A configuration object.
11952  */
11953 Roo.data.ScriptTagProxy = function(config){
11954     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11955     Roo.apply(this, config);
11956     this.head = document.getElementsByTagName("head")[0];
11957 };
11958
11959 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11960
11961 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11962     /**
11963      * @cfg {String} url The URL from which to request the data object.
11964      */
11965     /**
11966      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11967      */
11968     timeout : 30000,
11969     /**
11970      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11971      * the server the name of the callback function set up by the load call to process the returned data object.
11972      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11973      * javascript output which calls this named function passing the data object as its only parameter.
11974      */
11975     callbackParam : "callback",
11976     /**
11977      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11978      * name to the request.
11979      */
11980     nocache : true,
11981
11982     /**
11983      * Load data from the configured URL, read the data object into
11984      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11985      * process that block using the passed callback.
11986      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11987      * for the request to the remote server.
11988      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11989      * object into a block of Roo.data.Records.
11990      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11991      * The function must be passed <ul>
11992      * <li>The Record block object</li>
11993      * <li>The "arg" argument from the load function</li>
11994      * <li>A boolean success indicator</li>
11995      * </ul>
11996      * @param {Object} scope The scope in which to call the callback
11997      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11998      */
11999     load : function(params, reader, callback, scope, arg){
12000         if(this.fireEvent("beforeload", this, params) !== false){
12001
12002             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12003
12004             var url = this.url;
12005             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12006             if(this.nocache){
12007                 url += "&_dc=" + (new Date().getTime());
12008             }
12009             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12010             var trans = {
12011                 id : transId,
12012                 cb : "stcCallback"+transId,
12013                 scriptId : "stcScript"+transId,
12014                 params : params,
12015                 arg : arg,
12016                 url : url,
12017                 callback : callback,
12018                 scope : scope,
12019                 reader : reader
12020             };
12021             var conn = this;
12022
12023             window[trans.cb] = function(o){
12024                 conn.handleResponse(o, trans);
12025             };
12026
12027             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12028
12029             if(this.autoAbort !== false){
12030                 this.abort();
12031             }
12032
12033             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12034
12035             var script = document.createElement("script");
12036             script.setAttribute("src", url);
12037             script.setAttribute("type", "text/javascript");
12038             script.setAttribute("id", trans.scriptId);
12039             this.head.appendChild(script);
12040
12041             this.trans = trans;
12042         }else{
12043             callback.call(scope||this, null, arg, false);
12044         }
12045     },
12046
12047     // private
12048     isLoading : function(){
12049         return this.trans ? true : false;
12050     },
12051
12052     /**
12053      * Abort the current server request.
12054      */
12055     abort : function(){
12056         if(this.isLoading()){
12057             this.destroyTrans(this.trans);
12058         }
12059     },
12060
12061     // private
12062     destroyTrans : function(trans, isLoaded){
12063         this.head.removeChild(document.getElementById(trans.scriptId));
12064         clearTimeout(trans.timeoutId);
12065         if(isLoaded){
12066             window[trans.cb] = undefined;
12067             try{
12068                 delete window[trans.cb];
12069             }catch(e){}
12070         }else{
12071             // if hasn't been loaded, wait for load to remove it to prevent script error
12072             window[trans.cb] = function(){
12073                 window[trans.cb] = undefined;
12074                 try{
12075                     delete window[trans.cb];
12076                 }catch(e){}
12077             };
12078         }
12079     },
12080
12081     // private
12082     handleResponse : function(o, trans){
12083         this.trans = false;
12084         this.destroyTrans(trans, true);
12085         var result;
12086         try {
12087             result = trans.reader.readRecords(o);
12088         }catch(e){
12089             this.fireEvent("loadexception", this, o, trans.arg, e);
12090             trans.callback.call(trans.scope||window, null, trans.arg, false);
12091             return;
12092         }
12093         this.fireEvent("load", this, o, trans.arg);
12094         trans.callback.call(trans.scope||window, result, trans.arg, true);
12095     },
12096
12097     // private
12098     handleFailure : function(trans){
12099         this.trans = false;
12100         this.destroyTrans(trans, false);
12101         this.fireEvent("loadexception", this, null, trans.arg);
12102         trans.callback.call(trans.scope||window, null, trans.arg, false);
12103     }
12104 });/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114
12115 /**
12116  * @class Roo.data.JsonReader
12117  * @extends Roo.data.DataReader
12118  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12119  * based on mappings in a provided Roo.data.Record constructor.
12120  * 
12121  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12122  * in the reply previously. 
12123  * 
12124  * <p>
12125  * Example code:
12126  * <pre><code>
12127 var RecordDef = Roo.data.Record.create([
12128     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12129     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12130 ]);
12131 var myReader = new Roo.data.JsonReader({
12132     totalProperty: "results",    // The property which contains the total dataset size (optional)
12133     root: "rows",                // The property which contains an Array of row objects
12134     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12135 }, RecordDef);
12136 </code></pre>
12137  * <p>
12138  * This would consume a JSON file like this:
12139  * <pre><code>
12140 { 'results': 2, 'rows': [
12141     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12142     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12143 }
12144 </code></pre>
12145  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12146  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12147  * paged from the remote server.
12148  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12149  * @cfg {String} root name of the property which contains the Array of row objects.
12150  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12151  * @cfg {Array} fields Array of field definition objects
12152  * @constructor
12153  * Create a new JsonReader
12154  * @param {Object} meta Metadata configuration options
12155  * @param {Object} recordType Either an Array of field definition objects,
12156  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12157  */
12158 Roo.data.JsonReader = function(meta, recordType){
12159     
12160     meta = meta || {};
12161     // set some defaults:
12162     Roo.applyIf(meta, {
12163         totalProperty: 'total',
12164         successProperty : 'success',
12165         root : 'data',
12166         id : 'id'
12167     });
12168     
12169     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12170 };
12171 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12172     
12173     /**
12174      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12175      * Used by Store query builder to append _requestMeta to params.
12176      * 
12177      */
12178     metaFromRemote : false,
12179     /**
12180      * This method is only used by a DataProxy which has retrieved data from a remote server.
12181      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12182      * @return {Object} data A data block which is used by an Roo.data.Store object as
12183      * a cache of Roo.data.Records.
12184      */
12185     read : function(response){
12186         var json = response.responseText;
12187        
12188         var o = /* eval:var:o */ eval("("+json+")");
12189         if(!o) {
12190             throw {message: "JsonReader.read: Json object not found"};
12191         }
12192         
12193         if(o.metaData){
12194             
12195             delete this.ef;
12196             this.metaFromRemote = true;
12197             this.meta = o.metaData;
12198             this.recordType = Roo.data.Record.create(o.metaData.fields);
12199             this.onMetaChange(this.meta, this.recordType, o);
12200         }
12201         return this.readRecords(o);
12202     },
12203
12204     // private function a store will implement
12205     onMetaChange : function(meta, recordType, o){
12206
12207     },
12208
12209     /**
12210          * @ignore
12211          */
12212     simpleAccess: function(obj, subsc) {
12213         return obj[subsc];
12214     },
12215
12216         /**
12217          * @ignore
12218          */
12219     getJsonAccessor: function(){
12220         var re = /[\[\.]/;
12221         return function(expr) {
12222             try {
12223                 return(re.test(expr))
12224                     ? new Function("obj", "return obj." + expr)
12225                     : function(obj){
12226                         return obj[expr];
12227                     };
12228             } catch(e){}
12229             return Roo.emptyFn;
12230         };
12231     }(),
12232
12233     /**
12234      * Create a data block containing Roo.data.Records from an XML document.
12235      * @param {Object} o An object which contains an Array of row objects in the property specified
12236      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12237      * which contains the total size of the dataset.
12238      * @return {Object} data A data block which is used by an Roo.data.Store object as
12239      * a cache of Roo.data.Records.
12240      */
12241     readRecords : function(o){
12242         /**
12243          * After any data loads, the raw JSON data is available for further custom processing.
12244          * @type Object
12245          */
12246         this.o = o;
12247         var s = this.meta, Record = this.recordType,
12248             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12249
12250 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12251         if (!this.ef) {
12252             if(s.totalProperty) {
12253                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12254                 }
12255                 if(s.successProperty) {
12256                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12257                 }
12258                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12259                 if (s.id) {
12260                         var g = this.getJsonAccessor(s.id);
12261                         this.getId = function(rec) {
12262                                 var r = g(rec);  
12263                                 return (r === undefined || r === "") ? null : r;
12264                         };
12265                 } else {
12266                         this.getId = function(){return null;};
12267                 }
12268             this.ef = [];
12269             for(var jj = 0; jj < fl; jj++){
12270                 f = fi[jj];
12271                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12272                 this.ef[jj] = this.getJsonAccessor(map);
12273             }
12274         }
12275
12276         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12277         if(s.totalProperty){
12278             var vt = parseInt(this.getTotal(o), 10);
12279             if(!isNaN(vt)){
12280                 totalRecords = vt;
12281             }
12282         }
12283         if(s.successProperty){
12284             var vs = this.getSuccess(o);
12285             if(vs === false || vs === 'false'){
12286                 success = false;
12287             }
12288         }
12289         var records = [];
12290         for(var i = 0; i < c; i++){
12291                 var n = root[i];
12292             var values = {};
12293             var id = this.getId(n);
12294             for(var j = 0; j < fl; j++){
12295                 f = fi[j];
12296             var v = this.ef[j](n);
12297             if (!f.convert) {
12298                 Roo.log('missing convert for ' + f.name);
12299                 Roo.log(f);
12300                 continue;
12301             }
12302             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12303             }
12304             var record = new Record(values, id);
12305             record.json = n;
12306             records[i] = record;
12307         }
12308         return {
12309             raw : o,
12310             success : success,
12311             records : records,
12312             totalRecords : totalRecords
12313         };
12314     }
12315 });/*
12316  * Based on:
12317  * Ext JS Library 1.1.1
12318  * Copyright(c) 2006-2007, Ext JS, LLC.
12319  *
12320  * Originally Released Under LGPL - original licence link has changed is not relivant.
12321  *
12322  * Fork - LGPL
12323  * <script type="text/javascript">
12324  */
12325
12326 /**
12327  * @class Roo.data.ArrayReader
12328  * @extends Roo.data.DataReader
12329  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12330  * Each element of that Array represents a row of data fields. The
12331  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12332  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12333  * <p>
12334  * Example code:.
12335  * <pre><code>
12336 var RecordDef = Roo.data.Record.create([
12337     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12338     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12339 ]);
12340 var myReader = new Roo.data.ArrayReader({
12341     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12342 }, RecordDef);
12343 </code></pre>
12344  * <p>
12345  * This would consume an Array like this:
12346  * <pre><code>
12347 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12348   </code></pre>
12349  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12350  * @constructor
12351  * Create a new JsonReader
12352  * @param {Object} meta Metadata configuration options.
12353  * @param {Object} recordType Either an Array of field definition objects
12354  * as specified to {@link Roo.data.Record#create},
12355  * or an {@link Roo.data.Record} object
12356  * created using {@link Roo.data.Record#create}.
12357  */
12358 Roo.data.ArrayReader = function(meta, recordType){
12359     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12360 };
12361
12362 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12363     /**
12364      * Create a data block containing Roo.data.Records from an XML document.
12365      * @param {Object} o An Array of row objects which represents the dataset.
12366      * @return {Object} data A data block which is used by an Roo.data.Store object as
12367      * a cache of Roo.data.Records.
12368      */
12369     readRecords : function(o){
12370         var sid = this.meta ? this.meta.id : null;
12371         var recordType = this.recordType, fields = recordType.prototype.fields;
12372         var records = [];
12373         var root = o;
12374             for(var i = 0; i < root.length; i++){
12375                     var n = root[i];
12376                 var values = {};
12377                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12378                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12379                 var f = fields.items[j];
12380                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12381                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12382                 v = f.convert(v);
12383                 values[f.name] = v;
12384             }
12385                 var record = new recordType(values, id);
12386                 record.json = n;
12387                 records[records.length] = record;
12388             }
12389             return {
12390                 records : records,
12391                 totalRecords : records.length
12392             };
12393     }
12394 });/*
12395  * - LGPL
12396  * * 
12397  */
12398
12399 /**
12400  * @class Roo.bootstrap.ComboBox
12401  * @extends Roo.bootstrap.TriggerField
12402  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12403  * @cfg {Boolean} append (true|false) default false
12404  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12405  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12406  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12407  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12408  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12409  * @cfg {Boolean} animate default true
12410  * @cfg {Boolean} emptyResultText only for touch device
12411  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12412  * @cfg {String} emptyTitle default ''
12413  * @constructor
12414  * Create a new ComboBox.
12415  * @param {Object} config Configuration options
12416  */
12417 Roo.bootstrap.ComboBox = function(config){
12418     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12419     this.addEvents({
12420         /**
12421          * @event expand
12422          * Fires when the dropdown list is expanded
12423         * @param {Roo.bootstrap.ComboBox} combo This combo box
12424         */
12425         'expand' : true,
12426         /**
12427          * @event collapse
12428          * Fires when the dropdown list is collapsed
12429         * @param {Roo.bootstrap.ComboBox} combo This combo box
12430         */
12431         'collapse' : true,
12432         /**
12433          * @event beforeselect
12434          * Fires before a list item is selected. Return false to cancel the selection.
12435         * @param {Roo.bootstrap.ComboBox} combo This combo box
12436         * @param {Roo.data.Record} record The data record returned from the underlying store
12437         * @param {Number} index The index of the selected item in the dropdown list
12438         */
12439         'beforeselect' : true,
12440         /**
12441          * @event select
12442          * Fires when a list item is selected
12443         * @param {Roo.bootstrap.ComboBox} combo This combo box
12444         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12445         * @param {Number} index The index of the selected item in the dropdown list
12446         */
12447         'select' : true,
12448         /**
12449          * @event beforequery
12450          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12451          * The event object passed has these properties:
12452         * @param {Roo.bootstrap.ComboBox} combo This combo box
12453         * @param {String} query The query
12454         * @param {Boolean} forceAll true to force "all" query
12455         * @param {Boolean} cancel true to cancel the query
12456         * @param {Object} e The query event object
12457         */
12458         'beforequery': true,
12459          /**
12460          * @event add
12461          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12462         * @param {Roo.bootstrap.ComboBox} combo This combo box
12463         */
12464         'add' : true,
12465         /**
12466          * @event edit
12467          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12468         * @param {Roo.bootstrap.ComboBox} combo This combo box
12469         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12470         */
12471         'edit' : true,
12472         /**
12473          * @event remove
12474          * Fires when the remove value from the combobox array
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'remove' : true,
12478         /**
12479          * @event afterremove
12480          * Fires when the remove value from the combobox array
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         */
12483         'afterremove' : true,
12484         /**
12485          * @event specialfilter
12486          * Fires when specialfilter
12487             * @param {Roo.bootstrap.ComboBox} combo This combo box
12488             */
12489         'specialfilter' : true,
12490         /**
12491          * @event tick
12492          * Fires when tick the element
12493             * @param {Roo.bootstrap.ComboBox} combo This combo box
12494             */
12495         'tick' : true,
12496         /**
12497          * @event touchviewdisplay
12498          * Fires when touch view require special display (default is using displayField)
12499             * @param {Roo.bootstrap.ComboBox} combo This combo box
12500             * @param {Object} cfg set html .
12501             */
12502         'touchviewdisplay' : true
12503         
12504     });
12505     
12506     this.item = [];
12507     this.tickItems = [];
12508     
12509     this.selectedIndex = -1;
12510     if(this.mode == 'local'){
12511         if(config.queryDelay === undefined){
12512             this.queryDelay = 10;
12513         }
12514         if(config.minChars === undefined){
12515             this.minChars = 0;
12516         }
12517     }
12518 };
12519
12520 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12521      
12522     /**
12523      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12524      * rendering into an Roo.Editor, defaults to false)
12525      */
12526     /**
12527      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12528      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12529      */
12530     /**
12531      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12532      */
12533     /**
12534      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12535      * the dropdown list (defaults to undefined, with no header element)
12536      */
12537
12538      /**
12539      * @cfg {String/Roo.Template} tpl The template to use to render the output
12540      */
12541      
12542      /**
12543      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12544      */
12545     listWidth: undefined,
12546     /**
12547      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12548      * mode = 'remote' or 'text' if mode = 'local')
12549      */
12550     displayField: undefined,
12551     
12552     /**
12553      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12554      * mode = 'remote' or 'value' if mode = 'local'). 
12555      * Note: use of a valueField requires the user make a selection
12556      * in order for a value to be mapped.
12557      */
12558     valueField: undefined,
12559     /**
12560      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12561      */
12562     modalTitle : '',
12563     
12564     /**
12565      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12566      * field's data value (defaults to the underlying DOM element's name)
12567      */
12568     hiddenName: undefined,
12569     /**
12570      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12571      */
12572     listClass: '',
12573     /**
12574      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12575      */
12576     selectedClass: 'active',
12577     
12578     /**
12579      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12580      */
12581     shadow:'sides',
12582     /**
12583      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12584      * anchor positions (defaults to 'tl-bl')
12585      */
12586     listAlign: 'tl-bl?',
12587     /**
12588      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12589      */
12590     maxHeight: 300,
12591     /**
12592      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12593      * query specified by the allQuery config option (defaults to 'query')
12594      */
12595     triggerAction: 'query',
12596     /**
12597      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12598      * (defaults to 4, does not apply if editable = false)
12599      */
12600     minChars : 4,
12601     /**
12602      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12603      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12604      */
12605     typeAhead: false,
12606     /**
12607      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12608      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12609      */
12610     queryDelay: 500,
12611     /**
12612      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12613      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12614      */
12615     pageSize: 0,
12616     /**
12617      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12618      * when editable = true (defaults to false)
12619      */
12620     selectOnFocus:false,
12621     /**
12622      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12623      */
12624     queryParam: 'query',
12625     /**
12626      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12627      * when mode = 'remote' (defaults to 'Loading...')
12628      */
12629     loadingText: 'Loading...',
12630     /**
12631      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12632      */
12633     resizable: false,
12634     /**
12635      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12636      */
12637     handleHeight : 8,
12638     /**
12639      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12640      * traditional select (defaults to true)
12641      */
12642     editable: true,
12643     /**
12644      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12645      */
12646     allQuery: '',
12647     /**
12648      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12649      */
12650     mode: 'remote',
12651     /**
12652      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12653      * listWidth has a higher value)
12654      */
12655     minListWidth : 70,
12656     /**
12657      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12658      * allow the user to set arbitrary text into the field (defaults to false)
12659      */
12660     forceSelection:false,
12661     /**
12662      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12663      * if typeAhead = true (defaults to 250)
12664      */
12665     typeAheadDelay : 250,
12666     /**
12667      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12668      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12669      */
12670     valueNotFoundText : undefined,
12671     /**
12672      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12673      */
12674     blockFocus : false,
12675     
12676     /**
12677      * @cfg {Boolean} disableClear Disable showing of clear button.
12678      */
12679     disableClear : false,
12680     /**
12681      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12682      */
12683     alwaysQuery : false,
12684     
12685     /**
12686      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12687      */
12688     multiple : false,
12689     
12690     /**
12691      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12692      */
12693     invalidClass : "has-warning",
12694     
12695     /**
12696      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12697      */
12698     validClass : "has-success",
12699     
12700     /**
12701      * @cfg {Boolean} specialFilter (true|false) special filter default false
12702      */
12703     specialFilter : false,
12704     
12705     /**
12706      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12707      */
12708     mobileTouchView : true,
12709     
12710     /**
12711      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12712      */
12713     useNativeIOS : false,
12714     
12715     ios_options : false,
12716     
12717     //private
12718     addicon : false,
12719     editicon: false,
12720     
12721     page: 0,
12722     hasQuery: false,
12723     append: false,
12724     loadNext: false,
12725     autoFocus : true,
12726     tickable : false,
12727     btnPosition : 'right',
12728     triggerList : true,
12729     showToggleBtn : true,
12730     animate : true,
12731     emptyResultText: 'Empty',
12732     triggerText : 'Select',
12733     emptyTitle : '',
12734     
12735     // element that contains real text value.. (when hidden is used..)
12736     
12737     getAutoCreate : function()
12738     {   
12739         var cfg = false;
12740         //render
12741         /*
12742          * Render classic select for iso
12743          */
12744         
12745         if(Roo.isIOS && this.useNativeIOS){
12746             cfg = this.getAutoCreateNativeIOS();
12747             return cfg;
12748         }
12749         
12750         /*
12751          * Touch Devices
12752          */
12753         
12754         if(Roo.isTouch && this.mobileTouchView){
12755             cfg = this.getAutoCreateTouchView();
12756             return cfg;;
12757         }
12758         
12759         /*
12760          *  Normal ComboBox
12761          */
12762         if(!this.tickable){
12763             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12764             return cfg;
12765         }
12766         
12767         /*
12768          *  ComboBox with tickable selections
12769          */
12770              
12771         var align = this.labelAlign || this.parentLabelAlign();
12772         
12773         cfg = {
12774             cls : 'form-group roo-combobox-tickable' //input-group
12775         };
12776         
12777         var btn_text_select = '';
12778         var btn_text_done = '';
12779         var btn_text_cancel = '';
12780         
12781         if (this.btn_text_show) {
12782             btn_text_select = 'Select';
12783             btn_text_done = 'Done';
12784             btn_text_cancel = 'Cancel'; 
12785         }
12786         
12787         var buttons = {
12788             tag : 'div',
12789             cls : 'tickable-buttons',
12790             cn : [
12791                 {
12792                     tag : 'button',
12793                     type : 'button',
12794                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12795                     //html : this.triggerText
12796                     html: btn_text_select
12797                 },
12798                 {
12799                     tag : 'button',
12800                     type : 'button',
12801                     name : 'ok',
12802                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12803                     //html : 'Done'
12804                     html: btn_text_done
12805                 },
12806                 {
12807                     tag : 'button',
12808                     type : 'button',
12809                     name : 'cancel',
12810                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12811                     //html : 'Cancel'
12812                     html: btn_text_cancel
12813                 }
12814             ]
12815         };
12816         
12817         if(this.editable){
12818             buttons.cn.unshift({
12819                 tag: 'input',
12820                 cls: 'roo-select2-search-field-input'
12821             });
12822         }
12823         
12824         var _this = this;
12825         
12826         Roo.each(buttons.cn, function(c){
12827             if (_this.size) {
12828                 c.cls += ' btn-' + _this.size;
12829             }
12830
12831             if (_this.disabled) {
12832                 c.disabled = true;
12833             }
12834         });
12835         
12836         var box = {
12837             tag: 'div',
12838             cn: [
12839                 {
12840                     tag: 'input',
12841                     type : 'hidden',
12842                     cls: 'form-hidden-field'
12843                 },
12844                 {
12845                     tag: 'ul',
12846                     cls: 'roo-select2-choices',
12847                     cn:[
12848                         {
12849                             tag: 'li',
12850                             cls: 'roo-select2-search-field',
12851                             cn: [
12852                                 buttons
12853                             ]
12854                         }
12855                     ]
12856                 }
12857             ]
12858         };
12859         
12860         var combobox = {
12861             cls: 'roo-select2-container input-group roo-select2-container-multi',
12862             cn: [
12863                 box
12864 //                {
12865 //                    tag: 'ul',
12866 //                    cls: 'typeahead typeahead-long dropdown-menu',
12867 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12868 //                }
12869             ]
12870         };
12871         
12872         if(this.hasFeedback && !this.allowBlank){
12873             
12874             var feedback = {
12875                 tag: 'span',
12876                 cls: 'glyphicon form-control-feedback'
12877             };
12878
12879             combobox.cn.push(feedback);
12880         }
12881         
12882         
12883         if (align ==='left' && this.fieldLabel.length) {
12884             
12885             cfg.cls += ' roo-form-group-label-left';
12886             
12887             cfg.cn = [
12888                 {
12889                     tag : 'i',
12890                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12891                     tooltip : 'This field is required'
12892                 },
12893                 {
12894                     tag: 'label',
12895                     'for' :  id,
12896                     cls : 'control-label',
12897                     html : this.fieldLabel
12898
12899                 },
12900                 {
12901                     cls : "", 
12902                     cn: [
12903                         combobox
12904                     ]
12905                 }
12906
12907             ];
12908             
12909             var labelCfg = cfg.cn[1];
12910             var contentCfg = cfg.cn[2];
12911             
12912
12913             if(this.indicatorpos == 'right'){
12914                 
12915                 cfg.cn = [
12916                     {
12917                         tag: 'label',
12918                         'for' :  id,
12919                         cls : 'control-label',
12920                         cn : [
12921                             {
12922                                 tag : 'span',
12923                                 html : this.fieldLabel
12924                             },
12925                             {
12926                                 tag : 'i',
12927                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12928                                 tooltip : 'This field is required'
12929                             }
12930                         ]
12931                     },
12932                     {
12933                         cls : "",
12934                         cn: [
12935                             combobox
12936                         ]
12937                     }
12938
12939                 ];
12940                 
12941                 
12942                 
12943                 labelCfg = cfg.cn[0];
12944                 contentCfg = cfg.cn[1];
12945             
12946             }
12947             
12948             if(this.labelWidth > 12){
12949                 labelCfg.style = "width: " + this.labelWidth + 'px';
12950             }
12951             
12952             if(this.labelWidth < 13 && this.labelmd == 0){
12953                 this.labelmd = this.labelWidth;
12954             }
12955             
12956             if(this.labellg > 0){
12957                 labelCfg.cls += ' col-lg-' + this.labellg;
12958                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12959             }
12960             
12961             if(this.labelmd > 0){
12962                 labelCfg.cls += ' col-md-' + this.labelmd;
12963                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12964             }
12965             
12966             if(this.labelsm > 0){
12967                 labelCfg.cls += ' col-sm-' + this.labelsm;
12968                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12969             }
12970             
12971             if(this.labelxs > 0){
12972                 labelCfg.cls += ' col-xs-' + this.labelxs;
12973                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12974             }
12975                 
12976                 
12977         } else if ( this.fieldLabel.length) {
12978 //                Roo.log(" label");
12979                  cfg.cn = [
12980                     {
12981                         tag : 'i',
12982                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12983                         tooltip : 'This field is required'
12984                     },
12985                     {
12986                         tag: 'label',
12987                         //cls : 'input-group-addon',
12988                         html : this.fieldLabel
12989                     },
12990                     combobox
12991                 ];
12992                 
12993                 if(this.indicatorpos == 'right'){
12994                     cfg.cn = [
12995                         {
12996                             tag: 'label',
12997                             //cls : 'input-group-addon',
12998                             html : this.fieldLabel
12999                         },
13000                         {
13001                             tag : 'i',
13002                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13003                             tooltip : 'This field is required'
13004                         },
13005                         combobox
13006                     ];
13007                     
13008                 }
13009
13010         } else {
13011             
13012 //                Roo.log(" no label && no align");
13013                 cfg = combobox
13014                      
13015                 
13016         }
13017          
13018         var settings=this;
13019         ['xs','sm','md','lg'].map(function(size){
13020             if (settings[size]) {
13021                 cfg.cls += ' col-' + size + '-' + settings[size];
13022             }
13023         });
13024         
13025         return cfg;
13026         
13027     },
13028     
13029     _initEventsCalled : false,
13030     
13031     // private
13032     initEvents: function()
13033     {   
13034         if (this._initEventsCalled) { // as we call render... prevent looping...
13035             return;
13036         }
13037         this._initEventsCalled = true;
13038         
13039         if (!this.store) {
13040             throw "can not find store for combo";
13041         }
13042         
13043         this.indicator = this.indicatorEl();
13044         
13045         this.store = Roo.factory(this.store, Roo.data);
13046         this.store.parent = this;
13047         
13048         // if we are building from html. then this element is so complex, that we can not really
13049         // use the rendered HTML.
13050         // so we have to trash and replace the previous code.
13051         if (Roo.XComponent.build_from_html) {
13052             // remove this element....
13053             var e = this.el.dom, k=0;
13054             while (e ) { e = e.previousSibling;  ++k;}
13055
13056             this.el.remove();
13057             
13058             this.el=false;
13059             this.rendered = false;
13060             
13061             this.render(this.parent().getChildContainer(true), k);
13062         }
13063         
13064         if(Roo.isIOS && this.useNativeIOS){
13065             this.initIOSView();
13066             return;
13067         }
13068         
13069         /*
13070          * Touch Devices
13071          */
13072         
13073         if(Roo.isTouch && this.mobileTouchView){
13074             this.initTouchView();
13075             return;
13076         }
13077         
13078         if(this.tickable){
13079             this.initTickableEvents();
13080             return;
13081         }
13082         
13083         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13084         
13085         if(this.hiddenName){
13086             
13087             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13088             
13089             this.hiddenField.dom.value =
13090                 this.hiddenValue !== undefined ? this.hiddenValue :
13091                 this.value !== undefined ? this.value : '';
13092
13093             // prevent input submission
13094             this.el.dom.removeAttribute('name');
13095             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13096              
13097              
13098         }
13099         //if(Roo.isGecko){
13100         //    this.el.dom.setAttribute('autocomplete', 'off');
13101         //}
13102         
13103         var cls = 'x-combo-list';
13104         
13105         //this.list = new Roo.Layer({
13106         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13107         //});
13108         
13109         var _this = this;
13110         
13111         (function(){
13112             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13113             _this.list.setWidth(lw);
13114         }).defer(100);
13115         
13116         this.list.on('mouseover', this.onViewOver, this);
13117         this.list.on('mousemove', this.onViewMove, this);
13118         this.list.on('scroll', this.onViewScroll, this);
13119         
13120         /*
13121         this.list.swallowEvent('mousewheel');
13122         this.assetHeight = 0;
13123
13124         if(this.title){
13125             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13126             this.assetHeight += this.header.getHeight();
13127         }
13128
13129         this.innerList = this.list.createChild({cls:cls+'-inner'});
13130         this.innerList.on('mouseover', this.onViewOver, this);
13131         this.innerList.on('mousemove', this.onViewMove, this);
13132         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13133         
13134         if(this.allowBlank && !this.pageSize && !this.disableClear){
13135             this.footer = this.list.createChild({cls:cls+'-ft'});
13136             this.pageTb = new Roo.Toolbar(this.footer);
13137            
13138         }
13139         if(this.pageSize){
13140             this.footer = this.list.createChild({cls:cls+'-ft'});
13141             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13142                     {pageSize: this.pageSize});
13143             
13144         }
13145         
13146         if (this.pageTb && this.allowBlank && !this.disableClear) {
13147             var _this = this;
13148             this.pageTb.add(new Roo.Toolbar.Fill(), {
13149                 cls: 'x-btn-icon x-btn-clear',
13150                 text: '&#160;',
13151                 handler: function()
13152                 {
13153                     _this.collapse();
13154                     _this.clearValue();
13155                     _this.onSelect(false, -1);
13156                 }
13157             });
13158         }
13159         if (this.footer) {
13160             this.assetHeight += this.footer.getHeight();
13161         }
13162         */
13163             
13164         if(!this.tpl){
13165             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13166         }
13167
13168         this.view = new Roo.View(this.list, this.tpl, {
13169             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13170         });
13171         //this.view.wrapEl.setDisplayed(false);
13172         this.view.on('click', this.onViewClick, this);
13173         
13174         
13175         this.store.on('beforeload', this.onBeforeLoad, this);
13176         this.store.on('load', this.onLoad, this);
13177         this.store.on('loadexception', this.onLoadException, this);
13178         /*
13179         if(this.resizable){
13180             this.resizer = new Roo.Resizable(this.list,  {
13181                pinned:true, handles:'se'
13182             });
13183             this.resizer.on('resize', function(r, w, h){
13184                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13185                 this.listWidth = w;
13186                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13187                 this.restrictHeight();
13188             }, this);
13189             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13190         }
13191         */
13192         if(!this.editable){
13193             this.editable = true;
13194             this.setEditable(false);
13195         }
13196         
13197         /*
13198         
13199         if (typeof(this.events.add.listeners) != 'undefined') {
13200             
13201             this.addicon = this.wrap.createChild(
13202                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13203        
13204             this.addicon.on('click', function(e) {
13205                 this.fireEvent('add', this);
13206             }, this);
13207         }
13208         if (typeof(this.events.edit.listeners) != 'undefined') {
13209             
13210             this.editicon = this.wrap.createChild(
13211                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13212             if (this.addicon) {
13213                 this.editicon.setStyle('margin-left', '40px');
13214             }
13215             this.editicon.on('click', function(e) {
13216                 
13217                 // we fire even  if inothing is selected..
13218                 this.fireEvent('edit', this, this.lastData );
13219                 
13220             }, this);
13221         }
13222         */
13223         
13224         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13225             "up" : function(e){
13226                 this.inKeyMode = true;
13227                 this.selectPrev();
13228             },
13229
13230             "down" : function(e){
13231                 if(!this.isExpanded()){
13232                     this.onTriggerClick();
13233                 }else{
13234                     this.inKeyMode = true;
13235                     this.selectNext();
13236                 }
13237             },
13238
13239             "enter" : function(e){
13240 //                this.onViewClick();
13241                 //return true;
13242                 this.collapse();
13243                 
13244                 if(this.fireEvent("specialkey", this, e)){
13245                     this.onViewClick(false);
13246                 }
13247                 
13248                 return true;
13249             },
13250
13251             "esc" : function(e){
13252                 this.collapse();
13253             },
13254
13255             "tab" : function(e){
13256                 this.collapse();
13257                 
13258                 if(this.fireEvent("specialkey", this, e)){
13259                     this.onViewClick(false);
13260                 }
13261                 
13262                 return true;
13263             },
13264
13265             scope : this,
13266
13267             doRelay : function(foo, bar, hname){
13268                 if(hname == 'down' || this.scope.isExpanded()){
13269                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13270                 }
13271                 return true;
13272             },
13273
13274             forceKeyDown: true
13275         });
13276         
13277         
13278         this.queryDelay = Math.max(this.queryDelay || 10,
13279                 this.mode == 'local' ? 10 : 250);
13280         
13281         
13282         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13283         
13284         if(this.typeAhead){
13285             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13286         }
13287         if(this.editable !== false){
13288             this.inputEl().on("keyup", this.onKeyUp, this);
13289         }
13290         if(this.forceSelection){
13291             this.inputEl().on('blur', this.doForce, this);
13292         }
13293         
13294         if(this.multiple){
13295             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13296             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13297         }
13298     },
13299     
13300     initTickableEvents: function()
13301     {   
13302         this.createList();
13303         
13304         if(this.hiddenName){
13305             
13306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13307             
13308             this.hiddenField.dom.value =
13309                 this.hiddenValue !== undefined ? this.hiddenValue :
13310                 this.value !== undefined ? this.value : '';
13311
13312             // prevent input submission
13313             this.el.dom.removeAttribute('name');
13314             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13315              
13316              
13317         }
13318         
13319 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13320         
13321         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13322         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13323         if(this.triggerList){
13324             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13325         }
13326          
13327         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13328         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13329         
13330         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13331         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13332         
13333         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13334         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13335         
13336         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13337         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13338         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13339         
13340         this.okBtn.hide();
13341         this.cancelBtn.hide();
13342         
13343         var _this = this;
13344         
13345         (function(){
13346             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13347             _this.list.setWidth(lw);
13348         }).defer(100);
13349         
13350         this.list.on('mouseover', this.onViewOver, this);
13351         this.list.on('mousemove', this.onViewMove, this);
13352         
13353         this.list.on('scroll', this.onViewScroll, this);
13354         
13355         if(!this.tpl){
13356             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>';
13357         }
13358
13359         this.view = new Roo.View(this.list, this.tpl, {
13360             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13361         });
13362         
13363         //this.view.wrapEl.setDisplayed(false);
13364         this.view.on('click', this.onViewClick, this);
13365         
13366         
13367         
13368         this.store.on('beforeload', this.onBeforeLoad, this);
13369         this.store.on('load', this.onLoad, this);
13370         this.store.on('loadexception', this.onLoadException, this);
13371         
13372         if(this.editable){
13373             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13374                 "up" : function(e){
13375                     this.inKeyMode = true;
13376                     this.selectPrev();
13377                 },
13378
13379                 "down" : function(e){
13380                     this.inKeyMode = true;
13381                     this.selectNext();
13382                 },
13383
13384                 "enter" : function(e){
13385                     if(this.fireEvent("specialkey", this, e)){
13386                         this.onViewClick(false);
13387                     }
13388                     
13389                     return true;
13390                 },
13391
13392                 "esc" : function(e){
13393                     this.onTickableFooterButtonClick(e, false, false);
13394                 },
13395
13396                 "tab" : function(e){
13397                     this.fireEvent("specialkey", this, e);
13398                     
13399                     this.onTickableFooterButtonClick(e, false, false);
13400                     
13401                     return true;
13402                 },
13403
13404                 scope : this,
13405
13406                 doRelay : function(e, fn, key){
13407                     if(this.scope.isExpanded()){
13408                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13409                     }
13410                     return true;
13411                 },
13412
13413                 forceKeyDown: true
13414             });
13415         }
13416         
13417         this.queryDelay = Math.max(this.queryDelay || 10,
13418                 this.mode == 'local' ? 10 : 250);
13419         
13420         
13421         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13422         
13423         if(this.typeAhead){
13424             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13425         }
13426         
13427         if(this.editable !== false){
13428             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13429         }
13430         
13431         this.indicator = this.indicatorEl();
13432         
13433         if(this.indicator){
13434             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13435             this.indicator.hide();
13436         }
13437         
13438     },
13439
13440     onDestroy : function(){
13441         if(this.view){
13442             this.view.setStore(null);
13443             this.view.el.removeAllListeners();
13444             this.view.el.remove();
13445             this.view.purgeListeners();
13446         }
13447         if(this.list){
13448             this.list.dom.innerHTML  = '';
13449         }
13450         
13451         if(this.store){
13452             this.store.un('beforeload', this.onBeforeLoad, this);
13453             this.store.un('load', this.onLoad, this);
13454             this.store.un('loadexception', this.onLoadException, this);
13455         }
13456         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13457     },
13458
13459     // private
13460     fireKey : function(e){
13461         if(e.isNavKeyPress() && !this.list.isVisible()){
13462             this.fireEvent("specialkey", this, e);
13463         }
13464     },
13465
13466     // private
13467     onResize: function(w, h){
13468 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13469 //        
13470 //        if(typeof w != 'number'){
13471 //            // we do not handle it!?!?
13472 //            return;
13473 //        }
13474 //        var tw = this.trigger.getWidth();
13475 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13476 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13477 //        var x = w - tw;
13478 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13479 //            
13480 //        //this.trigger.setStyle('left', x+'px');
13481 //        
13482 //        if(this.list && this.listWidth === undefined){
13483 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13484 //            this.list.setWidth(lw);
13485 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13486 //        }
13487         
13488     
13489         
13490     },
13491
13492     /**
13493      * Allow or prevent the user from directly editing the field text.  If false is passed,
13494      * the user will only be able to select from the items defined in the dropdown list.  This method
13495      * is the runtime equivalent of setting the 'editable' config option at config time.
13496      * @param {Boolean} value True to allow the user to directly edit the field text
13497      */
13498     setEditable : function(value){
13499         if(value == this.editable){
13500             return;
13501         }
13502         this.editable = value;
13503         if(!value){
13504             this.inputEl().dom.setAttribute('readOnly', true);
13505             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13506             this.inputEl().addClass('x-combo-noedit');
13507         }else{
13508             this.inputEl().dom.setAttribute('readOnly', false);
13509             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13510             this.inputEl().removeClass('x-combo-noedit');
13511         }
13512     },
13513
13514     // private
13515     
13516     onBeforeLoad : function(combo,opts){
13517         if(!this.hasFocus){
13518             return;
13519         }
13520          if (!opts.add) {
13521             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13522          }
13523         this.restrictHeight();
13524         this.selectedIndex = -1;
13525     },
13526
13527     // private
13528     onLoad : function(){
13529         
13530         this.hasQuery = false;
13531         
13532         if(!this.hasFocus){
13533             return;
13534         }
13535         
13536         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13537             this.loading.hide();
13538         }
13539         
13540         if(this.store.getCount() > 0){
13541             
13542             this.expand();
13543             this.restrictHeight();
13544             if(this.lastQuery == this.allQuery){
13545                 if(this.editable && !this.tickable){
13546                     this.inputEl().dom.select();
13547                 }
13548                 
13549                 if(
13550                     !this.selectByValue(this.value, true) &&
13551                     this.autoFocus && 
13552                     (
13553                         !this.store.lastOptions ||
13554                         typeof(this.store.lastOptions.add) == 'undefined' || 
13555                         this.store.lastOptions.add != true
13556                     )
13557                 ){
13558                     this.select(0, true);
13559                 }
13560             }else{
13561                 if(this.autoFocus){
13562                     this.selectNext();
13563                 }
13564                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13565                     this.taTask.delay(this.typeAheadDelay);
13566                 }
13567             }
13568         }else{
13569             this.onEmptyResults();
13570         }
13571         
13572         //this.el.focus();
13573     },
13574     // private
13575     onLoadException : function()
13576     {
13577         this.hasQuery = false;
13578         
13579         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13580             this.loading.hide();
13581         }
13582         
13583         if(this.tickable && this.editable){
13584             return;
13585         }
13586         
13587         this.collapse();
13588         // only causes errors at present
13589         //Roo.log(this.store.reader.jsonData);
13590         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13591             // fixme
13592             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13593         //}
13594         
13595         
13596     },
13597     // private
13598     onTypeAhead : function(){
13599         if(this.store.getCount() > 0){
13600             var r = this.store.getAt(0);
13601             var newValue = r.data[this.displayField];
13602             var len = newValue.length;
13603             var selStart = this.getRawValue().length;
13604             
13605             if(selStart != len){
13606                 this.setRawValue(newValue);
13607                 this.selectText(selStart, newValue.length);
13608             }
13609         }
13610     },
13611
13612     // private
13613     onSelect : function(record, index){
13614         
13615         if(this.fireEvent('beforeselect', this, record, index) !== false){
13616         
13617             this.setFromData(index > -1 ? record.data : false);
13618             
13619             this.collapse();
13620             this.fireEvent('select', this, record, index);
13621         }
13622     },
13623
13624     /**
13625      * Returns the currently selected field value or empty string if no value is set.
13626      * @return {String} value The selected value
13627      */
13628     getValue : function()
13629     {
13630         if(Roo.isIOS && this.useNativeIOS){
13631             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13632         }
13633         
13634         if(this.multiple){
13635             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13636         }
13637         
13638         if(this.valueField){
13639             return typeof this.value != 'undefined' ? this.value : '';
13640         }else{
13641             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13642         }
13643     },
13644     
13645     getRawValue : function()
13646     {
13647         if(Roo.isIOS && this.useNativeIOS){
13648             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13649         }
13650         
13651         var v = this.inputEl().getValue();
13652         
13653         return v;
13654     },
13655
13656     /**
13657      * Clears any text/value currently set in the field
13658      */
13659     clearValue : function(){
13660         
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = '';
13663         }
13664         this.value = '';
13665         this.setRawValue('');
13666         this.lastSelectionText = '';
13667         this.lastData = false;
13668         
13669         var close = this.closeTriggerEl();
13670         
13671         if(close){
13672             close.hide();
13673         }
13674         
13675         this.validate();
13676         
13677     },
13678
13679     /**
13680      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13681      * will be displayed in the field.  If the value does not match the data value of an existing item,
13682      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13683      * Otherwise the field will be blank (although the value will still be set).
13684      * @param {String} value The value to match
13685      */
13686     setValue : function(v)
13687     {
13688         if(Roo.isIOS && this.useNativeIOS){
13689             this.setIOSValue(v);
13690             return;
13691         }
13692         
13693         if(this.multiple){
13694             this.syncValue();
13695             return;
13696         }
13697         
13698         var text = v;
13699         if(this.valueField){
13700             var r = this.findRecord(this.valueField, v);
13701             if(r){
13702                 text = r.data[this.displayField];
13703             }else if(this.valueNotFoundText !== undefined){
13704                 text = this.valueNotFoundText;
13705             }
13706         }
13707         this.lastSelectionText = text;
13708         if(this.hiddenField){
13709             this.hiddenField.dom.value = v;
13710         }
13711         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13712         this.value = v;
13713         
13714         var close = this.closeTriggerEl();
13715         
13716         if(close){
13717             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13718         }
13719         
13720         this.validate();
13721     },
13722     /**
13723      * @property {Object} the last set data for the element
13724      */
13725     
13726     lastData : false,
13727     /**
13728      * Sets the value of the field based on a object which is related to the record format for the store.
13729      * @param {Object} value the value to set as. or false on reset?
13730      */
13731     setFromData : function(o){
13732         
13733         Roo.log(o);
13734         
13735         if(this.multiple){
13736             this.addItem(o);
13737             return;
13738         }
13739             
13740         var dv = ''; // display value
13741         var vv = ''; // value value..
13742         this.lastData = o;
13743         if (this.displayField) {
13744             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13745         } else {
13746             // this is an error condition!!!
13747             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13748         }
13749         
13750         if(this.valueField){
13751             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13752         }
13753         
13754         var close = this.closeTriggerEl();
13755         
13756         if(close){
13757             if(dv.length || vv * 1 > 0){
13758                 close.show() ;
13759                 this.blockFocus=true;
13760             } else {
13761                 close.hide();
13762             }             
13763         }
13764         
13765         if(this.hiddenField){
13766             this.hiddenField.dom.value = vv;
13767             
13768             this.lastSelectionText = dv;
13769             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13770             this.value = vv;
13771             return;
13772         }
13773         // no hidden field.. - we store the value in 'value', but still display
13774         // display field!!!!
13775         this.lastSelectionText = dv;
13776         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13777         this.value = vv;
13778         
13779         
13780         
13781     },
13782     // private
13783     reset : function(){
13784         // overridden so that last data is reset..
13785         
13786         if(this.multiple){
13787             this.clearItem();
13788             return;
13789         }
13790         
13791         this.setValue(this.originalValue);
13792         //this.clearInvalid();
13793         this.lastData = false;
13794         if (this.view) {
13795             this.view.clearSelections();
13796         }
13797         
13798         this.validate();
13799     },
13800     // private
13801     findRecord : function(prop, value){
13802         var record;
13803         if(this.store.getCount() > 0){
13804             this.store.each(function(r){
13805                 if(r.data[prop] == value){
13806                     record = r;
13807                     return false;
13808                 }
13809                 return true;
13810             });
13811         }
13812         return record;
13813     },
13814     
13815     getName: function()
13816     {
13817         // returns hidden if it's set..
13818         if (!this.rendered) {return ''};
13819         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13820         
13821     },
13822     // private
13823     onViewMove : function(e, t){
13824         this.inKeyMode = false;
13825     },
13826
13827     // private
13828     onViewOver : function(e, t){
13829         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13830             return;
13831         }
13832         var item = this.view.findItemFromChild(t);
13833         
13834         if(item){
13835             var index = this.view.indexOf(item);
13836             this.select(index, false);
13837         }
13838     },
13839
13840     // private
13841     onViewClick : function(view, doFocus, el, e)
13842     {
13843         var index = this.view.getSelectedIndexes()[0];
13844         
13845         var r = this.store.getAt(index);
13846         
13847         if(this.tickable){
13848             
13849             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13850                 return;
13851             }
13852             
13853             var rm = false;
13854             var _this = this;
13855             
13856             Roo.each(this.tickItems, function(v,k){
13857                 
13858                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13859                     Roo.log(v);
13860                     _this.tickItems.splice(k, 1);
13861                     
13862                     if(typeof(e) == 'undefined' && view == false){
13863                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13864                     }
13865                     
13866                     rm = true;
13867                     return;
13868                 }
13869             });
13870             
13871             if(rm){
13872                 return;
13873             }
13874             
13875             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13876                 this.tickItems.push(r.data);
13877             }
13878             
13879             if(typeof(e) == 'undefined' && view == false){
13880                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13881             }
13882                     
13883             return;
13884         }
13885         
13886         if(r){
13887             this.onSelect(r, index);
13888         }
13889         if(doFocus !== false && !this.blockFocus){
13890             this.inputEl().focus();
13891         }
13892     },
13893
13894     // private
13895     restrictHeight : function(){
13896         //this.innerList.dom.style.height = '';
13897         //var inner = this.innerList.dom;
13898         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13899         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13900         //this.list.beginUpdate();
13901         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13902         this.list.alignTo(this.inputEl(), this.listAlign);
13903         this.list.alignTo(this.inputEl(), this.listAlign);
13904         //this.list.endUpdate();
13905     },
13906
13907     // private
13908     onEmptyResults : function(){
13909         
13910         if(this.tickable && this.editable){
13911             this.restrictHeight();
13912             return;
13913         }
13914         
13915         this.collapse();
13916     },
13917
13918     /**
13919      * Returns true if the dropdown list is expanded, else false.
13920      */
13921     isExpanded : function(){
13922         return this.list.isVisible();
13923     },
13924
13925     /**
13926      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13927      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13928      * @param {String} value The data value of the item to select
13929      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13930      * selected item if it is not currently in view (defaults to true)
13931      * @return {Boolean} True if the value matched an item in the list, else false
13932      */
13933     selectByValue : function(v, scrollIntoView){
13934         if(v !== undefined && v !== null){
13935             var r = this.findRecord(this.valueField || this.displayField, v);
13936             if(r){
13937                 this.select(this.store.indexOf(r), scrollIntoView);
13938                 return true;
13939             }
13940         }
13941         return false;
13942     },
13943
13944     /**
13945      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13946      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13947      * @param {Number} index The zero-based index of the list item to select
13948      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13949      * selected item if it is not currently in view (defaults to true)
13950      */
13951     select : function(index, scrollIntoView){
13952         this.selectedIndex = index;
13953         this.view.select(index);
13954         if(scrollIntoView !== false){
13955             var el = this.view.getNode(index);
13956             /*
13957              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13958              */
13959             if(el){
13960                 this.list.scrollChildIntoView(el, false);
13961             }
13962         }
13963     },
13964
13965     // private
13966     selectNext : function(){
13967         var ct = this.store.getCount();
13968         if(ct > 0){
13969             if(this.selectedIndex == -1){
13970                 this.select(0);
13971             }else if(this.selectedIndex < ct-1){
13972                 this.select(this.selectedIndex+1);
13973             }
13974         }
13975     },
13976
13977     // private
13978     selectPrev : function(){
13979         var ct = this.store.getCount();
13980         if(ct > 0){
13981             if(this.selectedIndex == -1){
13982                 this.select(0);
13983             }else if(this.selectedIndex != 0){
13984                 this.select(this.selectedIndex-1);
13985             }
13986         }
13987     },
13988
13989     // private
13990     onKeyUp : function(e){
13991         if(this.editable !== false && !e.isSpecialKey()){
13992             this.lastKey = e.getKey();
13993             this.dqTask.delay(this.queryDelay);
13994         }
13995     },
13996
13997     // private
13998     validateBlur : function(){
13999         return !this.list || !this.list.isVisible();   
14000     },
14001
14002     // private
14003     initQuery : function(){
14004         
14005         var v = this.getRawValue();
14006         
14007         if(this.tickable && this.editable){
14008             v = this.tickableInputEl().getValue();
14009         }
14010         
14011         this.doQuery(v);
14012     },
14013
14014     // private
14015     doForce : function(){
14016         if(this.inputEl().dom.value.length > 0){
14017             this.inputEl().dom.value =
14018                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14019              
14020         }
14021     },
14022
14023     /**
14024      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14025      * query allowing the query action to be canceled if needed.
14026      * @param {String} query The SQL query to execute
14027      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14028      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14029      * saved in the current store (defaults to false)
14030      */
14031     doQuery : function(q, forceAll){
14032         
14033         if(q === undefined || q === null){
14034             q = '';
14035         }
14036         var qe = {
14037             query: q,
14038             forceAll: forceAll,
14039             combo: this,
14040             cancel:false
14041         };
14042         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14043             return false;
14044         }
14045         q = qe.query;
14046         
14047         forceAll = qe.forceAll;
14048         if(forceAll === true || (q.length >= this.minChars)){
14049             
14050             this.hasQuery = true;
14051             
14052             if(this.lastQuery != q || this.alwaysQuery){
14053                 this.lastQuery = q;
14054                 if(this.mode == 'local'){
14055                     this.selectedIndex = -1;
14056                     if(forceAll){
14057                         this.store.clearFilter();
14058                     }else{
14059                         
14060                         if(this.specialFilter){
14061                             this.fireEvent('specialfilter', this);
14062                             this.onLoad();
14063                             return;
14064                         }
14065                         
14066                         this.store.filter(this.displayField, q);
14067                     }
14068                     
14069                     this.store.fireEvent("datachanged", this.store);
14070                     
14071                     this.onLoad();
14072                     
14073                     
14074                 }else{
14075                     
14076                     this.store.baseParams[this.queryParam] = q;
14077                     
14078                     var options = {params : this.getParams(q)};
14079                     
14080                     if(this.loadNext){
14081                         options.add = true;
14082                         options.params.start = this.page * this.pageSize;
14083                     }
14084                     
14085                     this.store.load(options);
14086                     
14087                     /*
14088                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14089                      *  we should expand the list on onLoad
14090                      *  so command out it
14091                      */
14092 //                    this.expand();
14093                 }
14094             }else{
14095                 this.selectedIndex = -1;
14096                 this.onLoad();   
14097             }
14098         }
14099         
14100         this.loadNext = false;
14101     },
14102     
14103     // private
14104     getParams : function(q){
14105         var p = {};
14106         //p[this.queryParam] = q;
14107         
14108         if(this.pageSize){
14109             p.start = 0;
14110             p.limit = this.pageSize;
14111         }
14112         return p;
14113     },
14114
14115     /**
14116      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14117      */
14118     collapse : function(){
14119         if(!this.isExpanded()){
14120             return;
14121         }
14122         
14123         this.list.hide();
14124         
14125         this.hasFocus = false;
14126         
14127         if(this.tickable){
14128             this.okBtn.hide();
14129             this.cancelBtn.hide();
14130             this.trigger.show();
14131             
14132             if(this.editable){
14133                 this.tickableInputEl().dom.value = '';
14134                 this.tickableInputEl().blur();
14135             }
14136             
14137         }
14138         
14139         Roo.get(document).un('mousedown', this.collapseIf, this);
14140         Roo.get(document).un('mousewheel', this.collapseIf, this);
14141         if (!this.editable) {
14142             Roo.get(document).un('keydown', this.listKeyPress, this);
14143         }
14144         this.fireEvent('collapse', this);
14145         
14146         this.validate();
14147     },
14148
14149     // private
14150     collapseIf : function(e){
14151         var in_combo  = e.within(this.el);
14152         var in_list =  e.within(this.list);
14153         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14154         
14155         if (in_combo || in_list || is_list) {
14156             //e.stopPropagation();
14157             return;
14158         }
14159         
14160         if(this.tickable){
14161             this.onTickableFooterButtonClick(e, false, false);
14162         }
14163
14164         this.collapse();
14165         
14166     },
14167
14168     /**
14169      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14170      */
14171     expand : function(){
14172        
14173         if(this.isExpanded() || !this.hasFocus){
14174             return;
14175         }
14176         
14177         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14178         this.list.setWidth(lw);
14179         
14180         Roo.log('expand');
14181         
14182         this.list.show();
14183         
14184         this.restrictHeight();
14185         
14186         if(this.tickable){
14187             
14188             this.tickItems = Roo.apply([], this.item);
14189             
14190             this.okBtn.show();
14191             this.cancelBtn.show();
14192             this.trigger.hide();
14193             
14194             if(this.editable){
14195                 this.tickableInputEl().focus();
14196             }
14197             
14198         }
14199         
14200         Roo.get(document).on('mousedown', this.collapseIf, this);
14201         Roo.get(document).on('mousewheel', this.collapseIf, this);
14202         if (!this.editable) {
14203             Roo.get(document).on('keydown', this.listKeyPress, this);
14204         }
14205         
14206         this.fireEvent('expand', this);
14207     },
14208
14209     // private
14210     // Implements the default empty TriggerField.onTriggerClick function
14211     onTriggerClick : function(e)
14212     {
14213         Roo.log('trigger click');
14214         
14215         if(this.disabled || !this.triggerList){
14216             return;
14217         }
14218         
14219         this.page = 0;
14220         this.loadNext = false;
14221         
14222         if(this.isExpanded()){
14223             this.collapse();
14224             if (!this.blockFocus) {
14225                 this.inputEl().focus();
14226             }
14227             
14228         }else {
14229             this.hasFocus = true;
14230             if(this.triggerAction == 'all') {
14231                 this.doQuery(this.allQuery, true);
14232             } else {
14233                 this.doQuery(this.getRawValue());
14234             }
14235             if (!this.blockFocus) {
14236                 this.inputEl().focus();
14237             }
14238         }
14239     },
14240     
14241     onTickableTriggerClick : function(e)
14242     {
14243         if(this.disabled){
14244             return;
14245         }
14246         
14247         this.page = 0;
14248         this.loadNext = false;
14249         this.hasFocus = true;
14250         
14251         if(this.triggerAction == 'all') {
14252             this.doQuery(this.allQuery, true);
14253         } else {
14254             this.doQuery(this.getRawValue());
14255         }
14256     },
14257     
14258     onSearchFieldClick : function(e)
14259     {
14260         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14261             this.onTickableFooterButtonClick(e, false, false);
14262             return;
14263         }
14264         
14265         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14266             return;
14267         }
14268         
14269         this.page = 0;
14270         this.loadNext = false;
14271         this.hasFocus = true;
14272         
14273         if(this.triggerAction == 'all') {
14274             this.doQuery(this.allQuery, true);
14275         } else {
14276             this.doQuery(this.getRawValue());
14277         }
14278     },
14279     
14280     listKeyPress : function(e)
14281     {
14282         //Roo.log('listkeypress');
14283         // scroll to first matching element based on key pres..
14284         if (e.isSpecialKey()) {
14285             return false;
14286         }
14287         var k = String.fromCharCode(e.getKey()).toUpperCase();
14288         //Roo.log(k);
14289         var match  = false;
14290         var csel = this.view.getSelectedNodes();
14291         var cselitem = false;
14292         if (csel.length) {
14293             var ix = this.view.indexOf(csel[0]);
14294             cselitem  = this.store.getAt(ix);
14295             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14296                 cselitem = false;
14297             }
14298             
14299         }
14300         
14301         this.store.each(function(v) { 
14302             if (cselitem) {
14303                 // start at existing selection.
14304                 if (cselitem.id == v.id) {
14305                     cselitem = false;
14306                 }
14307                 return true;
14308             }
14309                 
14310             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14311                 match = this.store.indexOf(v);
14312                 return false;
14313             }
14314             return true;
14315         }, this);
14316         
14317         if (match === false) {
14318             return true; // no more action?
14319         }
14320         // scroll to?
14321         this.view.select(match);
14322         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14323         sn.scrollIntoView(sn.dom.parentNode, false);
14324     },
14325     
14326     onViewScroll : function(e, t){
14327         
14328         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){
14329             return;
14330         }
14331         
14332         this.hasQuery = true;
14333         
14334         this.loading = this.list.select('.loading', true).first();
14335         
14336         if(this.loading === null){
14337             this.list.createChild({
14338                 tag: 'div',
14339                 cls: 'loading roo-select2-more-results roo-select2-active',
14340                 html: 'Loading more results...'
14341             });
14342             
14343             this.loading = this.list.select('.loading', true).first();
14344             
14345             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14346             
14347             this.loading.hide();
14348         }
14349         
14350         this.loading.show();
14351         
14352         var _combo = this;
14353         
14354         this.page++;
14355         this.loadNext = true;
14356         
14357         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14358         
14359         return;
14360     },
14361     
14362     addItem : function(o)
14363     {   
14364         var dv = ''; // display value
14365         
14366         if (this.displayField) {
14367             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14368         } else {
14369             // this is an error condition!!!
14370             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14371         }
14372         
14373         if(!dv.length){
14374             return;
14375         }
14376         
14377         var choice = this.choices.createChild({
14378             tag: 'li',
14379             cls: 'roo-select2-search-choice',
14380             cn: [
14381                 {
14382                     tag: 'div',
14383                     html: dv
14384                 },
14385                 {
14386                     tag: 'a',
14387                     href: '#',
14388                     cls: 'roo-select2-search-choice-close fa fa-times',
14389                     tabindex: '-1'
14390                 }
14391             ]
14392             
14393         }, this.searchField);
14394         
14395         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14396         
14397         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14398         
14399         this.item.push(o);
14400         
14401         this.lastData = o;
14402         
14403         this.syncValue();
14404         
14405         this.inputEl().dom.value = '';
14406         
14407         this.validate();
14408     },
14409     
14410     onRemoveItem : function(e, _self, o)
14411     {
14412         e.preventDefault();
14413         
14414         this.lastItem = Roo.apply([], this.item);
14415         
14416         var index = this.item.indexOf(o.data) * 1;
14417         
14418         if( index < 0){
14419             Roo.log('not this item?!');
14420             return;
14421         }
14422         
14423         this.item.splice(index, 1);
14424         o.item.remove();
14425         
14426         this.syncValue();
14427         
14428         this.fireEvent('remove', this, e);
14429         
14430         this.validate();
14431         
14432     },
14433     
14434     syncValue : function()
14435     {
14436         if(!this.item.length){
14437             this.clearValue();
14438             return;
14439         }
14440             
14441         var value = [];
14442         var _this = this;
14443         Roo.each(this.item, function(i){
14444             if(_this.valueField){
14445                 value.push(i[_this.valueField]);
14446                 return;
14447             }
14448
14449             value.push(i);
14450         });
14451
14452         this.value = value.join(',');
14453
14454         if(this.hiddenField){
14455             this.hiddenField.dom.value = this.value;
14456         }
14457         
14458         this.store.fireEvent("datachanged", this.store);
14459         
14460         this.validate();
14461     },
14462     
14463     clearItem : function()
14464     {
14465         if(!this.multiple){
14466             return;
14467         }
14468         
14469         this.item = [];
14470         
14471         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14472            c.remove();
14473         });
14474         
14475         this.syncValue();
14476         
14477         this.validate();
14478         
14479         if(this.tickable && !Roo.isTouch){
14480             this.view.refresh();
14481         }
14482     },
14483     
14484     inputEl: function ()
14485     {
14486         if(Roo.isIOS && this.useNativeIOS){
14487             return this.el.select('select.roo-ios-select', true).first();
14488         }
14489         
14490         if(Roo.isTouch && this.mobileTouchView){
14491             return this.el.select('input.form-control',true).first();
14492         }
14493         
14494         if(this.tickable){
14495             return this.searchField;
14496         }
14497         
14498         return this.el.select('input.form-control',true).first();
14499     },
14500     
14501     onTickableFooterButtonClick : function(e, btn, el)
14502     {
14503         e.preventDefault();
14504         
14505         this.lastItem = Roo.apply([], this.item);
14506         
14507         if(btn && btn.name == 'cancel'){
14508             this.tickItems = Roo.apply([], this.item);
14509             this.collapse();
14510             return;
14511         }
14512         
14513         this.clearItem();
14514         
14515         var _this = this;
14516         
14517         Roo.each(this.tickItems, function(o){
14518             _this.addItem(o);
14519         });
14520         
14521         this.collapse();
14522         
14523     },
14524     
14525     validate : function()
14526     {
14527         var v = this.getRawValue();
14528         
14529         if(this.multiple){
14530             v = this.getValue();
14531         }
14532         
14533         if(this.disabled || this.allowBlank || v.length){
14534             this.markValid();
14535             return true;
14536         }
14537         
14538         this.markInvalid();
14539         return false;
14540     },
14541     
14542     tickableInputEl : function()
14543     {
14544         if(!this.tickable || !this.editable){
14545             return this.inputEl();
14546         }
14547         
14548         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14549     },
14550     
14551     
14552     getAutoCreateTouchView : function()
14553     {
14554         var id = Roo.id();
14555         
14556         var cfg = {
14557             cls: 'form-group' //input-group
14558         };
14559         
14560         var input =  {
14561             tag: 'input',
14562             id : id,
14563             type : this.inputType,
14564             cls : 'form-control x-combo-noedit',
14565             autocomplete: 'new-password',
14566             placeholder : this.placeholder || '',
14567             readonly : true
14568         };
14569         
14570         if (this.name) {
14571             input.name = this.name;
14572         }
14573         
14574         if (this.size) {
14575             input.cls += ' input-' + this.size;
14576         }
14577         
14578         if (this.disabled) {
14579             input.disabled = true;
14580         }
14581         
14582         var inputblock = {
14583             cls : '',
14584             cn : [
14585                 input
14586             ]
14587         };
14588         
14589         if(this.before){
14590             inputblock.cls += ' input-group';
14591             
14592             inputblock.cn.unshift({
14593                 tag :'span',
14594                 cls : 'input-group-addon',
14595                 html : this.before
14596             });
14597         }
14598         
14599         if(this.removable && !this.multiple){
14600             inputblock.cls += ' roo-removable';
14601             
14602             inputblock.cn.push({
14603                 tag: 'button',
14604                 html : 'x',
14605                 cls : 'roo-combo-removable-btn close'
14606             });
14607         }
14608
14609         if(this.hasFeedback && !this.allowBlank){
14610             
14611             inputblock.cls += ' has-feedback';
14612             
14613             inputblock.cn.push({
14614                 tag: 'span',
14615                 cls: 'glyphicon form-control-feedback'
14616             });
14617             
14618         }
14619         
14620         if (this.after) {
14621             
14622             inputblock.cls += (this.before) ? '' : ' input-group';
14623             
14624             inputblock.cn.push({
14625                 tag :'span',
14626                 cls : 'input-group-addon',
14627                 html : this.after
14628             });
14629         }
14630
14631         var box = {
14632             tag: 'div',
14633             cn: [
14634                 {
14635                     tag: 'input',
14636                     type : 'hidden',
14637                     cls: 'form-hidden-field'
14638                 },
14639                 inputblock
14640             ]
14641             
14642         };
14643         
14644         if(this.multiple){
14645             box = {
14646                 tag: 'div',
14647                 cn: [
14648                     {
14649                         tag: 'input',
14650                         type : 'hidden',
14651                         cls: 'form-hidden-field'
14652                     },
14653                     {
14654                         tag: 'ul',
14655                         cls: 'roo-select2-choices',
14656                         cn:[
14657                             {
14658                                 tag: 'li',
14659                                 cls: 'roo-select2-search-field',
14660                                 cn: [
14661
14662                                     inputblock
14663                                 ]
14664                             }
14665                         ]
14666                     }
14667                 ]
14668             }
14669         };
14670         
14671         var combobox = {
14672             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14673             cn: [
14674                 box
14675             ]
14676         };
14677         
14678         if(!this.multiple && this.showToggleBtn){
14679             
14680             var caret = {
14681                         tag: 'span',
14682                         cls: 'caret'
14683             };
14684             
14685             if (this.caret != false) {
14686                 caret = {
14687                      tag: 'i',
14688                      cls: 'fa fa-' + this.caret
14689                 };
14690                 
14691             }
14692             
14693             combobox.cn.push({
14694                 tag :'span',
14695                 cls : 'input-group-addon btn dropdown-toggle',
14696                 cn : [
14697                     caret,
14698                     {
14699                         tag: 'span',
14700                         cls: 'combobox-clear',
14701                         cn  : [
14702                             {
14703                                 tag : 'i',
14704                                 cls: 'icon-remove'
14705                             }
14706                         ]
14707                     }
14708                 ]
14709
14710             })
14711         }
14712         
14713         if(this.multiple){
14714             combobox.cls += ' roo-select2-container-multi';
14715         }
14716         
14717         var align = this.labelAlign || this.parentLabelAlign();
14718         
14719         if (align ==='left' && this.fieldLabel.length) {
14720
14721             cfg.cn = [
14722                 {
14723                    tag : 'i',
14724                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14725                    tooltip : 'This field is required'
14726                 },
14727                 {
14728                     tag: 'label',
14729                     cls : 'control-label',
14730                     html : this.fieldLabel
14731
14732                 },
14733                 {
14734                     cls : '', 
14735                     cn: [
14736                         combobox
14737                     ]
14738                 }
14739             ];
14740             
14741             var labelCfg = cfg.cn[1];
14742             var contentCfg = cfg.cn[2];
14743             
14744
14745             if(this.indicatorpos == 'right'){
14746                 cfg.cn = [
14747                     {
14748                         tag: 'label',
14749                         'for' :  id,
14750                         cls : 'control-label',
14751                         cn : [
14752                             {
14753                                 tag : 'span',
14754                                 html : this.fieldLabel
14755                             },
14756                             {
14757                                 tag : 'i',
14758                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14759                                 tooltip : 'This field is required'
14760                             }
14761                         ]
14762                     },
14763                     {
14764                         cls : "",
14765                         cn: [
14766                             combobox
14767                         ]
14768                     }
14769
14770                 ];
14771                 
14772                 labelCfg = cfg.cn[0];
14773                 contentCfg = cfg.cn[1];
14774             }
14775             
14776            
14777             
14778             if(this.labelWidth > 12){
14779                 labelCfg.style = "width: " + this.labelWidth + 'px';
14780             }
14781             
14782             if(this.labelWidth < 13 && this.labelmd == 0){
14783                 this.labelmd = this.labelWidth;
14784             }
14785             
14786             if(this.labellg > 0){
14787                 labelCfg.cls += ' col-lg-' + this.labellg;
14788                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14789             }
14790             
14791             if(this.labelmd > 0){
14792                 labelCfg.cls += ' col-md-' + this.labelmd;
14793                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14794             }
14795             
14796             if(this.labelsm > 0){
14797                 labelCfg.cls += ' col-sm-' + this.labelsm;
14798                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14799             }
14800             
14801             if(this.labelxs > 0){
14802                 labelCfg.cls += ' col-xs-' + this.labelxs;
14803                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14804             }
14805                 
14806                 
14807         } else if ( this.fieldLabel.length) {
14808             cfg.cn = [
14809                 {
14810                    tag : 'i',
14811                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14812                    tooltip : 'This field is required'
14813                 },
14814                 {
14815                     tag: 'label',
14816                     cls : 'control-label',
14817                     html : this.fieldLabel
14818
14819                 },
14820                 {
14821                     cls : '', 
14822                     cn: [
14823                         combobox
14824                     ]
14825                 }
14826             ];
14827             
14828             if(this.indicatorpos == 'right'){
14829                 cfg.cn = [
14830                     {
14831                         tag: 'label',
14832                         cls : 'control-label',
14833                         html : this.fieldLabel,
14834                         cn : [
14835                             {
14836                                tag : 'i',
14837                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14838                                tooltip : 'This field is required'
14839                             }
14840                         ]
14841                     },
14842                     {
14843                         cls : '', 
14844                         cn: [
14845                             combobox
14846                         ]
14847                     }
14848                 ];
14849             }
14850         } else {
14851             cfg.cn = combobox;    
14852         }
14853         
14854         
14855         var settings = this;
14856         
14857         ['xs','sm','md','lg'].map(function(size){
14858             if (settings[size]) {
14859                 cfg.cls += ' col-' + size + '-' + settings[size];
14860             }
14861         });
14862         
14863         return cfg;
14864     },
14865     
14866     initTouchView : function()
14867     {
14868         this.renderTouchView();
14869         
14870         this.touchViewEl.on('scroll', function(){
14871             this.el.dom.scrollTop = 0;
14872         }, this);
14873         
14874         this.originalValue = this.getValue();
14875         
14876         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14877         
14878         this.inputEl().on("click", this.showTouchView, this);
14879         if (this.triggerEl) {
14880             this.triggerEl.on("click", this.showTouchView, this);
14881         }
14882         
14883         
14884         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14885         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14886         
14887         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14888         
14889         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14890         this.store.on('load', this.onTouchViewLoad, this);
14891         this.store.on('loadexception', this.onTouchViewLoadException, this);
14892         
14893         if(this.hiddenName){
14894             
14895             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14896             
14897             this.hiddenField.dom.value =
14898                 this.hiddenValue !== undefined ? this.hiddenValue :
14899                 this.value !== undefined ? this.value : '';
14900         
14901             this.el.dom.removeAttribute('name');
14902             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14903         }
14904         
14905         if(this.multiple){
14906             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14907             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14908         }
14909         
14910         if(this.removable && !this.multiple){
14911             var close = this.closeTriggerEl();
14912             if(close){
14913                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14914                 close.on('click', this.removeBtnClick, this, close);
14915             }
14916         }
14917         /*
14918          * fix the bug in Safari iOS8
14919          */
14920         this.inputEl().on("focus", function(e){
14921             document.activeElement.blur();
14922         }, this);
14923         
14924         return;
14925         
14926         
14927     },
14928     
14929     renderTouchView : function()
14930     {
14931         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14932         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14933         
14934         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14935         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14936         
14937         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14938         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14939         this.touchViewBodyEl.setStyle('overflow', 'auto');
14940         
14941         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14942         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14943         
14944         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14945         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14946         
14947     },
14948     
14949     showTouchView : function()
14950     {
14951         if(this.disabled){
14952             return;
14953         }
14954         
14955         this.touchViewHeaderEl.hide();
14956
14957         if(this.modalTitle.length){
14958             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14959             this.touchViewHeaderEl.show();
14960         }
14961
14962         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14963         this.touchViewEl.show();
14964
14965         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14966         
14967         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14968         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14969
14970         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14971
14972         if(this.modalTitle.length){
14973             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14974         }
14975         
14976         this.touchViewBodyEl.setHeight(bodyHeight);
14977
14978         if(this.animate){
14979             var _this = this;
14980             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14981         }else{
14982             this.touchViewEl.addClass('in');
14983         }
14984
14985         this.doTouchViewQuery();
14986         
14987     },
14988     
14989     hideTouchView : function()
14990     {
14991         this.touchViewEl.removeClass('in');
14992
14993         if(this.animate){
14994             var _this = this;
14995             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14996         }else{
14997             this.touchViewEl.setStyle('display', 'none');
14998         }
14999         
15000     },
15001     
15002     setTouchViewValue : function()
15003     {
15004         if(this.multiple){
15005             this.clearItem();
15006         
15007             var _this = this;
15008
15009             Roo.each(this.tickItems, function(o){
15010                 this.addItem(o);
15011             }, this);
15012         }
15013         
15014         this.hideTouchView();
15015     },
15016     
15017     doTouchViewQuery : function()
15018     {
15019         var qe = {
15020             query: '',
15021             forceAll: true,
15022             combo: this,
15023             cancel:false
15024         };
15025         
15026         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15027             return false;
15028         }
15029         
15030         if(!this.alwaysQuery || this.mode == 'local'){
15031             this.onTouchViewLoad();
15032             return;
15033         }
15034         
15035         this.store.load();
15036     },
15037     
15038     onTouchViewBeforeLoad : function(combo,opts)
15039     {
15040         return;
15041     },
15042
15043     // private
15044     onTouchViewLoad : function()
15045     {
15046         if(this.store.getCount() < 1){
15047             this.onTouchViewEmptyResults();
15048             return;
15049         }
15050         
15051         this.clearTouchView();
15052         
15053         var rawValue = this.getRawValue();
15054         
15055         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15056         
15057         this.tickItems = [];
15058         
15059         this.store.data.each(function(d, rowIndex){
15060             var row = this.touchViewListGroup.createChild(template);
15061             
15062             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15063                 row.addClass(d.data.cls);
15064             }
15065             
15066             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15067                 var cfg = {
15068                     data : d.data,
15069                     html : d.data[this.displayField]
15070                 };
15071                 
15072                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15073                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15074                 }
15075             }
15076             row.removeClass('selected');
15077             if(!this.multiple && this.valueField &&
15078                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15079             {
15080                 // radio buttons..
15081                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15082                 row.addClass('selected');
15083             }
15084             
15085             if(this.multiple && this.valueField &&
15086                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15087             {
15088                 
15089                 // checkboxes...
15090                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15091                 this.tickItems.push(d.data);
15092             }
15093             
15094             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15095             
15096         }, this);
15097         
15098         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15099         
15100         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15101
15102         if(this.modalTitle.length){
15103             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15104         }
15105
15106         var listHeight = this.touchViewListGroup.getHeight();
15107         
15108         var _this = this;
15109         
15110         if(firstChecked && listHeight > bodyHeight){
15111             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15112         }
15113         
15114     },
15115     
15116     onTouchViewLoadException : function()
15117     {
15118         this.hideTouchView();
15119     },
15120     
15121     onTouchViewEmptyResults : function()
15122     {
15123         this.clearTouchView();
15124         
15125         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15126         
15127         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15128         
15129     },
15130     
15131     clearTouchView : function()
15132     {
15133         this.touchViewListGroup.dom.innerHTML = '';
15134     },
15135     
15136     onTouchViewClick : function(e, el, o)
15137     {
15138         e.preventDefault();
15139         
15140         var row = o.row;
15141         var rowIndex = o.rowIndex;
15142         
15143         var r = this.store.getAt(rowIndex);
15144         
15145         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15146             
15147             if(!this.multiple){
15148                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15149                     c.dom.removeAttribute('checked');
15150                 }, this);
15151
15152                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15153
15154                 this.setFromData(r.data);
15155
15156                 var close = this.closeTriggerEl();
15157
15158                 if(close){
15159                     close.show();
15160                 }
15161
15162                 this.hideTouchView();
15163
15164                 this.fireEvent('select', this, r, rowIndex);
15165
15166                 return;
15167             }
15168
15169             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15170                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15171                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15172                 return;
15173             }
15174
15175             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15176             this.addItem(r.data);
15177             this.tickItems.push(r.data);
15178         }
15179     },
15180     
15181     getAutoCreateNativeIOS : function()
15182     {
15183         var cfg = {
15184             cls: 'form-group' //input-group,
15185         };
15186         
15187         var combobox =  {
15188             tag: 'select',
15189             cls : 'roo-ios-select'
15190         };
15191         
15192         if (this.name) {
15193             combobox.name = this.name;
15194         }
15195         
15196         if (this.disabled) {
15197             combobox.disabled = true;
15198         }
15199         
15200         var settings = this;
15201         
15202         ['xs','sm','md','lg'].map(function(size){
15203             if (settings[size]) {
15204                 cfg.cls += ' col-' + size + '-' + settings[size];
15205             }
15206         });
15207         
15208         cfg.cn = combobox;
15209         
15210         return cfg;
15211         
15212     },
15213     
15214     initIOSView : function()
15215     {
15216         this.store.on('load', this.onIOSViewLoad, this);
15217         
15218         return;
15219     },
15220     
15221     onIOSViewLoad : function()
15222     {
15223         if(this.store.getCount() < 1){
15224             return;
15225         }
15226         
15227         this.clearIOSView();
15228         
15229         if(this.allowBlank) {
15230             
15231             var default_text = '-- SELECT --';
15232             
15233             var opt = this.inputEl().createChild({
15234                 tag: 'option',
15235                 value : 0,
15236                 html : default_text
15237             });
15238             
15239             var o = {};
15240             o[this.valueField] = 0;
15241             o[this.displayField] = default_text;
15242             
15243             this.ios_options.push({
15244                 data : o,
15245                 el : opt
15246             });
15247             
15248         }
15249         
15250         this.store.data.each(function(d, rowIndex){
15251             
15252             var html = '';
15253             
15254             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15255                 html = d.data[this.displayField];
15256             }
15257             
15258             var value = '';
15259             
15260             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15261                 value = d.data[this.valueField];
15262             }
15263             
15264             var option = {
15265                 tag: 'option',
15266                 value : value,
15267                 html : html
15268             };
15269             
15270             if(this.value == d.data[this.valueField]){
15271                 option['selected'] = true;
15272             }
15273             
15274             var opt = this.inputEl().createChild(option);
15275             
15276             this.ios_options.push({
15277                 data : d.data,
15278                 el : opt
15279             });
15280             
15281         }, this);
15282         
15283         this.inputEl().on('change', function(){
15284            this.fireEvent('select', this);
15285         }, this);
15286         
15287     },
15288     
15289     clearIOSView: function()
15290     {
15291         this.inputEl().dom.innerHTML = '';
15292         
15293         this.ios_options = [];
15294     },
15295     
15296     setIOSValue: function(v)
15297     {
15298         this.value = v;
15299         
15300         if(!this.ios_options){
15301             return;
15302         }
15303         
15304         Roo.each(this.ios_options, function(opts){
15305            
15306            opts.el.dom.removeAttribute('selected');
15307            
15308            if(opts.data[this.valueField] != v){
15309                return;
15310            }
15311            
15312            opts.el.dom.setAttribute('selected', true);
15313            
15314         }, this);
15315     }
15316
15317     /** 
15318     * @cfg {Boolean} grow 
15319     * @hide 
15320     */
15321     /** 
15322     * @cfg {Number} growMin 
15323     * @hide 
15324     */
15325     /** 
15326     * @cfg {Number} growMax 
15327     * @hide 
15328     */
15329     /**
15330      * @hide
15331      * @method autoSize
15332      */
15333 });
15334
15335 Roo.apply(Roo.bootstrap.ComboBox,  {
15336     
15337     header : {
15338         tag: 'div',
15339         cls: 'modal-header',
15340         cn: [
15341             {
15342                 tag: 'h4',
15343                 cls: 'modal-title'
15344             }
15345         ]
15346     },
15347     
15348     body : {
15349         tag: 'div',
15350         cls: 'modal-body',
15351         cn: [
15352             {
15353                 tag: 'ul',
15354                 cls: 'list-group'
15355             }
15356         ]
15357     },
15358     
15359     listItemRadio : {
15360         tag: 'li',
15361         cls: 'list-group-item',
15362         cn: [
15363             {
15364                 tag: 'span',
15365                 cls: 'roo-combobox-list-group-item-value'
15366             },
15367             {
15368                 tag: 'div',
15369                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15370                 cn: [
15371                     {
15372                         tag: 'input',
15373                         type: 'radio'
15374                     },
15375                     {
15376                         tag: 'label'
15377                     }
15378                 ]
15379             }
15380         ]
15381     },
15382     
15383     listItemCheckbox : {
15384         tag: 'li',
15385         cls: 'list-group-item',
15386         cn: [
15387             {
15388                 tag: 'span',
15389                 cls: 'roo-combobox-list-group-item-value'
15390             },
15391             {
15392                 tag: 'div',
15393                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15394                 cn: [
15395                     {
15396                         tag: 'input',
15397                         type: 'checkbox'
15398                     },
15399                     {
15400                         tag: 'label'
15401                     }
15402                 ]
15403             }
15404         ]
15405     },
15406     
15407     emptyResult : {
15408         tag: 'div',
15409         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15410     },
15411     
15412     footer : {
15413         tag: 'div',
15414         cls: 'modal-footer',
15415         cn: [
15416             {
15417                 tag: 'div',
15418                 cls: 'row',
15419                 cn: [
15420                     {
15421                         tag: 'div',
15422                         cls: 'col-xs-6 text-left',
15423                         cn: {
15424                             tag: 'button',
15425                             cls: 'btn btn-danger roo-touch-view-cancel',
15426                             html: 'Cancel'
15427                         }
15428                     },
15429                     {
15430                         tag: 'div',
15431                         cls: 'col-xs-6 text-right',
15432                         cn: {
15433                             tag: 'button',
15434                             cls: 'btn btn-success roo-touch-view-ok',
15435                             html: 'OK'
15436                         }
15437                     }
15438                 ]
15439             }
15440         ]
15441         
15442     }
15443 });
15444
15445 Roo.apply(Roo.bootstrap.ComboBox,  {
15446     
15447     touchViewTemplate : {
15448         tag: 'div',
15449         cls: 'modal fade roo-combobox-touch-view',
15450         cn: [
15451             {
15452                 tag: 'div',
15453                 cls: 'modal-dialog',
15454                 style : 'position:fixed', // we have to fix position....
15455                 cn: [
15456                     {
15457                         tag: 'div',
15458                         cls: 'modal-content',
15459                         cn: [
15460                             Roo.bootstrap.ComboBox.header,
15461                             Roo.bootstrap.ComboBox.body,
15462                             Roo.bootstrap.ComboBox.footer
15463                         ]
15464                     }
15465                 ]
15466             }
15467         ]
15468     }
15469 });/*
15470  * Based on:
15471  * Ext JS Library 1.1.1
15472  * Copyright(c) 2006-2007, Ext JS, LLC.
15473  *
15474  * Originally Released Under LGPL - original licence link has changed is not relivant.
15475  *
15476  * Fork - LGPL
15477  * <script type="text/javascript">
15478  */
15479
15480 /**
15481  * @class Roo.View
15482  * @extends Roo.util.Observable
15483  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15484  * This class also supports single and multi selection modes. <br>
15485  * Create a data model bound view:
15486  <pre><code>
15487  var store = new Roo.data.Store(...);
15488
15489  var view = new Roo.View({
15490     el : "my-element",
15491     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15492  
15493     singleSelect: true,
15494     selectedClass: "ydataview-selected",
15495     store: store
15496  });
15497
15498  // listen for node click?
15499  view.on("click", function(vw, index, node, e){
15500  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15501  });
15502
15503  // load XML data
15504  dataModel.load("foobar.xml");
15505  </code></pre>
15506  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15507  * <br><br>
15508  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15509  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15510  * 
15511  * Note: old style constructor is still suported (container, template, config)
15512  * 
15513  * @constructor
15514  * Create a new View
15515  * @param {Object} config The config object
15516  * 
15517  */
15518 Roo.View = function(config, depreciated_tpl, depreciated_config){
15519     
15520     this.parent = false;
15521     
15522     if (typeof(depreciated_tpl) == 'undefined') {
15523         // new way.. - universal constructor.
15524         Roo.apply(this, config);
15525         this.el  = Roo.get(this.el);
15526     } else {
15527         // old format..
15528         this.el  = Roo.get(config);
15529         this.tpl = depreciated_tpl;
15530         Roo.apply(this, depreciated_config);
15531     }
15532     this.wrapEl  = this.el.wrap().wrap();
15533     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15534     
15535     
15536     if(typeof(this.tpl) == "string"){
15537         this.tpl = new Roo.Template(this.tpl);
15538     } else {
15539         // support xtype ctors..
15540         this.tpl = new Roo.factory(this.tpl, Roo);
15541     }
15542     
15543     
15544     this.tpl.compile();
15545     
15546     /** @private */
15547     this.addEvents({
15548         /**
15549          * @event beforeclick
15550          * Fires before a click is processed. Returns false to cancel the default action.
15551          * @param {Roo.View} this
15552          * @param {Number} index The index of the target node
15553          * @param {HTMLElement} node The target node
15554          * @param {Roo.EventObject} e The raw event object
15555          */
15556             "beforeclick" : true,
15557         /**
15558          * @event click
15559          * Fires when a template node is clicked.
15560          * @param {Roo.View} this
15561          * @param {Number} index The index of the target node
15562          * @param {HTMLElement} node The target node
15563          * @param {Roo.EventObject} e The raw event object
15564          */
15565             "click" : true,
15566         /**
15567          * @event dblclick
15568          * Fires when a template node is double clicked.
15569          * @param {Roo.View} this
15570          * @param {Number} index The index of the target node
15571          * @param {HTMLElement} node The target node
15572          * @param {Roo.EventObject} e The raw event object
15573          */
15574             "dblclick" : true,
15575         /**
15576          * @event contextmenu
15577          * Fires when a template node is right clicked.
15578          * @param {Roo.View} this
15579          * @param {Number} index The index of the target node
15580          * @param {HTMLElement} node The target node
15581          * @param {Roo.EventObject} e The raw event object
15582          */
15583             "contextmenu" : true,
15584         /**
15585          * @event selectionchange
15586          * Fires when the selected nodes change.
15587          * @param {Roo.View} this
15588          * @param {Array} selections Array of the selected nodes
15589          */
15590             "selectionchange" : true,
15591     
15592         /**
15593          * @event beforeselect
15594          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15595          * @param {Roo.View} this
15596          * @param {HTMLElement} node The node to be selected
15597          * @param {Array} selections Array of currently selected nodes
15598          */
15599             "beforeselect" : true,
15600         /**
15601          * @event preparedata
15602          * Fires on every row to render, to allow you to change the data.
15603          * @param {Roo.View} this
15604          * @param {Object} data to be rendered (change this)
15605          */
15606           "preparedata" : true
15607           
15608           
15609         });
15610
15611
15612
15613     this.el.on({
15614         "click": this.onClick,
15615         "dblclick": this.onDblClick,
15616         "contextmenu": this.onContextMenu,
15617         scope:this
15618     });
15619
15620     this.selections = [];
15621     this.nodes = [];
15622     this.cmp = new Roo.CompositeElementLite([]);
15623     if(this.store){
15624         this.store = Roo.factory(this.store, Roo.data);
15625         this.setStore(this.store, true);
15626     }
15627     
15628     if ( this.footer && this.footer.xtype) {
15629            
15630          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15631         
15632         this.footer.dataSource = this.store;
15633         this.footer.container = fctr;
15634         this.footer = Roo.factory(this.footer, Roo);
15635         fctr.insertFirst(this.el);
15636         
15637         // this is a bit insane - as the paging toolbar seems to detach the el..
15638 //        dom.parentNode.parentNode.parentNode
15639          // they get detached?
15640     }
15641     
15642     
15643     Roo.View.superclass.constructor.call(this);
15644     
15645     
15646 };
15647
15648 Roo.extend(Roo.View, Roo.util.Observable, {
15649     
15650      /**
15651      * @cfg {Roo.data.Store} store Data store to load data from.
15652      */
15653     store : false,
15654     
15655     /**
15656      * @cfg {String|Roo.Element} el The container element.
15657      */
15658     el : '',
15659     
15660     /**
15661      * @cfg {String|Roo.Template} tpl The template used by this View 
15662      */
15663     tpl : false,
15664     /**
15665      * @cfg {String} dataName the named area of the template to use as the data area
15666      *                          Works with domtemplates roo-name="name"
15667      */
15668     dataName: false,
15669     /**
15670      * @cfg {String} selectedClass The css class to add to selected nodes
15671      */
15672     selectedClass : "x-view-selected",
15673      /**
15674      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15675      */
15676     emptyText : "",
15677     
15678     /**
15679      * @cfg {String} text to display on mask (default Loading)
15680      */
15681     mask : false,
15682     /**
15683      * @cfg {Boolean} multiSelect Allow multiple selection
15684      */
15685     multiSelect : false,
15686     /**
15687      * @cfg {Boolean} singleSelect Allow single selection
15688      */
15689     singleSelect:  false,
15690     
15691     /**
15692      * @cfg {Boolean} toggleSelect - selecting 
15693      */
15694     toggleSelect : false,
15695     
15696     /**
15697      * @cfg {Boolean} tickable - selecting 
15698      */
15699     tickable : false,
15700     
15701     /**
15702      * Returns the element this view is bound to.
15703      * @return {Roo.Element}
15704      */
15705     getEl : function(){
15706         return this.wrapEl;
15707     },
15708     
15709     
15710
15711     /**
15712      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15713      */
15714     refresh : function(){
15715         //Roo.log('refresh');
15716         var t = this.tpl;
15717         
15718         // if we are using something like 'domtemplate', then
15719         // the what gets used is:
15720         // t.applySubtemplate(NAME, data, wrapping data..)
15721         // the outer template then get' applied with
15722         //     the store 'extra data'
15723         // and the body get's added to the
15724         //      roo-name="data" node?
15725         //      <span class='roo-tpl-{name}'></span> ?????
15726         
15727         
15728         
15729         this.clearSelections();
15730         this.el.update("");
15731         var html = [];
15732         var records = this.store.getRange();
15733         if(records.length < 1) {
15734             
15735             // is this valid??  = should it render a template??
15736             
15737             this.el.update(this.emptyText);
15738             return;
15739         }
15740         var el = this.el;
15741         if (this.dataName) {
15742             this.el.update(t.apply(this.store.meta)); //????
15743             el = this.el.child('.roo-tpl-' + this.dataName);
15744         }
15745         
15746         for(var i = 0, len = records.length; i < len; i++){
15747             var data = this.prepareData(records[i].data, i, records[i]);
15748             this.fireEvent("preparedata", this, data, i, records[i]);
15749             
15750             var d = Roo.apply({}, data);
15751             
15752             if(this.tickable){
15753                 Roo.apply(d, {'roo-id' : Roo.id()});
15754                 
15755                 var _this = this;
15756             
15757                 Roo.each(this.parent.item, function(item){
15758                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15759                         return;
15760                     }
15761                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15762                 });
15763             }
15764             
15765             html[html.length] = Roo.util.Format.trim(
15766                 this.dataName ?
15767                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15768                     t.apply(d)
15769             );
15770         }
15771         
15772         
15773         
15774         el.update(html.join(""));
15775         this.nodes = el.dom.childNodes;
15776         this.updateIndexes(0);
15777     },
15778     
15779
15780     /**
15781      * Function to override to reformat the data that is sent to
15782      * the template for each node.
15783      * DEPRICATED - use the preparedata event handler.
15784      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15785      * a JSON object for an UpdateManager bound view).
15786      */
15787     prepareData : function(data, index, record)
15788     {
15789         this.fireEvent("preparedata", this, data, index, record);
15790         return data;
15791     },
15792
15793     onUpdate : function(ds, record){
15794         // Roo.log('on update');   
15795         this.clearSelections();
15796         var index = this.store.indexOf(record);
15797         var n = this.nodes[index];
15798         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15799         n.parentNode.removeChild(n);
15800         this.updateIndexes(index, index);
15801     },
15802
15803     
15804     
15805 // --------- FIXME     
15806     onAdd : function(ds, records, index)
15807     {
15808         //Roo.log(['on Add', ds, records, index] );        
15809         this.clearSelections();
15810         if(this.nodes.length == 0){
15811             this.refresh();
15812             return;
15813         }
15814         var n = this.nodes[index];
15815         for(var i = 0, len = records.length; i < len; i++){
15816             var d = this.prepareData(records[i].data, i, records[i]);
15817             if(n){
15818                 this.tpl.insertBefore(n, d);
15819             }else{
15820                 
15821                 this.tpl.append(this.el, d);
15822             }
15823         }
15824         this.updateIndexes(index);
15825     },
15826
15827     onRemove : function(ds, record, index){
15828        // Roo.log('onRemove');
15829         this.clearSelections();
15830         var el = this.dataName  ?
15831             this.el.child('.roo-tpl-' + this.dataName) :
15832             this.el; 
15833         
15834         el.dom.removeChild(this.nodes[index]);
15835         this.updateIndexes(index);
15836     },
15837
15838     /**
15839      * Refresh an individual node.
15840      * @param {Number} index
15841      */
15842     refreshNode : function(index){
15843         this.onUpdate(this.store, this.store.getAt(index));
15844     },
15845
15846     updateIndexes : function(startIndex, endIndex){
15847         var ns = this.nodes;
15848         startIndex = startIndex || 0;
15849         endIndex = endIndex || ns.length - 1;
15850         for(var i = startIndex; i <= endIndex; i++){
15851             ns[i].nodeIndex = i;
15852         }
15853     },
15854
15855     /**
15856      * Changes the data store this view uses and refresh the view.
15857      * @param {Store} store
15858      */
15859     setStore : function(store, initial){
15860         if(!initial && this.store){
15861             this.store.un("datachanged", this.refresh);
15862             this.store.un("add", this.onAdd);
15863             this.store.un("remove", this.onRemove);
15864             this.store.un("update", this.onUpdate);
15865             this.store.un("clear", this.refresh);
15866             this.store.un("beforeload", this.onBeforeLoad);
15867             this.store.un("load", this.onLoad);
15868             this.store.un("loadexception", this.onLoad);
15869         }
15870         if(store){
15871           
15872             store.on("datachanged", this.refresh, this);
15873             store.on("add", this.onAdd, this);
15874             store.on("remove", this.onRemove, this);
15875             store.on("update", this.onUpdate, this);
15876             store.on("clear", this.refresh, this);
15877             store.on("beforeload", this.onBeforeLoad, this);
15878             store.on("load", this.onLoad, this);
15879             store.on("loadexception", this.onLoad, this);
15880         }
15881         
15882         if(store){
15883             this.refresh();
15884         }
15885     },
15886     /**
15887      * onbeforeLoad - masks the loading area.
15888      *
15889      */
15890     onBeforeLoad : function(store,opts)
15891     {
15892          //Roo.log('onBeforeLoad');   
15893         if (!opts.add) {
15894             this.el.update("");
15895         }
15896         this.el.mask(this.mask ? this.mask : "Loading" ); 
15897     },
15898     onLoad : function ()
15899     {
15900         this.el.unmask();
15901     },
15902     
15903
15904     /**
15905      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15906      * @param {HTMLElement} node
15907      * @return {HTMLElement} The template node
15908      */
15909     findItemFromChild : function(node){
15910         var el = this.dataName  ?
15911             this.el.child('.roo-tpl-' + this.dataName,true) :
15912             this.el.dom; 
15913         
15914         if(!node || node.parentNode == el){
15915                     return node;
15916             }
15917             var p = node.parentNode;
15918             while(p && p != el){
15919             if(p.parentNode == el){
15920                 return p;
15921             }
15922             p = p.parentNode;
15923         }
15924             return null;
15925     },
15926
15927     /** @ignore */
15928     onClick : function(e){
15929         var item = this.findItemFromChild(e.getTarget());
15930         if(item){
15931             var index = this.indexOf(item);
15932             if(this.onItemClick(item, index, e) !== false){
15933                 this.fireEvent("click", this, index, item, e);
15934             }
15935         }else{
15936             this.clearSelections();
15937         }
15938     },
15939
15940     /** @ignore */
15941     onContextMenu : function(e){
15942         var item = this.findItemFromChild(e.getTarget());
15943         if(item){
15944             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15945         }
15946     },
15947
15948     /** @ignore */
15949     onDblClick : function(e){
15950         var item = this.findItemFromChild(e.getTarget());
15951         if(item){
15952             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15953         }
15954     },
15955
15956     onItemClick : function(item, index, e)
15957     {
15958         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15959             return false;
15960         }
15961         if (this.toggleSelect) {
15962             var m = this.isSelected(item) ? 'unselect' : 'select';
15963             //Roo.log(m);
15964             var _t = this;
15965             _t[m](item, true, false);
15966             return true;
15967         }
15968         if(this.multiSelect || this.singleSelect){
15969             if(this.multiSelect && e.shiftKey && this.lastSelection){
15970                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15971             }else{
15972                 this.select(item, this.multiSelect && e.ctrlKey);
15973                 this.lastSelection = item;
15974             }
15975             
15976             if(!this.tickable){
15977                 e.preventDefault();
15978             }
15979             
15980         }
15981         return true;
15982     },
15983
15984     /**
15985      * Get the number of selected nodes.
15986      * @return {Number}
15987      */
15988     getSelectionCount : function(){
15989         return this.selections.length;
15990     },
15991
15992     /**
15993      * Get the currently selected nodes.
15994      * @return {Array} An array of HTMLElements
15995      */
15996     getSelectedNodes : function(){
15997         return this.selections;
15998     },
15999
16000     /**
16001      * Get the indexes of the selected nodes.
16002      * @return {Array}
16003      */
16004     getSelectedIndexes : function(){
16005         var indexes = [], s = this.selections;
16006         for(var i = 0, len = s.length; i < len; i++){
16007             indexes.push(s[i].nodeIndex);
16008         }
16009         return indexes;
16010     },
16011
16012     /**
16013      * Clear all selections
16014      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16015      */
16016     clearSelections : function(suppressEvent){
16017         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16018             this.cmp.elements = this.selections;
16019             this.cmp.removeClass(this.selectedClass);
16020             this.selections = [];
16021             if(!suppressEvent){
16022                 this.fireEvent("selectionchange", this, this.selections);
16023             }
16024         }
16025     },
16026
16027     /**
16028      * Returns true if the passed node is selected
16029      * @param {HTMLElement/Number} node The node or node index
16030      * @return {Boolean}
16031      */
16032     isSelected : function(node){
16033         var s = this.selections;
16034         if(s.length < 1){
16035             return false;
16036         }
16037         node = this.getNode(node);
16038         return s.indexOf(node) !== -1;
16039     },
16040
16041     /**
16042      * Selects nodes.
16043      * @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
16044      * @param {Boolean} keepExisting (optional) true to keep existing selections
16045      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16046      */
16047     select : function(nodeInfo, keepExisting, suppressEvent){
16048         if(nodeInfo instanceof Array){
16049             if(!keepExisting){
16050                 this.clearSelections(true);
16051             }
16052             for(var i = 0, len = nodeInfo.length; i < len; i++){
16053                 this.select(nodeInfo[i], true, true);
16054             }
16055             return;
16056         } 
16057         var node = this.getNode(nodeInfo);
16058         if(!node || this.isSelected(node)){
16059             return; // already selected.
16060         }
16061         if(!keepExisting){
16062             this.clearSelections(true);
16063         }
16064         
16065         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16066             Roo.fly(node).addClass(this.selectedClass);
16067             this.selections.push(node);
16068             if(!suppressEvent){
16069                 this.fireEvent("selectionchange", this, this.selections);
16070             }
16071         }
16072         
16073         
16074     },
16075       /**
16076      * Unselects nodes.
16077      * @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
16078      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16079      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16080      */
16081     unselect : function(nodeInfo, keepExisting, suppressEvent)
16082     {
16083         if(nodeInfo instanceof Array){
16084             Roo.each(this.selections, function(s) {
16085                 this.unselect(s, nodeInfo);
16086             }, this);
16087             return;
16088         }
16089         var node = this.getNode(nodeInfo);
16090         if(!node || !this.isSelected(node)){
16091             //Roo.log("not selected");
16092             return; // not selected.
16093         }
16094         // fireevent???
16095         var ns = [];
16096         Roo.each(this.selections, function(s) {
16097             if (s == node ) {
16098                 Roo.fly(node).removeClass(this.selectedClass);
16099
16100                 return;
16101             }
16102             ns.push(s);
16103         },this);
16104         
16105         this.selections= ns;
16106         this.fireEvent("selectionchange", this, this.selections);
16107     },
16108
16109     /**
16110      * Gets a template node.
16111      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16112      * @return {HTMLElement} The node or null if it wasn't found
16113      */
16114     getNode : function(nodeInfo){
16115         if(typeof nodeInfo == "string"){
16116             return document.getElementById(nodeInfo);
16117         }else if(typeof nodeInfo == "number"){
16118             return this.nodes[nodeInfo];
16119         }
16120         return nodeInfo;
16121     },
16122
16123     /**
16124      * Gets a range template nodes.
16125      * @param {Number} startIndex
16126      * @param {Number} endIndex
16127      * @return {Array} An array of nodes
16128      */
16129     getNodes : function(start, end){
16130         var ns = this.nodes;
16131         start = start || 0;
16132         end = typeof end == "undefined" ? ns.length - 1 : end;
16133         var nodes = [];
16134         if(start <= end){
16135             for(var i = start; i <= end; i++){
16136                 nodes.push(ns[i]);
16137             }
16138         } else{
16139             for(var i = start; i >= end; i--){
16140                 nodes.push(ns[i]);
16141             }
16142         }
16143         return nodes;
16144     },
16145
16146     /**
16147      * Finds the index of the passed node
16148      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16149      * @return {Number} The index of the node or -1
16150      */
16151     indexOf : function(node){
16152         node = this.getNode(node);
16153         if(typeof node.nodeIndex == "number"){
16154             return node.nodeIndex;
16155         }
16156         var ns = this.nodes;
16157         for(var i = 0, len = ns.length; i < len; i++){
16158             if(ns[i] == node){
16159                 return i;
16160             }
16161         }
16162         return -1;
16163     }
16164 });
16165 /*
16166  * - LGPL
16167  *
16168  * based on jquery fullcalendar
16169  * 
16170  */
16171
16172 Roo.bootstrap = Roo.bootstrap || {};
16173 /**
16174  * @class Roo.bootstrap.Calendar
16175  * @extends Roo.bootstrap.Component
16176  * Bootstrap Calendar class
16177  * @cfg {Boolean} loadMask (true|false) default false
16178  * @cfg {Object} header generate the user specific header of the calendar, default false
16179
16180  * @constructor
16181  * Create a new Container
16182  * @param {Object} config The config object
16183  */
16184
16185
16186
16187 Roo.bootstrap.Calendar = function(config){
16188     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16189      this.addEvents({
16190         /**
16191              * @event select
16192              * Fires when a date is selected
16193              * @param {DatePicker} this
16194              * @param {Date} date The selected date
16195              */
16196         'select': true,
16197         /**
16198              * @event monthchange
16199              * Fires when the displayed month changes 
16200              * @param {DatePicker} this
16201              * @param {Date} date The selected month
16202              */
16203         'monthchange': true,
16204         /**
16205              * @event evententer
16206              * Fires when mouse over an event
16207              * @param {Calendar} this
16208              * @param {event} Event
16209              */
16210         'evententer': true,
16211         /**
16212              * @event eventleave
16213              * Fires when the mouse leaves an
16214              * @param {Calendar} this
16215              * @param {event}
16216              */
16217         'eventleave': true,
16218         /**
16219              * @event eventclick
16220              * Fires when the mouse click an
16221              * @param {Calendar} this
16222              * @param {event}
16223              */
16224         'eventclick': true
16225         
16226     });
16227
16228 };
16229
16230 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16231     
16232      /**
16233      * @cfg {Number} startDay
16234      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16235      */
16236     startDay : 0,
16237     
16238     loadMask : false,
16239     
16240     header : false,
16241       
16242     getAutoCreate : function(){
16243         
16244         
16245         var fc_button = function(name, corner, style, content ) {
16246             return Roo.apply({},{
16247                 tag : 'span',
16248                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16249                          (corner.length ?
16250                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16251                             ''
16252                         ),
16253                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16254                 unselectable: 'on'
16255             });
16256         };
16257         
16258         var header = {};
16259         
16260         if(!this.header){
16261             header = {
16262                 tag : 'table',
16263                 cls : 'fc-header',
16264                 style : 'width:100%',
16265                 cn : [
16266                     {
16267                         tag: 'tr',
16268                         cn : [
16269                             {
16270                                 tag : 'td',
16271                                 cls : 'fc-header-left',
16272                                 cn : [
16273                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16274                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16275                                     { tag: 'span', cls: 'fc-header-space' },
16276                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16277
16278
16279                                 ]
16280                             },
16281
16282                             {
16283                                 tag : 'td',
16284                                 cls : 'fc-header-center',
16285                                 cn : [
16286                                     {
16287                                         tag: 'span',
16288                                         cls: 'fc-header-title',
16289                                         cn : {
16290                                             tag: 'H2',
16291                                             html : 'month / year'
16292                                         }
16293                                     }
16294
16295                                 ]
16296                             },
16297                             {
16298                                 tag : 'td',
16299                                 cls : 'fc-header-right',
16300                                 cn : [
16301                               /*      fc_button('month', 'left', '', 'month' ),
16302                                     fc_button('week', '', '', 'week' ),
16303                                     fc_button('day', 'right', '', 'day' )
16304                                 */    
16305
16306                                 ]
16307                             }
16308
16309                         ]
16310                     }
16311                 ]
16312             };
16313         }
16314         
16315         header = this.header;
16316         
16317        
16318         var cal_heads = function() {
16319             var ret = [];
16320             // fixme - handle this.
16321             
16322             for (var i =0; i < Date.dayNames.length; i++) {
16323                 var d = Date.dayNames[i];
16324                 ret.push({
16325                     tag: 'th',
16326                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16327                     html : d.substring(0,3)
16328                 });
16329                 
16330             }
16331             ret[0].cls += ' fc-first';
16332             ret[6].cls += ' fc-last';
16333             return ret;
16334         };
16335         var cal_cell = function(n) {
16336             return  {
16337                 tag: 'td',
16338                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16339                 cn : [
16340                     {
16341                         cn : [
16342                             {
16343                                 cls: 'fc-day-number',
16344                                 html: 'D'
16345                             },
16346                             {
16347                                 cls: 'fc-day-content',
16348                              
16349                                 cn : [
16350                                      {
16351                                         style: 'position: relative;' // height: 17px;
16352                                     }
16353                                 ]
16354                             }
16355                             
16356                             
16357                         ]
16358                     }
16359                 ]
16360                 
16361             }
16362         };
16363         var cal_rows = function() {
16364             
16365             var ret = [];
16366             for (var r = 0; r < 6; r++) {
16367                 var row= {
16368                     tag : 'tr',
16369                     cls : 'fc-week',
16370                     cn : []
16371                 };
16372                 
16373                 for (var i =0; i < Date.dayNames.length; i++) {
16374                     var d = Date.dayNames[i];
16375                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16376
16377                 }
16378                 row.cn[0].cls+=' fc-first';
16379                 row.cn[0].cn[0].style = 'min-height:90px';
16380                 row.cn[6].cls+=' fc-last';
16381                 ret.push(row);
16382                 
16383             }
16384             ret[0].cls += ' fc-first';
16385             ret[4].cls += ' fc-prev-last';
16386             ret[5].cls += ' fc-last';
16387             return ret;
16388             
16389         };
16390         
16391         var cal_table = {
16392             tag: 'table',
16393             cls: 'fc-border-separate',
16394             style : 'width:100%',
16395             cellspacing  : 0,
16396             cn : [
16397                 { 
16398                     tag: 'thead',
16399                     cn : [
16400                         { 
16401                             tag: 'tr',
16402                             cls : 'fc-first fc-last',
16403                             cn : cal_heads()
16404                         }
16405                     ]
16406                 },
16407                 { 
16408                     tag: 'tbody',
16409                     cn : cal_rows()
16410                 }
16411                   
16412             ]
16413         };
16414          
16415          var cfg = {
16416             cls : 'fc fc-ltr',
16417             cn : [
16418                 header,
16419                 {
16420                     cls : 'fc-content',
16421                     style : "position: relative;",
16422                     cn : [
16423                         {
16424                             cls : 'fc-view fc-view-month fc-grid',
16425                             style : 'position: relative',
16426                             unselectable : 'on',
16427                             cn : [
16428                                 {
16429                                     cls : 'fc-event-container',
16430                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16431                                 },
16432                                 cal_table
16433                             ]
16434                         }
16435                     ]
16436     
16437                 }
16438            ] 
16439             
16440         };
16441         
16442          
16443         
16444         return cfg;
16445     },
16446     
16447     
16448     initEvents : function()
16449     {
16450         if(!this.store){
16451             throw "can not find store for calendar";
16452         }
16453         
16454         var mark = {
16455             tag: "div",
16456             cls:"x-dlg-mask",
16457             style: "text-align:center",
16458             cn: [
16459                 {
16460                     tag: "div",
16461                     style: "background-color:white;width:50%;margin:250 auto",
16462                     cn: [
16463                         {
16464                             tag: "img",
16465                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16466                         },
16467                         {
16468                             tag: "span",
16469                             html: "Loading"
16470                         }
16471                         
16472                     ]
16473                 }
16474             ]
16475         };
16476         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16477         
16478         var size = this.el.select('.fc-content', true).first().getSize();
16479         this.maskEl.setSize(size.width, size.height);
16480         this.maskEl.enableDisplayMode("block");
16481         if(!this.loadMask){
16482             this.maskEl.hide();
16483         }
16484         
16485         this.store = Roo.factory(this.store, Roo.data);
16486         this.store.on('load', this.onLoad, this);
16487         this.store.on('beforeload', this.onBeforeLoad, this);
16488         
16489         this.resize();
16490         
16491         this.cells = this.el.select('.fc-day',true);
16492         //Roo.log(this.cells);
16493         this.textNodes = this.el.query('.fc-day-number');
16494         this.cells.addClassOnOver('fc-state-hover');
16495         
16496         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16497         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16498         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16499         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16500         
16501         this.on('monthchange', this.onMonthChange, this);
16502         
16503         this.update(new Date().clearTime());
16504     },
16505     
16506     resize : function() {
16507         var sz  = this.el.getSize();
16508         
16509         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16510         this.el.select('.fc-day-content div',true).setHeight(34);
16511     },
16512     
16513     
16514     // private
16515     showPrevMonth : function(e){
16516         this.update(this.activeDate.add("mo", -1));
16517     },
16518     showToday : function(e){
16519         this.update(new Date().clearTime());
16520     },
16521     // private
16522     showNextMonth : function(e){
16523         this.update(this.activeDate.add("mo", 1));
16524     },
16525
16526     // private
16527     showPrevYear : function(){
16528         this.update(this.activeDate.add("y", -1));
16529     },
16530
16531     // private
16532     showNextYear : function(){
16533         this.update(this.activeDate.add("y", 1));
16534     },
16535
16536     
16537    // private
16538     update : function(date)
16539     {
16540         var vd = this.activeDate;
16541         this.activeDate = date;
16542 //        if(vd && this.el){
16543 //            var t = date.getTime();
16544 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16545 //                Roo.log('using add remove');
16546 //                
16547 //                this.fireEvent('monthchange', this, date);
16548 //                
16549 //                this.cells.removeClass("fc-state-highlight");
16550 //                this.cells.each(function(c){
16551 //                   if(c.dateValue == t){
16552 //                       c.addClass("fc-state-highlight");
16553 //                       setTimeout(function(){
16554 //                            try{c.dom.firstChild.focus();}catch(e){}
16555 //                       }, 50);
16556 //                       return false;
16557 //                   }
16558 //                   return true;
16559 //                });
16560 //                return;
16561 //            }
16562 //        }
16563         
16564         var days = date.getDaysInMonth();
16565         
16566         var firstOfMonth = date.getFirstDateOfMonth();
16567         var startingPos = firstOfMonth.getDay()-this.startDay;
16568         
16569         if(startingPos < this.startDay){
16570             startingPos += 7;
16571         }
16572         
16573         var pm = date.add(Date.MONTH, -1);
16574         var prevStart = pm.getDaysInMonth()-startingPos;
16575 //        
16576         this.cells = this.el.select('.fc-day',true);
16577         this.textNodes = this.el.query('.fc-day-number');
16578         this.cells.addClassOnOver('fc-state-hover');
16579         
16580         var cells = this.cells.elements;
16581         var textEls = this.textNodes;
16582         
16583         Roo.each(cells, function(cell){
16584             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16585         });
16586         
16587         days += startingPos;
16588
16589         // convert everything to numbers so it's fast
16590         var day = 86400000;
16591         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16592         //Roo.log(d);
16593         //Roo.log(pm);
16594         //Roo.log(prevStart);
16595         
16596         var today = new Date().clearTime().getTime();
16597         var sel = date.clearTime().getTime();
16598         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16599         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16600         var ddMatch = this.disabledDatesRE;
16601         var ddText = this.disabledDatesText;
16602         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16603         var ddaysText = this.disabledDaysText;
16604         var format = this.format;
16605         
16606         var setCellClass = function(cal, cell){
16607             cell.row = 0;
16608             cell.events = [];
16609             cell.more = [];
16610             //Roo.log('set Cell Class');
16611             cell.title = "";
16612             var t = d.getTime();
16613             
16614             //Roo.log(d);
16615             
16616             cell.dateValue = t;
16617             if(t == today){
16618                 cell.className += " fc-today";
16619                 cell.className += " fc-state-highlight";
16620                 cell.title = cal.todayText;
16621             }
16622             if(t == sel){
16623                 // disable highlight in other month..
16624                 //cell.className += " fc-state-highlight";
16625                 
16626             }
16627             // disabling
16628             if(t < min) {
16629                 cell.className = " fc-state-disabled";
16630                 cell.title = cal.minText;
16631                 return;
16632             }
16633             if(t > max) {
16634                 cell.className = " fc-state-disabled";
16635                 cell.title = cal.maxText;
16636                 return;
16637             }
16638             if(ddays){
16639                 if(ddays.indexOf(d.getDay()) != -1){
16640                     cell.title = ddaysText;
16641                     cell.className = " fc-state-disabled";
16642                 }
16643             }
16644             if(ddMatch && format){
16645                 var fvalue = d.dateFormat(format);
16646                 if(ddMatch.test(fvalue)){
16647                     cell.title = ddText.replace("%0", fvalue);
16648                     cell.className = " fc-state-disabled";
16649                 }
16650             }
16651             
16652             if (!cell.initialClassName) {
16653                 cell.initialClassName = cell.dom.className;
16654             }
16655             
16656             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16657         };
16658
16659         var i = 0;
16660         
16661         for(; i < startingPos; i++) {
16662             textEls[i].innerHTML = (++prevStart);
16663             d.setDate(d.getDate()+1);
16664             
16665             cells[i].className = "fc-past fc-other-month";
16666             setCellClass(this, cells[i]);
16667         }
16668         
16669         var intDay = 0;
16670         
16671         for(; i < days; i++){
16672             intDay = i - startingPos + 1;
16673             textEls[i].innerHTML = (intDay);
16674             d.setDate(d.getDate()+1);
16675             
16676             cells[i].className = ''; // "x-date-active";
16677             setCellClass(this, cells[i]);
16678         }
16679         var extraDays = 0;
16680         
16681         for(; i < 42; i++) {
16682             textEls[i].innerHTML = (++extraDays);
16683             d.setDate(d.getDate()+1);
16684             
16685             cells[i].className = "fc-future fc-other-month";
16686             setCellClass(this, cells[i]);
16687         }
16688         
16689         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16690         
16691         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16692         
16693         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16694         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16695         
16696         if(totalRows != 6){
16697             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16698             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16699         }
16700         
16701         this.fireEvent('monthchange', this, date);
16702         
16703         
16704         /*
16705         if(!this.internalRender){
16706             var main = this.el.dom.firstChild;
16707             var w = main.offsetWidth;
16708             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16709             Roo.fly(main).setWidth(w);
16710             this.internalRender = true;
16711             // opera does not respect the auto grow header center column
16712             // then, after it gets a width opera refuses to recalculate
16713             // without a second pass
16714             if(Roo.isOpera && !this.secondPass){
16715                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16716                 this.secondPass = true;
16717                 this.update.defer(10, this, [date]);
16718             }
16719         }
16720         */
16721         
16722     },
16723     
16724     findCell : function(dt) {
16725         dt = dt.clearTime().getTime();
16726         var ret = false;
16727         this.cells.each(function(c){
16728             //Roo.log("check " +c.dateValue + '?=' + dt);
16729             if(c.dateValue == dt){
16730                 ret = c;
16731                 return false;
16732             }
16733             return true;
16734         });
16735         
16736         return ret;
16737     },
16738     
16739     findCells : function(ev) {
16740         var s = ev.start.clone().clearTime().getTime();
16741        // Roo.log(s);
16742         var e= ev.end.clone().clearTime().getTime();
16743        // Roo.log(e);
16744         var ret = [];
16745         this.cells.each(function(c){
16746              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16747             
16748             if(c.dateValue > e){
16749                 return ;
16750             }
16751             if(c.dateValue < s){
16752                 return ;
16753             }
16754             ret.push(c);
16755         });
16756         
16757         return ret;    
16758     },
16759     
16760 //    findBestRow: function(cells)
16761 //    {
16762 //        var ret = 0;
16763 //        
16764 //        for (var i =0 ; i < cells.length;i++) {
16765 //            ret  = Math.max(cells[i].rows || 0,ret);
16766 //        }
16767 //        return ret;
16768 //        
16769 //    },
16770     
16771     
16772     addItem : function(ev)
16773     {
16774         // look for vertical location slot in
16775         var cells = this.findCells(ev);
16776         
16777 //        ev.row = this.findBestRow(cells);
16778         
16779         // work out the location.
16780         
16781         var crow = false;
16782         var rows = [];
16783         for(var i =0; i < cells.length; i++) {
16784             
16785             cells[i].row = cells[0].row;
16786             
16787             if(i == 0){
16788                 cells[i].row = cells[i].row + 1;
16789             }
16790             
16791             if (!crow) {
16792                 crow = {
16793                     start : cells[i],
16794                     end :  cells[i]
16795                 };
16796                 continue;
16797             }
16798             if (crow.start.getY() == cells[i].getY()) {
16799                 // on same row.
16800                 crow.end = cells[i];
16801                 continue;
16802             }
16803             // different row.
16804             rows.push(crow);
16805             crow = {
16806                 start: cells[i],
16807                 end : cells[i]
16808             };
16809             
16810         }
16811         
16812         rows.push(crow);
16813         ev.els = [];
16814         ev.rows = rows;
16815         ev.cells = cells;
16816         
16817         cells[0].events.push(ev);
16818         
16819         this.calevents.push(ev);
16820     },
16821     
16822     clearEvents: function() {
16823         
16824         if(!this.calevents){
16825             return;
16826         }
16827         
16828         Roo.each(this.cells.elements, function(c){
16829             c.row = 0;
16830             c.events = [];
16831             c.more = [];
16832         });
16833         
16834         Roo.each(this.calevents, function(e) {
16835             Roo.each(e.els, function(el) {
16836                 el.un('mouseenter' ,this.onEventEnter, this);
16837                 el.un('mouseleave' ,this.onEventLeave, this);
16838                 el.remove();
16839             },this);
16840         },this);
16841         
16842         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16843             e.remove();
16844         });
16845         
16846     },
16847     
16848     renderEvents: function()
16849     {   
16850         var _this = this;
16851         
16852         this.cells.each(function(c) {
16853             
16854             if(c.row < 5){
16855                 return;
16856             }
16857             
16858             var ev = c.events;
16859             
16860             var r = 4;
16861             if(c.row != c.events.length){
16862                 r = 4 - (4 - (c.row - c.events.length));
16863             }
16864             
16865             c.events = ev.slice(0, r);
16866             c.more = ev.slice(r);
16867             
16868             if(c.more.length && c.more.length == 1){
16869                 c.events.push(c.more.pop());
16870             }
16871             
16872             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16873             
16874         });
16875             
16876         this.cells.each(function(c) {
16877             
16878             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16879             
16880             
16881             for (var e = 0; e < c.events.length; e++){
16882                 var ev = c.events[e];
16883                 var rows = ev.rows;
16884                 
16885                 for(var i = 0; i < rows.length; i++) {
16886                 
16887                     // how many rows should it span..
16888
16889                     var  cfg = {
16890                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16891                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16892
16893                         unselectable : "on",
16894                         cn : [
16895                             {
16896                                 cls: 'fc-event-inner',
16897                                 cn : [
16898     //                                {
16899     //                                  tag:'span',
16900     //                                  cls: 'fc-event-time',
16901     //                                  html : cells.length > 1 ? '' : ev.time
16902     //                                },
16903                                     {
16904                                       tag:'span',
16905                                       cls: 'fc-event-title',
16906                                       html : String.format('{0}', ev.title)
16907                                     }
16908
16909
16910                                 ]
16911                             },
16912                             {
16913                                 cls: 'ui-resizable-handle ui-resizable-e',
16914                                 html : '&nbsp;&nbsp;&nbsp'
16915                             }
16916
16917                         ]
16918                     };
16919
16920                     if (i == 0) {
16921                         cfg.cls += ' fc-event-start';
16922                     }
16923                     if ((i+1) == rows.length) {
16924                         cfg.cls += ' fc-event-end';
16925                     }
16926
16927                     var ctr = _this.el.select('.fc-event-container',true).first();
16928                     var cg = ctr.createChild(cfg);
16929
16930                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16931                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16932
16933                     var r = (c.more.length) ? 1 : 0;
16934                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16935                     cg.setWidth(ebox.right - sbox.x -2);
16936
16937                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16938                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16939                     cg.on('click', _this.onEventClick, _this, ev);
16940
16941                     ev.els.push(cg);
16942                     
16943                 }
16944                 
16945             }
16946             
16947             
16948             if(c.more.length){
16949                 var  cfg = {
16950                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16951                     style : 'position: absolute',
16952                     unselectable : "on",
16953                     cn : [
16954                         {
16955                             cls: 'fc-event-inner',
16956                             cn : [
16957                                 {
16958                                   tag:'span',
16959                                   cls: 'fc-event-title',
16960                                   html : 'More'
16961                                 }
16962
16963
16964                             ]
16965                         },
16966                         {
16967                             cls: 'ui-resizable-handle ui-resizable-e',
16968                             html : '&nbsp;&nbsp;&nbsp'
16969                         }
16970
16971                     ]
16972                 };
16973
16974                 var ctr = _this.el.select('.fc-event-container',true).first();
16975                 var cg = ctr.createChild(cfg);
16976
16977                 var sbox = c.select('.fc-day-content',true).first().getBox();
16978                 var ebox = c.select('.fc-day-content',true).first().getBox();
16979                 //Roo.log(cg);
16980                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16981                 cg.setWidth(ebox.right - sbox.x -2);
16982
16983                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16984                 
16985             }
16986             
16987         });
16988         
16989         
16990         
16991     },
16992     
16993     onEventEnter: function (e, el,event,d) {
16994         this.fireEvent('evententer', this, el, event);
16995     },
16996     
16997     onEventLeave: function (e, el,event,d) {
16998         this.fireEvent('eventleave', this, el, event);
16999     },
17000     
17001     onEventClick: function (e, el,event,d) {
17002         this.fireEvent('eventclick', this, el, event);
17003     },
17004     
17005     onMonthChange: function () {
17006         this.store.load();
17007     },
17008     
17009     onMoreEventClick: function(e, el, more)
17010     {
17011         var _this = this;
17012         
17013         this.calpopover.placement = 'right';
17014         this.calpopover.setTitle('More');
17015         
17016         this.calpopover.setContent('');
17017         
17018         var ctr = this.calpopover.el.select('.popover-content', true).first();
17019         
17020         Roo.each(more, function(m){
17021             var cfg = {
17022                 cls : 'fc-event-hori fc-event-draggable',
17023                 html : m.title
17024             };
17025             var cg = ctr.createChild(cfg);
17026             
17027             cg.on('click', _this.onEventClick, _this, m);
17028         });
17029         
17030         this.calpopover.show(el);
17031         
17032         
17033     },
17034     
17035     onLoad: function () 
17036     {   
17037         this.calevents = [];
17038         var cal = this;
17039         
17040         if(this.store.getCount() > 0){
17041             this.store.data.each(function(d){
17042                cal.addItem({
17043                     id : d.data.id,
17044                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17045                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17046                     time : d.data.start_time,
17047                     title : d.data.title,
17048                     description : d.data.description,
17049                     venue : d.data.venue
17050                 });
17051             });
17052         }
17053         
17054         this.renderEvents();
17055         
17056         if(this.calevents.length && this.loadMask){
17057             this.maskEl.hide();
17058         }
17059     },
17060     
17061     onBeforeLoad: function()
17062     {
17063         this.clearEvents();
17064         if(this.loadMask){
17065             this.maskEl.show();
17066         }
17067     }
17068 });
17069
17070  
17071  /*
17072  * - LGPL
17073  *
17074  * element
17075  * 
17076  */
17077
17078 /**
17079  * @class Roo.bootstrap.Popover
17080  * @extends Roo.bootstrap.Component
17081  * Bootstrap Popover class
17082  * @cfg {String} html contents of the popover   (or false to use children..)
17083  * @cfg {String} title of popover (or false to hide)
17084  * @cfg {String} placement how it is placed
17085  * @cfg {String} trigger click || hover (or false to trigger manually)
17086  * @cfg {String} over what (parent or false to trigger manually.)
17087  * @cfg {Number} delay - delay before showing
17088  
17089  * @constructor
17090  * Create a new Popover
17091  * @param {Object} config The config object
17092  */
17093
17094 Roo.bootstrap.Popover = function(config){
17095     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17096     
17097     this.addEvents({
17098         // raw events
17099          /**
17100          * @event show
17101          * After the popover show
17102          * 
17103          * @param {Roo.bootstrap.Popover} this
17104          */
17105         "show" : true,
17106         /**
17107          * @event hide
17108          * After the popover hide
17109          * 
17110          * @param {Roo.bootstrap.Popover} this
17111          */
17112         "hide" : true
17113     });
17114 };
17115
17116 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17117     
17118     title: 'Fill in a title',
17119     html: false,
17120     
17121     placement : 'right',
17122     trigger : 'hover', // hover
17123     
17124     delay : 0,
17125     
17126     over: 'parent',
17127     
17128     can_build_overlaid : false,
17129     
17130     getChildContainer : function()
17131     {
17132         return this.el.select('.popover-content',true).first();
17133     },
17134     
17135     getAutoCreate : function(){
17136          
17137         var cfg = {
17138            cls : 'popover roo-dynamic',
17139            style: 'display:block',
17140            cn : [
17141                 {
17142                     cls : 'arrow'
17143                 },
17144                 {
17145                     cls : 'popover-inner',
17146                     cn : [
17147                         {
17148                             tag: 'h3',
17149                             cls: 'popover-title',
17150                             html : this.title
17151                         },
17152                         {
17153                             cls : 'popover-content',
17154                             html : this.html
17155                         }
17156                     ]
17157                     
17158                 }
17159            ]
17160         };
17161         
17162         return cfg;
17163     },
17164     setTitle: function(str)
17165     {
17166         this.title = str;
17167         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17168     },
17169     setContent: function(str)
17170     {
17171         this.html = str;
17172         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17173     },
17174     // as it get's added to the bottom of the page.
17175     onRender : function(ct, position)
17176     {
17177         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17178         if(!this.el){
17179             var cfg = Roo.apply({},  this.getAutoCreate());
17180             cfg.id = Roo.id();
17181             
17182             if (this.cls) {
17183                 cfg.cls += ' ' + this.cls;
17184             }
17185             if (this.style) {
17186                 cfg.style = this.style;
17187             }
17188             //Roo.log("adding to ");
17189             this.el = Roo.get(document.body).createChild(cfg, position);
17190 //            Roo.log(this.el);
17191         }
17192         this.initEvents();
17193     },
17194     
17195     initEvents : function()
17196     {
17197         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17198         this.el.enableDisplayMode('block');
17199         this.el.hide();
17200         if (this.over === false) {
17201             return; 
17202         }
17203         if (this.triggers === false) {
17204             return;
17205         }
17206         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17207         var triggers = this.trigger ? this.trigger.split(' ') : [];
17208         Roo.each(triggers, function(trigger) {
17209         
17210             if (trigger == 'click') {
17211                 on_el.on('click', this.toggle, this);
17212             } else if (trigger != 'manual') {
17213                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17214                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17215       
17216                 on_el.on(eventIn  ,this.enter, this);
17217                 on_el.on(eventOut, this.leave, this);
17218             }
17219         }, this);
17220         
17221     },
17222     
17223     
17224     // private
17225     timeout : null,
17226     hoverState : null,
17227     
17228     toggle : function () {
17229         this.hoverState == 'in' ? this.leave() : this.enter();
17230     },
17231     
17232     enter : function () {
17233         
17234         clearTimeout(this.timeout);
17235     
17236         this.hoverState = 'in';
17237     
17238         if (!this.delay || !this.delay.show) {
17239             this.show();
17240             return;
17241         }
17242         var _t = this;
17243         this.timeout = setTimeout(function () {
17244             if (_t.hoverState == 'in') {
17245                 _t.show();
17246             }
17247         }, this.delay.show)
17248     },
17249     
17250     leave : function() {
17251         clearTimeout(this.timeout);
17252     
17253         this.hoverState = 'out';
17254     
17255         if (!this.delay || !this.delay.hide) {
17256             this.hide();
17257             return;
17258         }
17259         var _t = this;
17260         this.timeout = setTimeout(function () {
17261             if (_t.hoverState == 'out') {
17262                 _t.hide();
17263             }
17264         }, this.delay.hide)
17265     },
17266     
17267     show : function (on_el)
17268     {
17269         if (!on_el) {
17270             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17271         }
17272         
17273         // set content.
17274         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17275         if (this.html !== false) {
17276             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17277         }
17278         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17279         if (!this.title.length) {
17280             this.el.select('.popover-title',true).hide();
17281         }
17282         
17283         var placement = typeof this.placement == 'function' ?
17284             this.placement.call(this, this.el, on_el) :
17285             this.placement;
17286             
17287         var autoToken = /\s?auto?\s?/i;
17288         var autoPlace = autoToken.test(placement);
17289         if (autoPlace) {
17290             placement = placement.replace(autoToken, '') || 'top';
17291         }
17292         
17293         //this.el.detach()
17294         //this.el.setXY([0,0]);
17295         this.el.show();
17296         this.el.dom.style.display='block';
17297         this.el.addClass(placement);
17298         
17299         //this.el.appendTo(on_el);
17300         
17301         var p = this.getPosition();
17302         var box = this.el.getBox();
17303         
17304         if (autoPlace) {
17305             // fixme..
17306         }
17307         var align = Roo.bootstrap.Popover.alignment[placement];
17308         this.el.alignTo(on_el, align[0],align[1]);
17309         //var arrow = this.el.select('.arrow',true).first();
17310         //arrow.set(align[2], 
17311         
17312         this.el.addClass('in');
17313         
17314         
17315         if (this.el.hasClass('fade')) {
17316             // fade it?
17317         }
17318         
17319         this.hoverState = 'in';
17320         
17321         this.fireEvent('show', this);
17322         
17323     },
17324     hide : function()
17325     {
17326         this.el.setXY([0,0]);
17327         this.el.removeClass('in');
17328         this.el.hide();
17329         this.hoverState = null;
17330         
17331         this.fireEvent('hide', this);
17332     }
17333     
17334 });
17335
17336 Roo.bootstrap.Popover.alignment = {
17337     'left' : ['r-l', [-10,0], 'right'],
17338     'right' : ['l-r', [10,0], 'left'],
17339     'bottom' : ['t-b', [0,10], 'top'],
17340     'top' : [ 'b-t', [0,-10], 'bottom']
17341 };
17342
17343  /*
17344  * - LGPL
17345  *
17346  * Progress
17347  * 
17348  */
17349
17350 /**
17351  * @class Roo.bootstrap.Progress
17352  * @extends Roo.bootstrap.Component
17353  * Bootstrap Progress class
17354  * @cfg {Boolean} striped striped of the progress bar
17355  * @cfg {Boolean} active animated of the progress bar
17356  * 
17357  * 
17358  * @constructor
17359  * Create a new Progress
17360  * @param {Object} config The config object
17361  */
17362
17363 Roo.bootstrap.Progress = function(config){
17364     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17365 };
17366
17367 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17368     
17369     striped : false,
17370     active: false,
17371     
17372     getAutoCreate : function(){
17373         var cfg = {
17374             tag: 'div',
17375             cls: 'progress'
17376         };
17377         
17378         
17379         if(this.striped){
17380             cfg.cls += ' progress-striped';
17381         }
17382       
17383         if(this.active){
17384             cfg.cls += ' active';
17385         }
17386         
17387         
17388         return cfg;
17389     }
17390    
17391 });
17392
17393  
17394
17395  /*
17396  * - LGPL
17397  *
17398  * ProgressBar
17399  * 
17400  */
17401
17402 /**
17403  * @class Roo.bootstrap.ProgressBar
17404  * @extends Roo.bootstrap.Component
17405  * Bootstrap ProgressBar class
17406  * @cfg {Number} aria_valuenow aria-value now
17407  * @cfg {Number} aria_valuemin aria-value min
17408  * @cfg {Number} aria_valuemax aria-value max
17409  * @cfg {String} label label for the progress bar
17410  * @cfg {String} panel (success | info | warning | danger )
17411  * @cfg {String} role role of the progress bar
17412  * @cfg {String} sr_only text
17413  * 
17414  * 
17415  * @constructor
17416  * Create a new ProgressBar
17417  * @param {Object} config The config object
17418  */
17419
17420 Roo.bootstrap.ProgressBar = function(config){
17421     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17422 };
17423
17424 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17425     
17426     aria_valuenow : 0,
17427     aria_valuemin : 0,
17428     aria_valuemax : 100,
17429     label : false,
17430     panel : false,
17431     role : false,
17432     sr_only: false,
17433     
17434     getAutoCreate : function()
17435     {
17436         
17437         var cfg = {
17438             tag: 'div',
17439             cls: 'progress-bar',
17440             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17441         };
17442         
17443         if(this.sr_only){
17444             cfg.cn = {
17445                 tag: 'span',
17446                 cls: 'sr-only',
17447                 html: this.sr_only
17448             }
17449         }
17450         
17451         if(this.role){
17452             cfg.role = this.role;
17453         }
17454         
17455         if(this.aria_valuenow){
17456             cfg['aria-valuenow'] = this.aria_valuenow;
17457         }
17458         
17459         if(this.aria_valuemin){
17460             cfg['aria-valuemin'] = this.aria_valuemin;
17461         }
17462         
17463         if(this.aria_valuemax){
17464             cfg['aria-valuemax'] = this.aria_valuemax;
17465         }
17466         
17467         if(this.label && !this.sr_only){
17468             cfg.html = this.label;
17469         }
17470         
17471         if(this.panel){
17472             cfg.cls += ' progress-bar-' + this.panel;
17473         }
17474         
17475         return cfg;
17476     },
17477     
17478     update : function(aria_valuenow)
17479     {
17480         this.aria_valuenow = aria_valuenow;
17481         
17482         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17483     }
17484    
17485 });
17486
17487  
17488
17489  /*
17490  * - LGPL
17491  *
17492  * column
17493  * 
17494  */
17495
17496 /**
17497  * @class Roo.bootstrap.TabGroup
17498  * @extends Roo.bootstrap.Column
17499  * Bootstrap Column class
17500  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17501  * @cfg {Boolean} carousel true to make the group behave like a carousel
17502  * @cfg {Boolean} bullets show bullets for the panels
17503  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17504  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17505  * @cfg {Boolean} showarrow (true|false) show arrow default true
17506  * 
17507  * @constructor
17508  * Create a new TabGroup
17509  * @param {Object} config The config object
17510  */
17511
17512 Roo.bootstrap.TabGroup = function(config){
17513     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17514     if (!this.navId) {
17515         this.navId = Roo.id();
17516     }
17517     this.tabs = [];
17518     Roo.bootstrap.TabGroup.register(this);
17519     
17520 };
17521
17522 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17523     
17524     carousel : false,
17525     transition : false,
17526     bullets : 0,
17527     timer : 0,
17528     autoslide : false,
17529     slideFn : false,
17530     slideOnTouch : false,
17531     showarrow : true,
17532     
17533     getAutoCreate : function()
17534     {
17535         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17536         
17537         cfg.cls += ' tab-content';
17538         
17539         if (this.carousel) {
17540             cfg.cls += ' carousel slide';
17541             
17542             cfg.cn = [{
17543                cls : 'carousel-inner',
17544                cn : []
17545             }];
17546         
17547             if(this.bullets  && !Roo.isTouch){
17548                 
17549                 var bullets = {
17550                     cls : 'carousel-bullets',
17551                     cn : []
17552                 };
17553                
17554                 if(this.bullets_cls){
17555                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17556                 }
17557                 
17558                 bullets.cn.push({
17559                     cls : 'clear'
17560                 });
17561                 
17562                 cfg.cn[0].cn.push(bullets);
17563             }
17564             
17565             if(this.showarrow){
17566                 cfg.cn[0].cn.push({
17567                     tag : 'div',
17568                     class : 'carousel-arrow',
17569                     cn : [
17570                         {
17571                             tag : 'div',
17572                             class : 'carousel-prev',
17573                             cn : [
17574                                 {
17575                                     tag : 'i',
17576                                     class : 'fa fa-chevron-left'
17577                                 }
17578                             ]
17579                         },
17580                         {
17581                             tag : 'div',
17582                             class : 'carousel-next',
17583                             cn : [
17584                                 {
17585                                     tag : 'i',
17586                                     class : 'fa fa-chevron-right'
17587                                 }
17588                             ]
17589                         }
17590                     ]
17591                 });
17592             }
17593             
17594         }
17595         
17596         return cfg;
17597     },
17598     
17599     initEvents:  function()
17600     {
17601 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17602 //            this.el.on("touchstart", this.onTouchStart, this);
17603 //        }
17604         
17605         if(this.autoslide){
17606             var _this = this;
17607             
17608             this.slideFn = window.setInterval(function() {
17609                 _this.showPanelNext();
17610             }, this.timer);
17611         }
17612         
17613         if(this.showarrow){
17614             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17615             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17616         }
17617         
17618         
17619     },
17620     
17621 //    onTouchStart : function(e, el, o)
17622 //    {
17623 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17624 //            return;
17625 //        }
17626 //        
17627 //        this.showPanelNext();
17628 //    },
17629     
17630     
17631     getChildContainer : function()
17632     {
17633         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17634     },
17635     
17636     /**
17637     * register a Navigation item
17638     * @param {Roo.bootstrap.NavItem} the navitem to add
17639     */
17640     register : function(item)
17641     {
17642         this.tabs.push( item);
17643         item.navId = this.navId; // not really needed..
17644         this.addBullet();
17645     
17646     },
17647     
17648     getActivePanel : function()
17649     {
17650         var r = false;
17651         Roo.each(this.tabs, function(t) {
17652             if (t.active) {
17653                 r = t;
17654                 return false;
17655             }
17656             return null;
17657         });
17658         return r;
17659         
17660     },
17661     getPanelByName : function(n)
17662     {
17663         var r = false;
17664         Roo.each(this.tabs, function(t) {
17665             if (t.tabId == n) {
17666                 r = t;
17667                 return false;
17668             }
17669             return null;
17670         });
17671         return r;
17672     },
17673     indexOfPanel : function(p)
17674     {
17675         var r = false;
17676         Roo.each(this.tabs, function(t,i) {
17677             if (t.tabId == p.tabId) {
17678                 r = i;
17679                 return false;
17680             }
17681             return null;
17682         });
17683         return r;
17684     },
17685     /**
17686      * show a specific panel
17687      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17688      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17689      */
17690     showPanel : function (pan)
17691     {
17692         if(this.transition || typeof(pan) == 'undefined'){
17693             Roo.log("waiting for the transitionend");
17694             return;
17695         }
17696         
17697         if (typeof(pan) == 'number') {
17698             pan = this.tabs[pan];
17699         }
17700         
17701         if (typeof(pan) == 'string') {
17702             pan = this.getPanelByName(pan);
17703         }
17704         
17705         var cur = this.getActivePanel();
17706         
17707         if(!pan || !cur){
17708             Roo.log('pan or acitve pan is undefined');
17709             return false;
17710         }
17711         
17712         if (pan.tabId == this.getActivePanel().tabId) {
17713             return true;
17714         }
17715         
17716         if (false === cur.fireEvent('beforedeactivate')) {
17717             return false;
17718         }
17719         
17720         if(this.bullets > 0 && !Roo.isTouch){
17721             this.setActiveBullet(this.indexOfPanel(pan));
17722         }
17723         
17724         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17725             
17726             this.transition = true;
17727             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17728             var lr = dir == 'next' ? 'left' : 'right';
17729             pan.el.addClass(dir); // or prev
17730             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17731             cur.el.addClass(lr); // or right
17732             pan.el.addClass(lr);
17733             
17734             var _this = this;
17735             cur.el.on('transitionend', function() {
17736                 Roo.log("trans end?");
17737                 
17738                 pan.el.removeClass([lr,dir]);
17739                 pan.setActive(true);
17740                 
17741                 cur.el.removeClass([lr]);
17742                 cur.setActive(false);
17743                 
17744                 _this.transition = false;
17745                 
17746             }, this, { single:  true } );
17747             
17748             return true;
17749         }
17750         
17751         cur.setActive(false);
17752         pan.setActive(true);
17753         
17754         return true;
17755         
17756     },
17757     showPanelNext : function()
17758     {
17759         var i = this.indexOfPanel(this.getActivePanel());
17760         
17761         if (i >= this.tabs.length - 1 && !this.autoslide) {
17762             return;
17763         }
17764         
17765         if (i >= this.tabs.length - 1 && this.autoslide) {
17766             i = -1;
17767         }
17768         
17769         this.showPanel(this.tabs[i+1]);
17770     },
17771     
17772     showPanelPrev : function()
17773     {
17774         var i = this.indexOfPanel(this.getActivePanel());
17775         
17776         if (i  < 1 && !this.autoslide) {
17777             return;
17778         }
17779         
17780         if (i < 1 && this.autoslide) {
17781             i = this.tabs.length;
17782         }
17783         
17784         this.showPanel(this.tabs[i-1]);
17785     },
17786     
17787     
17788     addBullet: function()
17789     {
17790         if(!this.bullets || Roo.isTouch){
17791             return;
17792         }
17793         var ctr = this.el.select('.carousel-bullets',true).first();
17794         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17795         var bullet = ctr.createChild({
17796             cls : 'bullet bullet-' + i
17797         },ctr.dom.lastChild);
17798         
17799         
17800         var _this = this;
17801         
17802         bullet.on('click', (function(e, el, o, ii, t){
17803
17804             e.preventDefault();
17805
17806             this.showPanel(ii);
17807
17808             if(this.autoslide && this.slideFn){
17809                 clearInterval(this.slideFn);
17810                 this.slideFn = window.setInterval(function() {
17811                     _this.showPanelNext();
17812                 }, this.timer);
17813             }
17814
17815         }).createDelegate(this, [i, bullet], true));
17816                 
17817         
17818     },
17819      
17820     setActiveBullet : function(i)
17821     {
17822         if(Roo.isTouch){
17823             return;
17824         }
17825         
17826         Roo.each(this.el.select('.bullet', true).elements, function(el){
17827             el.removeClass('selected');
17828         });
17829
17830         var bullet = this.el.select('.bullet-' + i, true).first();
17831         
17832         if(!bullet){
17833             return;
17834         }
17835         
17836         bullet.addClass('selected');
17837     }
17838     
17839     
17840   
17841 });
17842
17843  
17844
17845  
17846  
17847 Roo.apply(Roo.bootstrap.TabGroup, {
17848     
17849     groups: {},
17850      /**
17851     * register a Navigation Group
17852     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17853     */
17854     register : function(navgrp)
17855     {
17856         this.groups[navgrp.navId] = navgrp;
17857         
17858     },
17859     /**
17860     * fetch a Navigation Group based on the navigation ID
17861     * if one does not exist , it will get created.
17862     * @param {string} the navgroup to add
17863     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17864     */
17865     get: function(navId) {
17866         if (typeof(this.groups[navId]) == 'undefined') {
17867             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17868         }
17869         return this.groups[navId] ;
17870     }
17871     
17872     
17873     
17874 });
17875
17876  /*
17877  * - LGPL
17878  *
17879  * TabPanel
17880  * 
17881  */
17882
17883 /**
17884  * @class Roo.bootstrap.TabPanel
17885  * @extends Roo.bootstrap.Component
17886  * Bootstrap TabPanel class
17887  * @cfg {Boolean} active panel active
17888  * @cfg {String} html panel content
17889  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17890  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17891  * @cfg {String} href click to link..
17892  * 
17893  * 
17894  * @constructor
17895  * Create a new TabPanel
17896  * @param {Object} config The config object
17897  */
17898
17899 Roo.bootstrap.TabPanel = function(config){
17900     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17901     this.addEvents({
17902         /**
17903              * @event changed
17904              * Fires when the active status changes
17905              * @param {Roo.bootstrap.TabPanel} this
17906              * @param {Boolean} state the new state
17907             
17908          */
17909         'changed': true,
17910         /**
17911              * @event beforedeactivate
17912              * Fires before a tab is de-activated - can be used to do validation on a form.
17913              * @param {Roo.bootstrap.TabPanel} this
17914              * @return {Boolean} false if there is an error
17915             
17916          */
17917         'beforedeactivate': true
17918      });
17919     
17920     this.tabId = this.tabId || Roo.id();
17921   
17922 };
17923
17924 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17925     
17926     active: false,
17927     html: false,
17928     tabId: false,
17929     navId : false,
17930     href : '',
17931     
17932     getAutoCreate : function(){
17933         var cfg = {
17934             tag: 'div',
17935             // item is needed for carousel - not sure if it has any effect otherwise
17936             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17937             html: this.html || ''
17938         };
17939         
17940         if(this.active){
17941             cfg.cls += ' active';
17942         }
17943         
17944         if(this.tabId){
17945             cfg.tabId = this.tabId;
17946         }
17947         
17948         
17949         return cfg;
17950     },
17951     
17952     initEvents:  function()
17953     {
17954         var p = this.parent();
17955         
17956         this.navId = this.navId || p.navId;
17957         
17958         if (typeof(this.navId) != 'undefined') {
17959             // not really needed.. but just in case.. parent should be a NavGroup.
17960             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17961             
17962             tg.register(this);
17963             
17964             var i = tg.tabs.length - 1;
17965             
17966             if(this.active && tg.bullets > 0 && i < tg.bullets){
17967                 tg.setActiveBullet(i);
17968             }
17969         }
17970         
17971         this.el.on('click', this.onClick, this);
17972         
17973         if(Roo.isTouch){
17974             this.el.on("touchstart", this.onTouchStart, this);
17975             this.el.on("touchmove", this.onTouchMove, this);
17976             this.el.on("touchend", this.onTouchEnd, this);
17977         }
17978         
17979     },
17980     
17981     onRender : function(ct, position)
17982     {
17983         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17984     },
17985     
17986     setActive : function(state)
17987     {
17988         Roo.log("panel - set active " + this.tabId + "=" + state);
17989         
17990         this.active = state;
17991         if (!state) {
17992             this.el.removeClass('active');
17993             
17994         } else  if (!this.el.hasClass('active')) {
17995             this.el.addClass('active');
17996         }
17997         
17998         this.fireEvent('changed', this, state);
17999     },
18000     
18001     onClick : function(e)
18002     {
18003         e.preventDefault();
18004         
18005         if(!this.href.length){
18006             return;
18007         }
18008         
18009         window.location.href = this.href;
18010     },
18011     
18012     startX : 0,
18013     startY : 0,
18014     endX : 0,
18015     endY : 0,
18016     swiping : false,
18017     
18018     onTouchStart : function(e)
18019     {
18020         this.swiping = false;
18021         
18022         this.startX = e.browserEvent.touches[0].clientX;
18023         this.startY = e.browserEvent.touches[0].clientY;
18024     },
18025     
18026     onTouchMove : function(e)
18027     {
18028         this.swiping = true;
18029         
18030         this.endX = e.browserEvent.touches[0].clientX;
18031         this.endY = e.browserEvent.touches[0].clientY;
18032     },
18033     
18034     onTouchEnd : function(e)
18035     {
18036         if(!this.swiping){
18037             this.onClick(e);
18038             return;
18039         }
18040         
18041         var tabGroup = this.parent();
18042         
18043         if(this.endX > this.startX){ // swiping right
18044             tabGroup.showPanelPrev();
18045             return;
18046         }
18047         
18048         if(this.startX > this.endX){ // swiping left
18049             tabGroup.showPanelNext();
18050             return;
18051         }
18052     }
18053     
18054     
18055 });
18056  
18057
18058  
18059
18060  /*
18061  * - LGPL
18062  *
18063  * DateField
18064  * 
18065  */
18066
18067 /**
18068  * @class Roo.bootstrap.DateField
18069  * @extends Roo.bootstrap.Input
18070  * Bootstrap DateField class
18071  * @cfg {Number} weekStart default 0
18072  * @cfg {String} viewMode default empty, (months|years)
18073  * @cfg {String} minViewMode default empty, (months|years)
18074  * @cfg {Number} startDate default -Infinity
18075  * @cfg {Number} endDate default Infinity
18076  * @cfg {Boolean} todayHighlight default false
18077  * @cfg {Boolean} todayBtn default false
18078  * @cfg {Boolean} calendarWeeks default false
18079  * @cfg {Object} daysOfWeekDisabled default empty
18080  * @cfg {Boolean} singleMode default false (true | false)
18081  * 
18082  * @cfg {Boolean} keyboardNavigation default true
18083  * @cfg {String} language default en
18084  * 
18085  * @constructor
18086  * Create a new DateField
18087  * @param {Object} config The config object
18088  */
18089
18090 Roo.bootstrap.DateField = function(config){
18091     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18092      this.addEvents({
18093             /**
18094              * @event show
18095              * Fires when this field show.
18096              * @param {Roo.bootstrap.DateField} this
18097              * @param {Mixed} date The date value
18098              */
18099             show : true,
18100             /**
18101              * @event show
18102              * Fires when this field hide.
18103              * @param {Roo.bootstrap.DateField} this
18104              * @param {Mixed} date The date value
18105              */
18106             hide : true,
18107             /**
18108              * @event select
18109              * Fires when select a date.
18110              * @param {Roo.bootstrap.DateField} this
18111              * @param {Mixed} date The date value
18112              */
18113             select : true,
18114             /**
18115              * @event beforeselect
18116              * Fires when before select a date.
18117              * @param {Roo.bootstrap.DateField} this
18118              * @param {Mixed} date The date value
18119              */
18120             beforeselect : true
18121         });
18122 };
18123
18124 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18125     
18126     /**
18127      * @cfg {String} format
18128      * The default date format string which can be overriden for localization support.  The format must be
18129      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18130      */
18131     format : "m/d/y",
18132     /**
18133      * @cfg {String} altFormats
18134      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18135      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18136      */
18137     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18138     
18139     weekStart : 0,
18140     
18141     viewMode : '',
18142     
18143     minViewMode : '',
18144     
18145     todayHighlight : false,
18146     
18147     todayBtn: false,
18148     
18149     language: 'en',
18150     
18151     keyboardNavigation: true,
18152     
18153     calendarWeeks: false,
18154     
18155     startDate: -Infinity,
18156     
18157     endDate: Infinity,
18158     
18159     daysOfWeekDisabled: [],
18160     
18161     _events: [],
18162     
18163     singleMode : false,
18164     
18165     UTCDate: function()
18166     {
18167         return new Date(Date.UTC.apply(Date, arguments));
18168     },
18169     
18170     UTCToday: function()
18171     {
18172         var today = new Date();
18173         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18174     },
18175     
18176     getDate: function() {
18177             var d = this.getUTCDate();
18178             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18179     },
18180     
18181     getUTCDate: function() {
18182             return this.date;
18183     },
18184     
18185     setDate: function(d) {
18186             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18187     },
18188     
18189     setUTCDate: function(d) {
18190             this.date = d;
18191             this.setValue(this.formatDate(this.date));
18192     },
18193         
18194     onRender: function(ct, position)
18195     {
18196         
18197         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18198         
18199         this.language = this.language || 'en';
18200         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18201         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18202         
18203         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18204         this.format = this.format || 'm/d/y';
18205         this.isInline = false;
18206         this.isInput = true;
18207         this.component = this.el.select('.add-on', true).first() || false;
18208         this.component = (this.component && this.component.length === 0) ? false : this.component;
18209         this.hasInput = this.component && this.inputEl().length;
18210         
18211         if (typeof(this.minViewMode === 'string')) {
18212             switch (this.minViewMode) {
18213                 case 'months':
18214                     this.minViewMode = 1;
18215                     break;
18216                 case 'years':
18217                     this.minViewMode = 2;
18218                     break;
18219                 default:
18220                     this.minViewMode = 0;
18221                     break;
18222             }
18223         }
18224         
18225         if (typeof(this.viewMode === 'string')) {
18226             switch (this.viewMode) {
18227                 case 'months':
18228                     this.viewMode = 1;
18229                     break;
18230                 case 'years':
18231                     this.viewMode = 2;
18232                     break;
18233                 default:
18234                     this.viewMode = 0;
18235                     break;
18236             }
18237         }
18238                 
18239         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18240         
18241 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18242         
18243         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18244         
18245         this.picker().on('mousedown', this.onMousedown, this);
18246         this.picker().on('click', this.onClick, this);
18247         
18248         this.picker().addClass('datepicker-dropdown');
18249         
18250         this.startViewMode = this.viewMode;
18251         
18252         if(this.singleMode){
18253             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18254                 v.setVisibilityMode(Roo.Element.DISPLAY);
18255                 v.hide();
18256             });
18257             
18258             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18259                 v.setStyle('width', '189px');
18260             });
18261         }
18262         
18263         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18264             if(!this.calendarWeeks){
18265                 v.remove();
18266                 return;
18267             }
18268             
18269             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18270             v.attr('colspan', function(i, val){
18271                 return parseInt(val) + 1;
18272             });
18273         });
18274                         
18275         
18276         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18277         
18278         this.setStartDate(this.startDate);
18279         this.setEndDate(this.endDate);
18280         
18281         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18282         
18283         this.fillDow();
18284         this.fillMonths();
18285         this.update();
18286         this.showMode();
18287         
18288         if(this.isInline) {
18289             this.show();
18290         }
18291     },
18292     
18293     picker : function()
18294     {
18295         return this.pickerEl;
18296 //        return this.el.select('.datepicker', true).first();
18297     },
18298     
18299     fillDow: function()
18300     {
18301         var dowCnt = this.weekStart;
18302         
18303         var dow = {
18304             tag: 'tr',
18305             cn: [
18306                 
18307             ]
18308         };
18309         
18310         if(this.calendarWeeks){
18311             dow.cn.push({
18312                 tag: 'th',
18313                 cls: 'cw',
18314                 html: '&nbsp;'
18315             })
18316         }
18317         
18318         while (dowCnt < this.weekStart + 7) {
18319             dow.cn.push({
18320                 tag: 'th',
18321                 cls: 'dow',
18322                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18323             });
18324         }
18325         
18326         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18327     },
18328     
18329     fillMonths: function()
18330     {    
18331         var i = 0;
18332         var months = this.picker().select('>.datepicker-months td', true).first();
18333         
18334         months.dom.innerHTML = '';
18335         
18336         while (i < 12) {
18337             var month = {
18338                 tag: 'span',
18339                 cls: 'month',
18340                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18341             };
18342             
18343             months.createChild(month);
18344         }
18345         
18346     },
18347     
18348     update: function()
18349     {
18350         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;
18351         
18352         if (this.date < this.startDate) {
18353             this.viewDate = new Date(this.startDate);
18354         } else if (this.date > this.endDate) {
18355             this.viewDate = new Date(this.endDate);
18356         } else {
18357             this.viewDate = new Date(this.date);
18358         }
18359         
18360         this.fill();
18361     },
18362     
18363     fill: function() 
18364     {
18365         var d = new Date(this.viewDate),
18366                 year = d.getUTCFullYear(),
18367                 month = d.getUTCMonth(),
18368                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18369                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18370                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18371                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18372                 currentDate = this.date && this.date.valueOf(),
18373                 today = this.UTCToday();
18374         
18375         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18376         
18377 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18378         
18379 //        this.picker.select('>tfoot th.today').
18380 //                                              .text(dates[this.language].today)
18381 //                                              .toggle(this.todayBtn !== false);
18382     
18383         this.updateNavArrows();
18384         this.fillMonths();
18385                                                 
18386         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18387         
18388         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18389          
18390         prevMonth.setUTCDate(day);
18391         
18392         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18393         
18394         var nextMonth = new Date(prevMonth);
18395         
18396         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18397         
18398         nextMonth = nextMonth.valueOf();
18399         
18400         var fillMonths = false;
18401         
18402         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18403         
18404         while(prevMonth.valueOf() < nextMonth) {
18405             var clsName = '';
18406             
18407             if (prevMonth.getUTCDay() === this.weekStart) {
18408                 if(fillMonths){
18409                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18410                 }
18411                     
18412                 fillMonths = {
18413                     tag: 'tr',
18414                     cn: []
18415                 };
18416                 
18417                 if(this.calendarWeeks){
18418                     // ISO 8601: First week contains first thursday.
18419                     // ISO also states week starts on Monday, but we can be more abstract here.
18420                     var
18421                     // Start of current week: based on weekstart/current date
18422                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18423                     // Thursday of this week
18424                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18425                     // First Thursday of year, year from thursday
18426                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18427                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18428                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18429                     
18430                     fillMonths.cn.push({
18431                         tag: 'td',
18432                         cls: 'cw',
18433                         html: calWeek
18434                     });
18435                 }
18436             }
18437             
18438             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18439                 clsName += ' old';
18440             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18441                 clsName += ' new';
18442             }
18443             if (this.todayHighlight &&
18444                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18445                 prevMonth.getUTCMonth() == today.getMonth() &&
18446                 prevMonth.getUTCDate() == today.getDate()) {
18447                 clsName += ' today';
18448             }
18449             
18450             if (currentDate && prevMonth.valueOf() === currentDate) {
18451                 clsName += ' active';
18452             }
18453             
18454             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18455                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18456                     clsName += ' disabled';
18457             }
18458             
18459             fillMonths.cn.push({
18460                 tag: 'td',
18461                 cls: 'day ' + clsName,
18462                 html: prevMonth.getDate()
18463             });
18464             
18465             prevMonth.setDate(prevMonth.getDate()+1);
18466         }
18467           
18468         var currentYear = this.date && this.date.getUTCFullYear();
18469         var currentMonth = this.date && this.date.getUTCMonth();
18470         
18471         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18472         
18473         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18474             v.removeClass('active');
18475             
18476             if(currentYear === year && k === currentMonth){
18477                 v.addClass('active');
18478             }
18479             
18480             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18481                 v.addClass('disabled');
18482             }
18483             
18484         });
18485         
18486         
18487         year = parseInt(year/10, 10) * 10;
18488         
18489         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18490         
18491         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18492         
18493         year -= 1;
18494         for (var i = -1; i < 11; i++) {
18495             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18496                 tag: 'span',
18497                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18498                 html: year
18499             });
18500             
18501             year += 1;
18502         }
18503     },
18504     
18505     showMode: function(dir) 
18506     {
18507         if (dir) {
18508             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18509         }
18510         
18511         Roo.each(this.picker().select('>div',true).elements, function(v){
18512             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18513             v.hide();
18514         });
18515         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18516     },
18517     
18518     place: function()
18519     {
18520         if(this.isInline) {
18521             return;
18522         }
18523         
18524         this.picker().removeClass(['bottom', 'top']);
18525         
18526         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18527             /*
18528              * place to the top of element!
18529              *
18530              */
18531             
18532             this.picker().addClass('top');
18533             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18534             
18535             return;
18536         }
18537         
18538         this.picker().addClass('bottom');
18539         
18540         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18541     },
18542     
18543     parseDate : function(value)
18544     {
18545         if(!value || value instanceof Date){
18546             return value;
18547         }
18548         var v = Date.parseDate(value, this.format);
18549         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18550             v = Date.parseDate(value, 'Y-m-d');
18551         }
18552         if(!v && this.altFormats){
18553             if(!this.altFormatsArray){
18554                 this.altFormatsArray = this.altFormats.split("|");
18555             }
18556             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18557                 v = Date.parseDate(value, this.altFormatsArray[i]);
18558             }
18559         }
18560         return v;
18561     },
18562     
18563     formatDate : function(date, fmt)
18564     {   
18565         return (!date || !(date instanceof Date)) ?
18566         date : date.dateFormat(fmt || this.format);
18567     },
18568     
18569     onFocus : function()
18570     {
18571         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18572         this.show();
18573     },
18574     
18575     onBlur : function()
18576     {
18577         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18578         
18579         var d = this.inputEl().getValue();
18580         
18581         this.setValue(d);
18582                 
18583         this.hide();
18584     },
18585     
18586     show : function()
18587     {
18588         this.picker().show();
18589         this.update();
18590         this.place();
18591         
18592         this.fireEvent('show', this, this.date);
18593     },
18594     
18595     hide : function()
18596     {
18597         if(this.isInline) {
18598             return;
18599         }
18600         this.picker().hide();
18601         this.viewMode = this.startViewMode;
18602         this.showMode();
18603         
18604         this.fireEvent('hide', this, this.date);
18605         
18606     },
18607     
18608     onMousedown: function(e)
18609     {
18610         e.stopPropagation();
18611         e.preventDefault();
18612     },
18613     
18614     keyup: function(e)
18615     {
18616         Roo.bootstrap.DateField.superclass.keyup.call(this);
18617         this.update();
18618     },
18619
18620     setValue: function(v)
18621     {
18622         if(this.fireEvent('beforeselect', this, v) !== false){
18623             var d = new Date(this.parseDate(v) ).clearTime();
18624         
18625             if(isNaN(d.getTime())){
18626                 this.date = this.viewDate = '';
18627                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18628                 return;
18629             }
18630
18631             v = this.formatDate(d);
18632
18633             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18634
18635             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18636
18637             this.update();
18638
18639             this.fireEvent('select', this, this.date);
18640         }
18641     },
18642     
18643     getValue: function()
18644     {
18645         return this.formatDate(this.date);
18646     },
18647     
18648     fireKey: function(e)
18649     {
18650         if (!this.picker().isVisible()){
18651             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18652                 this.show();
18653             }
18654             return;
18655         }
18656         
18657         var dateChanged = false,
18658         dir, day, month,
18659         newDate, newViewDate;
18660         
18661         switch(e.keyCode){
18662             case 27: // escape
18663                 this.hide();
18664                 e.preventDefault();
18665                 break;
18666             case 37: // left
18667             case 39: // right
18668                 if (!this.keyboardNavigation) {
18669                     break;
18670                 }
18671                 dir = e.keyCode == 37 ? -1 : 1;
18672                 
18673                 if (e.ctrlKey){
18674                     newDate = this.moveYear(this.date, dir);
18675                     newViewDate = this.moveYear(this.viewDate, dir);
18676                 } else if (e.shiftKey){
18677                     newDate = this.moveMonth(this.date, dir);
18678                     newViewDate = this.moveMonth(this.viewDate, dir);
18679                 } else {
18680                     newDate = new Date(this.date);
18681                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18682                     newViewDate = new Date(this.viewDate);
18683                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18684                 }
18685                 if (this.dateWithinRange(newDate)){
18686                     this.date = newDate;
18687                     this.viewDate = newViewDate;
18688                     this.setValue(this.formatDate(this.date));
18689 //                    this.update();
18690                     e.preventDefault();
18691                     dateChanged = true;
18692                 }
18693                 break;
18694             case 38: // up
18695             case 40: // down
18696                 if (!this.keyboardNavigation) {
18697                     break;
18698                 }
18699                 dir = e.keyCode == 38 ? -1 : 1;
18700                 if (e.ctrlKey){
18701                     newDate = this.moveYear(this.date, dir);
18702                     newViewDate = this.moveYear(this.viewDate, dir);
18703                 } else if (e.shiftKey){
18704                     newDate = this.moveMonth(this.date, dir);
18705                     newViewDate = this.moveMonth(this.viewDate, dir);
18706                 } else {
18707                     newDate = new Date(this.date);
18708                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18709                     newViewDate = new Date(this.viewDate);
18710                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18711                 }
18712                 if (this.dateWithinRange(newDate)){
18713                     this.date = newDate;
18714                     this.viewDate = newViewDate;
18715                     this.setValue(this.formatDate(this.date));
18716 //                    this.update();
18717                     e.preventDefault();
18718                     dateChanged = true;
18719                 }
18720                 break;
18721             case 13: // enter
18722                 this.setValue(this.formatDate(this.date));
18723                 this.hide();
18724                 e.preventDefault();
18725                 break;
18726             case 9: // tab
18727                 this.setValue(this.formatDate(this.date));
18728                 this.hide();
18729                 break;
18730             case 16: // shift
18731             case 17: // ctrl
18732             case 18: // alt
18733                 break;
18734             default :
18735                 this.hide();
18736                 
18737         }
18738     },
18739     
18740     
18741     onClick: function(e) 
18742     {
18743         e.stopPropagation();
18744         e.preventDefault();
18745         
18746         var target = e.getTarget();
18747         
18748         if(target.nodeName.toLowerCase() === 'i'){
18749             target = Roo.get(target).dom.parentNode;
18750         }
18751         
18752         var nodeName = target.nodeName;
18753         var className = target.className;
18754         var html = target.innerHTML;
18755         //Roo.log(nodeName);
18756         
18757         switch(nodeName.toLowerCase()) {
18758             case 'th':
18759                 switch(className) {
18760                     case 'switch':
18761                         this.showMode(1);
18762                         break;
18763                     case 'prev':
18764                     case 'next':
18765                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18766                         switch(this.viewMode){
18767                                 case 0:
18768                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18769                                         break;
18770                                 case 1:
18771                                 case 2:
18772                                         this.viewDate = this.moveYear(this.viewDate, dir);
18773                                         break;
18774                         }
18775                         this.fill();
18776                         break;
18777                     case 'today':
18778                         var date = new Date();
18779                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18780 //                        this.fill()
18781                         this.setValue(this.formatDate(this.date));
18782                         
18783                         this.hide();
18784                         break;
18785                 }
18786                 break;
18787             case 'span':
18788                 if (className.indexOf('disabled') < 0) {
18789                     this.viewDate.setUTCDate(1);
18790                     if (className.indexOf('month') > -1) {
18791                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18792                     } else {
18793                         var year = parseInt(html, 10) || 0;
18794                         this.viewDate.setUTCFullYear(year);
18795                         
18796                     }
18797                     
18798                     if(this.singleMode){
18799                         this.setValue(this.formatDate(this.viewDate));
18800                         this.hide();
18801                         return;
18802                     }
18803                     
18804                     this.showMode(-1);
18805                     this.fill();
18806                 }
18807                 break;
18808                 
18809             case 'td':
18810                 //Roo.log(className);
18811                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18812                     var day = parseInt(html, 10) || 1;
18813                     var year = this.viewDate.getUTCFullYear(),
18814                         month = this.viewDate.getUTCMonth();
18815
18816                     if (className.indexOf('old') > -1) {
18817                         if(month === 0 ){
18818                             month = 11;
18819                             year -= 1;
18820                         }else{
18821                             month -= 1;
18822                         }
18823                     } else if (className.indexOf('new') > -1) {
18824                         if (month == 11) {
18825                             month = 0;
18826                             year += 1;
18827                         } else {
18828                             month += 1;
18829                         }
18830                     }
18831                     //Roo.log([year,month,day]);
18832                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18833                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18834 //                    this.fill();
18835                     //Roo.log(this.formatDate(this.date));
18836                     this.setValue(this.formatDate(this.date));
18837                     this.hide();
18838                 }
18839                 break;
18840         }
18841     },
18842     
18843     setStartDate: function(startDate)
18844     {
18845         this.startDate = startDate || -Infinity;
18846         if (this.startDate !== -Infinity) {
18847             this.startDate = this.parseDate(this.startDate);
18848         }
18849         this.update();
18850         this.updateNavArrows();
18851     },
18852
18853     setEndDate: function(endDate)
18854     {
18855         this.endDate = endDate || Infinity;
18856         if (this.endDate !== Infinity) {
18857             this.endDate = this.parseDate(this.endDate);
18858         }
18859         this.update();
18860         this.updateNavArrows();
18861     },
18862     
18863     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18864     {
18865         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18866         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18867             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18868         }
18869         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18870             return parseInt(d, 10);
18871         });
18872         this.update();
18873         this.updateNavArrows();
18874     },
18875     
18876     updateNavArrows: function() 
18877     {
18878         if(this.singleMode){
18879             return;
18880         }
18881         
18882         var d = new Date(this.viewDate),
18883         year = d.getUTCFullYear(),
18884         month = d.getUTCMonth();
18885         
18886         Roo.each(this.picker().select('.prev', true).elements, function(v){
18887             v.show();
18888             switch (this.viewMode) {
18889                 case 0:
18890
18891                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18892                         v.hide();
18893                     }
18894                     break;
18895                 case 1:
18896                 case 2:
18897                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18898                         v.hide();
18899                     }
18900                     break;
18901             }
18902         });
18903         
18904         Roo.each(this.picker().select('.next', true).elements, function(v){
18905             v.show();
18906             switch (this.viewMode) {
18907                 case 0:
18908
18909                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18910                         v.hide();
18911                     }
18912                     break;
18913                 case 1:
18914                 case 2:
18915                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18916                         v.hide();
18917                     }
18918                     break;
18919             }
18920         })
18921     },
18922     
18923     moveMonth: function(date, dir)
18924     {
18925         if (!dir) {
18926             return date;
18927         }
18928         var new_date = new Date(date.valueOf()),
18929         day = new_date.getUTCDate(),
18930         month = new_date.getUTCMonth(),
18931         mag = Math.abs(dir),
18932         new_month, test;
18933         dir = dir > 0 ? 1 : -1;
18934         if (mag == 1){
18935             test = dir == -1
18936             // If going back one month, make sure month is not current month
18937             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18938             ? function(){
18939                 return new_date.getUTCMonth() == month;
18940             }
18941             // If going forward one month, make sure month is as expected
18942             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18943             : function(){
18944                 return new_date.getUTCMonth() != new_month;
18945             };
18946             new_month = month + dir;
18947             new_date.setUTCMonth(new_month);
18948             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18949             if (new_month < 0 || new_month > 11) {
18950                 new_month = (new_month + 12) % 12;
18951             }
18952         } else {
18953             // For magnitudes >1, move one month at a time...
18954             for (var i=0; i<mag; i++) {
18955                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18956                 new_date = this.moveMonth(new_date, dir);
18957             }
18958             // ...then reset the day, keeping it in the new month
18959             new_month = new_date.getUTCMonth();
18960             new_date.setUTCDate(day);
18961             test = function(){
18962                 return new_month != new_date.getUTCMonth();
18963             };
18964         }
18965         // Common date-resetting loop -- if date is beyond end of month, make it
18966         // end of month
18967         while (test()){
18968             new_date.setUTCDate(--day);
18969             new_date.setUTCMonth(new_month);
18970         }
18971         return new_date;
18972     },
18973
18974     moveYear: function(date, dir)
18975     {
18976         return this.moveMonth(date, dir*12);
18977     },
18978
18979     dateWithinRange: function(date)
18980     {
18981         return date >= this.startDate && date <= this.endDate;
18982     },
18983
18984     
18985     remove: function() 
18986     {
18987         this.picker().remove();
18988     },
18989     
18990     validateValue : function(value)
18991     {
18992         if(value.length < 1)  {
18993             if(this.allowBlank){
18994                 return true;
18995             }
18996             return false;
18997         }
18998         
18999         if(value.length < this.minLength){
19000             return false;
19001         }
19002         if(value.length > this.maxLength){
19003             return false;
19004         }
19005         if(this.vtype){
19006             var vt = Roo.form.VTypes;
19007             if(!vt[this.vtype](value, this)){
19008                 return false;
19009             }
19010         }
19011         if(typeof this.validator == "function"){
19012             var msg = this.validator(value);
19013             if(msg !== true){
19014                 return false;
19015             }
19016         }
19017         
19018         if(this.regex && !this.regex.test(value)){
19019             return false;
19020         }
19021         
19022         if(typeof(this.parseDate(value)) == 'undefined'){
19023             return false;
19024         }
19025         
19026         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19027             return false;
19028         }      
19029         
19030         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19031             return false;
19032         } 
19033         
19034         
19035         return true;
19036     }
19037    
19038 });
19039
19040 Roo.apply(Roo.bootstrap.DateField,  {
19041     
19042     head : {
19043         tag: 'thead',
19044         cn: [
19045         {
19046             tag: 'tr',
19047             cn: [
19048             {
19049                 tag: 'th',
19050                 cls: 'prev',
19051                 html: '<i class="fa fa-arrow-left"/>'
19052             },
19053             {
19054                 tag: 'th',
19055                 cls: 'switch',
19056                 colspan: '5'
19057             },
19058             {
19059                 tag: 'th',
19060                 cls: 'next',
19061                 html: '<i class="fa fa-arrow-right"/>'
19062             }
19063
19064             ]
19065         }
19066         ]
19067     },
19068     
19069     content : {
19070         tag: 'tbody',
19071         cn: [
19072         {
19073             tag: 'tr',
19074             cn: [
19075             {
19076                 tag: 'td',
19077                 colspan: '7'
19078             }
19079             ]
19080         }
19081         ]
19082     },
19083     
19084     footer : {
19085         tag: 'tfoot',
19086         cn: [
19087         {
19088             tag: 'tr',
19089             cn: [
19090             {
19091                 tag: 'th',
19092                 colspan: '7',
19093                 cls: 'today'
19094             }
19095                     
19096             ]
19097         }
19098         ]
19099     },
19100     
19101     dates:{
19102         en: {
19103             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19104             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19105             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19106             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19107             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19108             today: "Today"
19109         }
19110     },
19111     
19112     modes: [
19113     {
19114         clsName: 'days',
19115         navFnc: 'Month',
19116         navStep: 1
19117     },
19118     {
19119         clsName: 'months',
19120         navFnc: 'FullYear',
19121         navStep: 1
19122     },
19123     {
19124         clsName: 'years',
19125         navFnc: 'FullYear',
19126         navStep: 10
19127     }]
19128 });
19129
19130 Roo.apply(Roo.bootstrap.DateField,  {
19131   
19132     template : {
19133         tag: 'div',
19134         cls: 'datepicker dropdown-menu roo-dynamic',
19135         cn: [
19136         {
19137             tag: 'div',
19138             cls: 'datepicker-days',
19139             cn: [
19140             {
19141                 tag: 'table',
19142                 cls: 'table-condensed',
19143                 cn:[
19144                 Roo.bootstrap.DateField.head,
19145                 {
19146                     tag: 'tbody'
19147                 },
19148                 Roo.bootstrap.DateField.footer
19149                 ]
19150             }
19151             ]
19152         },
19153         {
19154             tag: 'div',
19155             cls: 'datepicker-months',
19156             cn: [
19157             {
19158                 tag: 'table',
19159                 cls: 'table-condensed',
19160                 cn:[
19161                 Roo.bootstrap.DateField.head,
19162                 Roo.bootstrap.DateField.content,
19163                 Roo.bootstrap.DateField.footer
19164                 ]
19165             }
19166             ]
19167         },
19168         {
19169             tag: 'div',
19170             cls: 'datepicker-years',
19171             cn: [
19172             {
19173                 tag: 'table',
19174                 cls: 'table-condensed',
19175                 cn:[
19176                 Roo.bootstrap.DateField.head,
19177                 Roo.bootstrap.DateField.content,
19178                 Roo.bootstrap.DateField.footer
19179                 ]
19180             }
19181             ]
19182         }
19183         ]
19184     }
19185 });
19186
19187  
19188
19189  /*
19190  * - LGPL
19191  *
19192  * TimeField
19193  * 
19194  */
19195
19196 /**
19197  * @class Roo.bootstrap.TimeField
19198  * @extends Roo.bootstrap.Input
19199  * Bootstrap DateField class
19200  * 
19201  * 
19202  * @constructor
19203  * Create a new TimeField
19204  * @param {Object} config The config object
19205  */
19206
19207 Roo.bootstrap.TimeField = function(config){
19208     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19209     this.addEvents({
19210             /**
19211              * @event show
19212              * Fires when this field show.
19213              * @param {Roo.bootstrap.DateField} thisthis
19214              * @param {Mixed} date The date value
19215              */
19216             show : true,
19217             /**
19218              * @event show
19219              * Fires when this field hide.
19220              * @param {Roo.bootstrap.DateField} this
19221              * @param {Mixed} date The date value
19222              */
19223             hide : true,
19224             /**
19225              * @event select
19226              * Fires when select a date.
19227              * @param {Roo.bootstrap.DateField} this
19228              * @param {Mixed} date The date value
19229              */
19230             select : true
19231         });
19232 };
19233
19234 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19235     
19236     /**
19237      * @cfg {String} format
19238      * The default time format string which can be overriden for localization support.  The format must be
19239      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19240      */
19241     format : "H:i",
19242        
19243     onRender: function(ct, position)
19244     {
19245         
19246         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19247                 
19248         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19249         
19250         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19251         
19252         this.pop = this.picker().select('>.datepicker-time',true).first();
19253         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19254         
19255         this.picker().on('mousedown', this.onMousedown, this);
19256         this.picker().on('click', this.onClick, this);
19257         
19258         this.picker().addClass('datepicker-dropdown');
19259     
19260         this.fillTime();
19261         this.update();
19262             
19263         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19264         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19265         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19266         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19267         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19268         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19269
19270     },
19271     
19272     fireKey: function(e){
19273         if (!this.picker().isVisible()){
19274             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19275                 this.show();
19276             }
19277             return;
19278         }
19279
19280         e.preventDefault();
19281         
19282         switch(e.keyCode){
19283             case 27: // escape
19284                 this.hide();
19285                 break;
19286             case 37: // left
19287             case 39: // right
19288                 this.onTogglePeriod();
19289                 break;
19290             case 38: // up
19291                 this.onIncrementMinutes();
19292                 break;
19293             case 40: // down
19294                 this.onDecrementMinutes();
19295                 break;
19296             case 13: // enter
19297             case 9: // tab
19298                 this.setTime();
19299                 break;
19300         }
19301     },
19302     
19303     onClick: function(e) {
19304         e.stopPropagation();
19305         e.preventDefault();
19306     },
19307     
19308     picker : function()
19309     {
19310         return this.el.select('.datepicker', true).first();
19311     },
19312     
19313     fillTime: function()
19314     {    
19315         var time = this.pop.select('tbody', true).first();
19316         
19317         time.dom.innerHTML = '';
19318         
19319         time.createChild({
19320             tag: 'tr',
19321             cn: [
19322                 {
19323                     tag: 'td',
19324                     cn: [
19325                         {
19326                             tag: 'a',
19327                             href: '#',
19328                             cls: 'btn',
19329                             cn: [
19330                                 {
19331                                     tag: 'span',
19332                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19333                                 }
19334                             ]
19335                         } 
19336                     ]
19337                 },
19338                 {
19339                     tag: 'td',
19340                     cls: 'separator'
19341                 },
19342                 {
19343                     tag: 'td',
19344                     cn: [
19345                         {
19346                             tag: 'a',
19347                             href: '#',
19348                             cls: 'btn',
19349                             cn: [
19350                                 {
19351                                     tag: 'span',
19352                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19353                                 }
19354                             ]
19355                         }
19356                     ]
19357                 },
19358                 {
19359                     tag: 'td',
19360                     cls: 'separator'
19361                 }
19362             ]
19363         });
19364         
19365         time.createChild({
19366             tag: 'tr',
19367             cn: [
19368                 {
19369                     tag: 'td',
19370                     cn: [
19371                         {
19372                             tag: 'span',
19373                             cls: 'timepicker-hour',
19374                             html: '00'
19375                         }  
19376                     ]
19377                 },
19378                 {
19379                     tag: 'td',
19380                     cls: 'separator',
19381                     html: ':'
19382                 },
19383                 {
19384                     tag: 'td',
19385                     cn: [
19386                         {
19387                             tag: 'span',
19388                             cls: 'timepicker-minute',
19389                             html: '00'
19390                         }  
19391                     ]
19392                 },
19393                 {
19394                     tag: 'td',
19395                     cls: 'separator'
19396                 },
19397                 {
19398                     tag: 'td',
19399                     cn: [
19400                         {
19401                             tag: 'button',
19402                             type: 'button',
19403                             cls: 'btn btn-primary period',
19404                             html: 'AM'
19405                             
19406                         }
19407                     ]
19408                 }
19409             ]
19410         });
19411         
19412         time.createChild({
19413             tag: 'tr',
19414             cn: [
19415                 {
19416                     tag: 'td',
19417                     cn: [
19418                         {
19419                             tag: 'a',
19420                             href: '#',
19421                             cls: 'btn',
19422                             cn: [
19423                                 {
19424                                     tag: 'span',
19425                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19426                                 }
19427                             ]
19428                         }
19429                     ]
19430                 },
19431                 {
19432                     tag: 'td',
19433                     cls: 'separator'
19434                 },
19435                 {
19436                     tag: 'td',
19437                     cn: [
19438                         {
19439                             tag: 'a',
19440                             href: '#',
19441                             cls: 'btn',
19442                             cn: [
19443                                 {
19444                                     tag: 'span',
19445                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19446                                 }
19447                             ]
19448                         }
19449                     ]
19450                 },
19451                 {
19452                     tag: 'td',
19453                     cls: 'separator'
19454                 }
19455             ]
19456         });
19457         
19458     },
19459     
19460     update: function()
19461     {
19462         
19463         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19464         
19465         this.fill();
19466     },
19467     
19468     fill: function() 
19469     {
19470         var hours = this.time.getHours();
19471         var minutes = this.time.getMinutes();
19472         var period = 'AM';
19473         
19474         if(hours > 11){
19475             period = 'PM';
19476         }
19477         
19478         if(hours == 0){
19479             hours = 12;
19480         }
19481         
19482         
19483         if(hours > 12){
19484             hours = hours - 12;
19485         }
19486         
19487         if(hours < 10){
19488             hours = '0' + hours;
19489         }
19490         
19491         if(minutes < 10){
19492             minutes = '0' + minutes;
19493         }
19494         
19495         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19496         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19497         this.pop.select('button', true).first().dom.innerHTML = period;
19498         
19499     },
19500     
19501     place: function()
19502     {   
19503         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19504         
19505         var cls = ['bottom'];
19506         
19507         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19508             cls.pop();
19509             cls.push('top');
19510         }
19511         
19512         cls.push('right');
19513         
19514         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19515             cls.pop();
19516             cls.push('left');
19517         }
19518         
19519         this.picker().addClass(cls.join('-'));
19520         
19521         var _this = this;
19522         
19523         Roo.each(cls, function(c){
19524             if(c == 'bottom'){
19525                 _this.picker().setTop(_this.inputEl().getHeight());
19526                 return;
19527             }
19528             if(c == 'top'){
19529                 _this.picker().setTop(0 - _this.picker().getHeight());
19530                 return;
19531             }
19532             
19533             if(c == 'left'){
19534                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19535                 return;
19536             }
19537             if(c == 'right'){
19538                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19539                 return;
19540             }
19541         });
19542         
19543     },
19544   
19545     onFocus : function()
19546     {
19547         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19548         this.show();
19549     },
19550     
19551     onBlur : function()
19552     {
19553         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19554         this.hide();
19555     },
19556     
19557     show : function()
19558     {
19559         this.picker().show();
19560         this.pop.show();
19561         this.update();
19562         this.place();
19563         
19564         this.fireEvent('show', this, this.date);
19565     },
19566     
19567     hide : function()
19568     {
19569         this.picker().hide();
19570         this.pop.hide();
19571         
19572         this.fireEvent('hide', this, this.date);
19573     },
19574     
19575     setTime : function()
19576     {
19577         this.hide();
19578         this.setValue(this.time.format(this.format));
19579         
19580         this.fireEvent('select', this, this.date);
19581         
19582         
19583     },
19584     
19585     onMousedown: function(e){
19586         e.stopPropagation();
19587         e.preventDefault();
19588     },
19589     
19590     onIncrementHours: function()
19591     {
19592         Roo.log('onIncrementHours');
19593         this.time = this.time.add(Date.HOUR, 1);
19594         this.update();
19595         
19596     },
19597     
19598     onDecrementHours: function()
19599     {
19600         Roo.log('onDecrementHours');
19601         this.time = this.time.add(Date.HOUR, -1);
19602         this.update();
19603     },
19604     
19605     onIncrementMinutes: function()
19606     {
19607         Roo.log('onIncrementMinutes');
19608         this.time = this.time.add(Date.MINUTE, 1);
19609         this.update();
19610     },
19611     
19612     onDecrementMinutes: function()
19613     {
19614         Roo.log('onDecrementMinutes');
19615         this.time = this.time.add(Date.MINUTE, -1);
19616         this.update();
19617     },
19618     
19619     onTogglePeriod: function()
19620     {
19621         Roo.log('onTogglePeriod');
19622         this.time = this.time.add(Date.HOUR, 12);
19623         this.update();
19624     }
19625     
19626    
19627 });
19628
19629 Roo.apply(Roo.bootstrap.TimeField,  {
19630     
19631     content : {
19632         tag: 'tbody',
19633         cn: [
19634             {
19635                 tag: 'tr',
19636                 cn: [
19637                 {
19638                     tag: 'td',
19639                     colspan: '7'
19640                 }
19641                 ]
19642             }
19643         ]
19644     },
19645     
19646     footer : {
19647         tag: 'tfoot',
19648         cn: [
19649             {
19650                 tag: 'tr',
19651                 cn: [
19652                 {
19653                     tag: 'th',
19654                     colspan: '7',
19655                     cls: '',
19656                     cn: [
19657                         {
19658                             tag: 'button',
19659                             cls: 'btn btn-info ok',
19660                             html: 'OK'
19661                         }
19662                     ]
19663                 }
19664
19665                 ]
19666             }
19667         ]
19668     }
19669 });
19670
19671 Roo.apply(Roo.bootstrap.TimeField,  {
19672   
19673     template : {
19674         tag: 'div',
19675         cls: 'datepicker dropdown-menu',
19676         cn: [
19677             {
19678                 tag: 'div',
19679                 cls: 'datepicker-time',
19680                 cn: [
19681                 {
19682                     tag: 'table',
19683                     cls: 'table-condensed',
19684                     cn:[
19685                     Roo.bootstrap.TimeField.content,
19686                     Roo.bootstrap.TimeField.footer
19687                     ]
19688                 }
19689                 ]
19690             }
19691         ]
19692     }
19693 });
19694
19695  
19696
19697  /*
19698  * - LGPL
19699  *
19700  * MonthField
19701  * 
19702  */
19703
19704 /**
19705  * @class Roo.bootstrap.MonthField
19706  * @extends Roo.bootstrap.Input
19707  * Bootstrap MonthField class
19708  * 
19709  * @cfg {String} language default en
19710  * 
19711  * @constructor
19712  * Create a new MonthField
19713  * @param {Object} config The config object
19714  */
19715
19716 Roo.bootstrap.MonthField = function(config){
19717     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19718     
19719     this.addEvents({
19720         /**
19721          * @event show
19722          * Fires when this field show.
19723          * @param {Roo.bootstrap.MonthField} this
19724          * @param {Mixed} date The date value
19725          */
19726         show : true,
19727         /**
19728          * @event show
19729          * Fires when this field hide.
19730          * @param {Roo.bootstrap.MonthField} this
19731          * @param {Mixed} date The date value
19732          */
19733         hide : true,
19734         /**
19735          * @event select
19736          * Fires when select a date.
19737          * @param {Roo.bootstrap.MonthField} this
19738          * @param {String} oldvalue The old value
19739          * @param {String} newvalue The new value
19740          */
19741         select : true
19742     });
19743 };
19744
19745 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19746     
19747     onRender: function(ct, position)
19748     {
19749         
19750         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19751         
19752         this.language = this.language || 'en';
19753         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19754         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19755         
19756         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19757         this.isInline = false;
19758         this.isInput = true;
19759         this.component = this.el.select('.add-on', true).first() || false;
19760         this.component = (this.component && this.component.length === 0) ? false : this.component;
19761         this.hasInput = this.component && this.inputEL().length;
19762         
19763         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19764         
19765         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19766         
19767         this.picker().on('mousedown', this.onMousedown, this);
19768         this.picker().on('click', this.onClick, this);
19769         
19770         this.picker().addClass('datepicker-dropdown');
19771         
19772         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19773             v.setStyle('width', '189px');
19774         });
19775         
19776         this.fillMonths();
19777         
19778         this.update();
19779         
19780         if(this.isInline) {
19781             this.show();
19782         }
19783         
19784     },
19785     
19786     setValue: function(v, suppressEvent)
19787     {   
19788         var o = this.getValue();
19789         
19790         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19791         
19792         this.update();
19793
19794         if(suppressEvent !== true){
19795             this.fireEvent('select', this, o, v);
19796         }
19797         
19798     },
19799     
19800     getValue: function()
19801     {
19802         return this.value;
19803     },
19804     
19805     onClick: function(e) 
19806     {
19807         e.stopPropagation();
19808         e.preventDefault();
19809         
19810         var target = e.getTarget();
19811         
19812         if(target.nodeName.toLowerCase() === 'i'){
19813             target = Roo.get(target).dom.parentNode;
19814         }
19815         
19816         var nodeName = target.nodeName;
19817         var className = target.className;
19818         var html = target.innerHTML;
19819         
19820         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19821             return;
19822         }
19823         
19824         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19825         
19826         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19827         
19828         this.hide();
19829                         
19830     },
19831     
19832     picker : function()
19833     {
19834         return this.pickerEl;
19835     },
19836     
19837     fillMonths: function()
19838     {    
19839         var i = 0;
19840         var months = this.picker().select('>.datepicker-months td', true).first();
19841         
19842         months.dom.innerHTML = '';
19843         
19844         while (i < 12) {
19845             var month = {
19846                 tag: 'span',
19847                 cls: 'month',
19848                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19849             };
19850             
19851             months.createChild(month);
19852         }
19853         
19854     },
19855     
19856     update: function()
19857     {
19858         var _this = this;
19859         
19860         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19861             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19862         }
19863         
19864         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19865             e.removeClass('active');
19866             
19867             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19868                 e.addClass('active');
19869             }
19870         })
19871     },
19872     
19873     place: function()
19874     {
19875         if(this.isInline) {
19876             return;
19877         }
19878         
19879         this.picker().removeClass(['bottom', 'top']);
19880         
19881         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19882             /*
19883              * place to the top of element!
19884              *
19885              */
19886             
19887             this.picker().addClass('top');
19888             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19889             
19890             return;
19891         }
19892         
19893         this.picker().addClass('bottom');
19894         
19895         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19896     },
19897     
19898     onFocus : function()
19899     {
19900         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19901         this.show();
19902     },
19903     
19904     onBlur : function()
19905     {
19906         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19907         
19908         var d = this.inputEl().getValue();
19909         
19910         this.setValue(d);
19911                 
19912         this.hide();
19913     },
19914     
19915     show : function()
19916     {
19917         this.picker().show();
19918         this.picker().select('>.datepicker-months', true).first().show();
19919         this.update();
19920         this.place();
19921         
19922         this.fireEvent('show', this, this.date);
19923     },
19924     
19925     hide : function()
19926     {
19927         if(this.isInline) {
19928             return;
19929         }
19930         this.picker().hide();
19931         this.fireEvent('hide', this, this.date);
19932         
19933     },
19934     
19935     onMousedown: function(e)
19936     {
19937         e.stopPropagation();
19938         e.preventDefault();
19939     },
19940     
19941     keyup: function(e)
19942     {
19943         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19944         this.update();
19945     },
19946
19947     fireKey: function(e)
19948     {
19949         if (!this.picker().isVisible()){
19950             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19951                 this.show();
19952             }
19953             return;
19954         }
19955         
19956         var dir;
19957         
19958         switch(e.keyCode){
19959             case 27: // escape
19960                 this.hide();
19961                 e.preventDefault();
19962                 break;
19963             case 37: // left
19964             case 39: // right
19965                 dir = e.keyCode == 37 ? -1 : 1;
19966                 
19967                 this.vIndex = this.vIndex + dir;
19968                 
19969                 if(this.vIndex < 0){
19970                     this.vIndex = 0;
19971                 }
19972                 
19973                 if(this.vIndex > 11){
19974                     this.vIndex = 11;
19975                 }
19976                 
19977                 if(isNaN(this.vIndex)){
19978                     this.vIndex = 0;
19979                 }
19980                 
19981                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19982                 
19983                 break;
19984             case 38: // up
19985             case 40: // down
19986                 
19987                 dir = e.keyCode == 38 ? -1 : 1;
19988                 
19989                 this.vIndex = this.vIndex + dir * 4;
19990                 
19991                 if(this.vIndex < 0){
19992                     this.vIndex = 0;
19993                 }
19994                 
19995                 if(this.vIndex > 11){
19996                     this.vIndex = 11;
19997                 }
19998                 
19999                 if(isNaN(this.vIndex)){
20000                     this.vIndex = 0;
20001                 }
20002                 
20003                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20004                 break;
20005                 
20006             case 13: // enter
20007                 
20008                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20009                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20010                 }
20011                 
20012                 this.hide();
20013                 e.preventDefault();
20014                 break;
20015             case 9: // tab
20016                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20017                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20018                 }
20019                 this.hide();
20020                 break;
20021             case 16: // shift
20022             case 17: // ctrl
20023             case 18: // alt
20024                 break;
20025             default :
20026                 this.hide();
20027                 
20028         }
20029     },
20030     
20031     remove: function() 
20032     {
20033         this.picker().remove();
20034     }
20035    
20036 });
20037
20038 Roo.apply(Roo.bootstrap.MonthField,  {
20039     
20040     content : {
20041         tag: 'tbody',
20042         cn: [
20043         {
20044             tag: 'tr',
20045             cn: [
20046             {
20047                 tag: 'td',
20048                 colspan: '7'
20049             }
20050             ]
20051         }
20052         ]
20053     },
20054     
20055     dates:{
20056         en: {
20057             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20058             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20059         }
20060     }
20061 });
20062
20063 Roo.apply(Roo.bootstrap.MonthField,  {
20064   
20065     template : {
20066         tag: 'div',
20067         cls: 'datepicker dropdown-menu roo-dynamic',
20068         cn: [
20069             {
20070                 tag: 'div',
20071                 cls: 'datepicker-months',
20072                 cn: [
20073                 {
20074                     tag: 'table',
20075                     cls: 'table-condensed',
20076                     cn:[
20077                         Roo.bootstrap.DateField.content
20078                     ]
20079                 }
20080                 ]
20081             }
20082         ]
20083     }
20084 });
20085
20086  
20087
20088  
20089  /*
20090  * - LGPL
20091  *
20092  * CheckBox
20093  * 
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.CheckBox
20098  * @extends Roo.bootstrap.Input
20099  * Bootstrap CheckBox class
20100  * 
20101  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20102  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20103  * @cfg {String} boxLabel The text that appears beside the checkbox
20104  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20105  * @cfg {Boolean} checked initnal the element
20106  * @cfg {Boolean} inline inline the element (default false)
20107  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20108  * 
20109  * @constructor
20110  * Create a new CheckBox
20111  * @param {Object} config The config object
20112  */
20113
20114 Roo.bootstrap.CheckBox = function(config){
20115     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20116    
20117     this.addEvents({
20118         /**
20119         * @event check
20120         * Fires when the element is checked or unchecked.
20121         * @param {Roo.bootstrap.CheckBox} this This input
20122         * @param {Boolean} checked The new checked value
20123         */
20124        check : true
20125     });
20126     
20127 };
20128
20129 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20130   
20131     inputType: 'checkbox',
20132     inputValue: 1,
20133     valueOff: 0,
20134     boxLabel: false,
20135     checked: false,
20136     weight : false,
20137     inline: false,
20138     
20139     getAutoCreate : function()
20140     {
20141         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20142         
20143         var id = Roo.id();
20144         
20145         var cfg = {};
20146         
20147         cfg.cls = 'form-group ' + this.inputType; //input-group
20148         
20149         if(this.inline){
20150             cfg.cls += ' ' + this.inputType + '-inline';
20151         }
20152         
20153         var input =  {
20154             tag: 'input',
20155             id : id,
20156             type : this.inputType,
20157             value : this.inputValue,
20158             cls : 'roo-' + this.inputType, //'form-box',
20159             placeholder : this.placeholder || ''
20160             
20161         };
20162         
20163         if(this.inputType != 'radio'){
20164             var hidden =  {
20165                 tag: 'input',
20166                 type : 'hidden',
20167                 cls : 'roo-hidden-value',
20168                 value : this.checked ? this.valueOff : this.inputValue
20169             };
20170         }
20171         
20172             
20173         if (this.weight) { // Validity check?
20174             cfg.cls += " " + this.inputType + "-" + this.weight;
20175         }
20176         
20177         if (this.disabled) {
20178             input.disabled=true;
20179         }
20180         
20181         if(this.checked){
20182             input.checked = this.checked;
20183             
20184         }
20185         
20186         
20187         if (this.name) {
20188             
20189             input.name = this.name;
20190             
20191             if(this.inputType != 'radio'){
20192                 hidden.name = this.name;
20193                 input.name = '_hidden_' + this.name;
20194             }
20195         }
20196         
20197         if (this.size) {
20198             input.cls += ' input-' + this.size;
20199         }
20200         
20201         var settings=this;
20202         
20203         ['xs','sm','md','lg'].map(function(size){
20204             if (settings[size]) {
20205                 cfg.cls += ' col-' + size + '-' + settings[size];
20206             }
20207         });
20208         
20209         var inputblock = input;
20210          
20211         if (this.before || this.after) {
20212             
20213             inputblock = {
20214                 cls : 'input-group',
20215                 cn :  [] 
20216             };
20217             
20218             if (this.before) {
20219                 inputblock.cn.push({
20220                     tag :'span',
20221                     cls : 'input-group-addon',
20222                     html : this.before
20223                 });
20224             }
20225             
20226             inputblock.cn.push(input);
20227             
20228             if(this.inputType != 'radio'){
20229                 inputblock.cn.push(hidden);
20230             }
20231             
20232             if (this.after) {
20233                 inputblock.cn.push({
20234                     tag :'span',
20235                     cls : 'input-group-addon',
20236                     html : this.after
20237                 });
20238             }
20239             
20240         }
20241         
20242         if (align ==='left' && this.fieldLabel.length) {
20243 //                Roo.log("left and has label");
20244             cfg.cn = [
20245                 {
20246                     tag: 'label',
20247                     'for' :  id,
20248                     cls : 'control-label',
20249                     html : this.fieldLabel
20250
20251                 },
20252                 {
20253                     cls : "", 
20254                     cn: [
20255                         inputblock
20256                     ]
20257                 }
20258             ];
20259             
20260             if(this.labelWidth > 12){
20261                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20262             }
20263             
20264             if(this.labelWidth < 13 && this.labelmd == 0){
20265                 this.labelmd = this.labelWidth;
20266             }
20267             
20268             if(this.labellg > 0){
20269                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20270                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20271             }
20272             
20273             if(this.labelmd > 0){
20274                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20275                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20276             }
20277             
20278             if(this.labelsm > 0){
20279                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20280                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20281             }
20282             
20283             if(this.labelxs > 0){
20284                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20285                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20286             }
20287             
20288         } else if ( this.fieldLabel.length) {
20289 //                Roo.log(" label");
20290                 cfg.cn = [
20291                    
20292                     {
20293                         tag: this.boxLabel ? 'span' : 'label',
20294                         'for': id,
20295                         cls: 'control-label box-input-label',
20296                         //cls : 'input-group-addon',
20297                         html : this.fieldLabel
20298                         
20299                     },
20300                     
20301                     inputblock
20302                     
20303                 ];
20304
20305         } else {
20306             
20307 //                Roo.log(" no label && no align");
20308                 cfg.cn = [  inputblock ] ;
20309                 
20310                 
20311         }
20312         
20313         if(this.boxLabel){
20314              var boxLabelCfg = {
20315                 tag: 'label',
20316                 //'for': id, // box label is handled by onclick - so no for...
20317                 cls: 'box-label',
20318                 html: this.boxLabel
20319             };
20320             
20321             if(this.tooltip){
20322                 boxLabelCfg.tooltip = this.tooltip;
20323             }
20324              
20325             cfg.cn.push(boxLabelCfg);
20326         }
20327         
20328         if(this.inputType != 'radio'){
20329             cfg.cn.push(hidden);
20330         }
20331         
20332         return cfg;
20333         
20334     },
20335     
20336     /**
20337      * return the real input element.
20338      */
20339     inputEl: function ()
20340     {
20341         return this.el.select('input.roo-' + this.inputType,true).first();
20342     },
20343     hiddenEl: function ()
20344     {
20345         return this.el.select('input.roo-hidden-value',true).first();
20346     },
20347     
20348     labelEl: function()
20349     {
20350         return this.el.select('label.control-label',true).first();
20351     },
20352     /* depricated... */
20353     
20354     label: function()
20355     {
20356         return this.labelEl();
20357     },
20358     
20359     boxLabelEl: function()
20360     {
20361         return this.el.select('label.box-label',true).first();
20362     },
20363     
20364     initEvents : function()
20365     {
20366 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20367         
20368         this.inputEl().on('click', this.onClick,  this);
20369         
20370         if (this.boxLabel) { 
20371             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20372         }
20373         
20374         this.startValue = this.getValue();
20375         
20376         if(this.groupId){
20377             Roo.bootstrap.CheckBox.register(this);
20378         }
20379     },
20380     
20381     onClick : function()
20382     {   
20383         this.setChecked(!this.checked);
20384     },
20385     
20386     setChecked : function(state,suppressEvent)
20387     {
20388         this.startValue = this.getValue();
20389
20390         if(this.inputType == 'radio'){
20391             
20392             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20393                 e.dom.checked = false;
20394             });
20395             
20396             this.inputEl().dom.checked = true;
20397             
20398             this.inputEl().dom.value = this.inputValue;
20399             
20400             if(suppressEvent !== true){
20401                 this.fireEvent('check', this, true);
20402             }
20403             
20404             this.validate();
20405             
20406             return;
20407         }
20408         
20409         this.checked = state;
20410         
20411         this.inputEl().dom.checked = state;
20412         
20413         
20414         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20415         
20416         if(suppressEvent !== true){
20417             this.fireEvent('check', this, state);
20418         }
20419         
20420         this.validate();
20421     },
20422     
20423     getValue : function()
20424     {
20425         if(this.inputType == 'radio'){
20426             return this.getGroupValue();
20427         }
20428         
20429         return this.hiddenEl().dom.value;
20430         
20431     },
20432     
20433     getGroupValue : function()
20434     {
20435         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20436             return '';
20437         }
20438         
20439         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20440     },
20441     
20442     setValue : function(v,suppressEvent)
20443     {
20444         if(this.inputType == 'radio'){
20445             this.setGroupValue(v, suppressEvent);
20446             return;
20447         }
20448         
20449         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20450         
20451         this.validate();
20452     },
20453     
20454     setGroupValue : function(v, suppressEvent)
20455     {
20456         this.startValue = this.getValue();
20457         
20458         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20459             e.dom.checked = false;
20460             
20461             if(e.dom.value == v){
20462                 e.dom.checked = true;
20463             }
20464         });
20465         
20466         if(suppressEvent !== true){
20467             this.fireEvent('check', this, true);
20468         }
20469
20470         this.validate();
20471         
20472         return;
20473     },
20474     
20475     validate : function()
20476     {
20477         if(
20478                 this.disabled || 
20479                 (this.inputType == 'radio' && this.validateRadio()) ||
20480                 (this.inputType == 'checkbox' && this.validateCheckbox())
20481         ){
20482             this.markValid();
20483             return true;
20484         }
20485         
20486         this.markInvalid();
20487         return false;
20488     },
20489     
20490     validateRadio : function()
20491     {
20492         if(this.allowBlank){
20493             return true;
20494         }
20495         
20496         var valid = false;
20497         
20498         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20499             if(!e.dom.checked){
20500                 return;
20501             }
20502             
20503             valid = true;
20504             
20505             return false;
20506         });
20507         
20508         return valid;
20509     },
20510     
20511     validateCheckbox : function()
20512     {
20513         if(!this.groupId){
20514             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20515             //return (this.getValue() == this.inputValue) ? true : false;
20516         }
20517         
20518         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20519         
20520         if(!group){
20521             return false;
20522         }
20523         
20524         var r = false;
20525         
20526         for(var i in group){
20527             if(r){
20528                 break;
20529             }
20530             
20531             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20532         }
20533         
20534         return r;
20535     },
20536     
20537     /**
20538      * Mark this field as valid
20539      */
20540     markValid : function()
20541     {
20542         var _this = this;
20543         
20544         this.fireEvent('valid', this);
20545         
20546         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20547         
20548         if(this.groupId){
20549             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20550         }
20551         
20552         if(label){
20553             label.markValid();
20554         }
20555
20556         if(this.inputType == 'radio'){
20557             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20558                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20559                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20560             });
20561             
20562             return;
20563         }
20564
20565         if(!this.groupId){
20566             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20567             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20568             return;
20569         }
20570         
20571         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20572         
20573         if(!group){
20574             return;
20575         }
20576         
20577         for(var i in group){
20578             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20579             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20580         }
20581     },
20582     
20583      /**
20584      * Mark this field as invalid
20585      * @param {String} msg The validation message
20586      */
20587     markInvalid : function(msg)
20588     {
20589         if(this.allowBlank){
20590             return;
20591         }
20592         
20593         var _this = this;
20594         
20595         this.fireEvent('invalid', this, msg);
20596         
20597         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20598         
20599         if(this.groupId){
20600             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20601         }
20602         
20603         if(label){
20604             label.markInvalid();
20605         }
20606             
20607         if(this.inputType == 'radio'){
20608             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20609                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20610                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20611             });
20612             
20613             return;
20614         }
20615         
20616         if(!this.groupId){
20617             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20618             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20619             return;
20620         }
20621         
20622         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20623         
20624         if(!group){
20625             return;
20626         }
20627         
20628         for(var i in group){
20629             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20630             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20631         }
20632         
20633     },
20634     
20635     clearInvalid : function()
20636     {
20637         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20638         
20639         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20640         
20641         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20642         
20643         if (label) {
20644             label.iconEl.removeClass(label.validClass);
20645             label.iconEl.removeClass(label.invalidClass);
20646         }
20647     },
20648     
20649     disable : function()
20650     {
20651         if(this.inputType != 'radio'){
20652             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20653             return;
20654         }
20655         
20656         var _this = this;
20657         
20658         if(this.rendered){
20659             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660                 _this.getActionEl().addClass(this.disabledClass);
20661                 e.dom.disabled = true;
20662             });
20663         }
20664         
20665         this.disabled = true;
20666         this.fireEvent("disable", this);
20667         return this;
20668     },
20669
20670     enable : function()
20671     {
20672         if(this.inputType != 'radio'){
20673             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20674             return;
20675         }
20676         
20677         var _this = this;
20678         
20679         if(this.rendered){
20680             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20681                 _this.getActionEl().removeClass(this.disabledClass);
20682                 e.dom.disabled = false;
20683             });
20684         }
20685         
20686         this.disabled = false;
20687         this.fireEvent("enable", this);
20688         return this;
20689     }
20690
20691 });
20692
20693 Roo.apply(Roo.bootstrap.CheckBox, {
20694     
20695     groups: {},
20696     
20697      /**
20698     * register a CheckBox Group
20699     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20700     */
20701     register : function(checkbox)
20702     {
20703         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20704             this.groups[checkbox.groupId] = {};
20705         }
20706         
20707         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20708             return;
20709         }
20710         
20711         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20712         
20713     },
20714     /**
20715     * fetch a CheckBox Group based on the group ID
20716     * @param {string} the group ID
20717     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20718     */
20719     get: function(groupId) {
20720         if (typeof(this.groups[groupId]) == 'undefined') {
20721             return false;
20722         }
20723         
20724         return this.groups[groupId] ;
20725     }
20726     
20727     
20728 });
20729 /*
20730  * - LGPL
20731  *
20732  * RadioItem
20733  * 
20734  */
20735
20736 /**
20737  * @class Roo.bootstrap.Radio
20738  * @extends Roo.bootstrap.Component
20739  * Bootstrap Radio class
20740  * @cfg {String} boxLabel - the label associated
20741  * @cfg {String} value - the value of radio
20742  * 
20743  * @constructor
20744  * Create a new Radio
20745  * @param {Object} config The config object
20746  */
20747 Roo.bootstrap.Radio = function(config){
20748     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20749     
20750 };
20751
20752 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20753     
20754     boxLabel : '',
20755     
20756     value : '',
20757     
20758     getAutoCreate : function()
20759     {
20760         var cfg = {
20761             tag : 'div',
20762             cls : 'form-group radio',
20763             cn : [
20764                 {
20765                     tag : 'label',
20766                     cls : 'box-label',
20767                     html : this.boxLabel
20768                 }
20769             ]
20770         };
20771         
20772         return cfg;
20773     },
20774     
20775     initEvents : function() 
20776     {
20777         this.parent().register(this);
20778         
20779         this.el.on('click', this.onClick, this);
20780         
20781     },
20782     
20783     onClick : function()
20784     {
20785         this.setChecked(true);
20786     },
20787     
20788     setChecked : function(state, suppressEvent)
20789     {
20790         this.parent().setValue(this.value, suppressEvent);
20791         
20792     },
20793     
20794     setBoxLabel : function(v)
20795     {
20796         this.boxLabel = v;
20797         
20798         if(this.rendered){
20799             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20800         }
20801     }
20802     
20803 });
20804  
20805
20806  /*
20807  * - LGPL
20808  *
20809  * Input
20810  * 
20811  */
20812
20813 /**
20814  * @class Roo.bootstrap.SecurePass
20815  * @extends Roo.bootstrap.Input
20816  * Bootstrap SecurePass class
20817  *
20818  * 
20819  * @constructor
20820  * Create a new SecurePass
20821  * @param {Object} config The config object
20822  */
20823  
20824 Roo.bootstrap.SecurePass = function (config) {
20825     // these go here, so the translation tool can replace them..
20826     this.errors = {
20827         PwdEmpty: "Please type a password, and then retype it to confirm.",
20828         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20829         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20830         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20831         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20832         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20833         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20834         TooWeak: "Your password is Too Weak."
20835     },
20836     this.meterLabel = "Password strength:";
20837     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20838     this.meterClass = [
20839         "roo-password-meter-tooweak", 
20840         "roo-password-meter-weak", 
20841         "roo-password-meter-medium", 
20842         "roo-password-meter-strong", 
20843         "roo-password-meter-grey"
20844     ];
20845     
20846     this.errors = {};
20847     
20848     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20849 }
20850
20851 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20852     /**
20853      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20854      * {
20855      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20856      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20857      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20858      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20859      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20860      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20861      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20862      * })
20863      */
20864     // private
20865     
20866     meterWidth: 300,
20867     errorMsg :'',    
20868     errors: false,
20869     imageRoot: '/',
20870     /**
20871      * @cfg {String/Object} Label for the strength meter (defaults to
20872      * 'Password strength:')
20873      */
20874     // private
20875     meterLabel: '',
20876     /**
20877      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20878      * ['Weak', 'Medium', 'Strong'])
20879      */
20880     // private    
20881     pwdStrengths: false,    
20882     // private
20883     strength: 0,
20884     // private
20885     _lastPwd: null,
20886     // private
20887     kCapitalLetter: 0,
20888     kSmallLetter: 1,
20889     kDigit: 2,
20890     kPunctuation: 3,
20891     
20892     insecure: false,
20893     // private
20894     initEvents: function ()
20895     {
20896         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20897
20898         if (this.el.is('input[type=password]') && Roo.isSafari) {
20899             this.el.on('keydown', this.SafariOnKeyDown, this);
20900         }
20901
20902         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20903     },
20904     // private
20905     onRender: function (ct, position)
20906     {
20907         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20908         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20909         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20910
20911         this.trigger.createChild({
20912                    cn: [
20913                     {
20914                     //id: 'PwdMeter',
20915                     tag: 'div',
20916                     cls: 'roo-password-meter-grey col-xs-12',
20917                     style: {
20918                         //width: 0,
20919                         //width: this.meterWidth + 'px'                                                
20920                         }
20921                     },
20922                     {                            
20923                          cls: 'roo-password-meter-text'                          
20924                     }
20925                 ]            
20926         });
20927
20928          
20929         if (this.hideTrigger) {
20930             this.trigger.setDisplayed(false);
20931         }
20932         this.setSize(this.width || '', this.height || '');
20933     },
20934     // private
20935     onDestroy: function ()
20936     {
20937         if (this.trigger) {
20938             this.trigger.removeAllListeners();
20939             this.trigger.remove();
20940         }
20941         if (this.wrap) {
20942             this.wrap.remove();
20943         }
20944         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20945     },
20946     // private
20947     checkStrength: function ()
20948     {
20949         var pwd = this.inputEl().getValue();
20950         if (pwd == this._lastPwd) {
20951             return;
20952         }
20953
20954         var strength;
20955         if (this.ClientSideStrongPassword(pwd)) {
20956             strength = 3;
20957         } else if (this.ClientSideMediumPassword(pwd)) {
20958             strength = 2;
20959         } else if (this.ClientSideWeakPassword(pwd)) {
20960             strength = 1;
20961         } else {
20962             strength = 0;
20963         }
20964         
20965         Roo.log('strength1: ' + strength);
20966         
20967         //var pm = this.trigger.child('div/div/div').dom;
20968         var pm = this.trigger.child('div/div');
20969         pm.removeClass(this.meterClass);
20970         pm.addClass(this.meterClass[strength]);
20971                 
20972         
20973         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20974                 
20975         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20976         
20977         this._lastPwd = pwd;
20978     },
20979     reset: function ()
20980     {
20981         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20982         
20983         this._lastPwd = '';
20984         
20985         var pm = this.trigger.child('div/div');
20986         pm.removeClass(this.meterClass);
20987         pm.addClass('roo-password-meter-grey');        
20988         
20989         
20990         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20991         
20992         pt.innerHTML = '';
20993         this.inputEl().dom.type='password';
20994     },
20995     // private
20996     validateValue: function (value)
20997     {
20998         
20999         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21000             return false;
21001         }
21002         if (value.length == 0) {
21003             if (this.allowBlank) {
21004                 this.clearInvalid();
21005                 return true;
21006             }
21007
21008             this.markInvalid(this.errors.PwdEmpty);
21009             this.errorMsg = this.errors.PwdEmpty;
21010             return false;
21011         }
21012         
21013         if(this.insecure){
21014             return true;
21015         }
21016         
21017         if ('[\x21-\x7e]*'.match(value)) {
21018             this.markInvalid(this.errors.PwdBadChar);
21019             this.errorMsg = this.errors.PwdBadChar;
21020             return false;
21021         }
21022         if (value.length < 6) {
21023             this.markInvalid(this.errors.PwdShort);
21024             this.errorMsg = this.errors.PwdShort;
21025             return false;
21026         }
21027         if (value.length > 16) {
21028             this.markInvalid(this.errors.PwdLong);
21029             this.errorMsg = this.errors.PwdLong;
21030             return false;
21031         }
21032         var strength;
21033         if (this.ClientSideStrongPassword(value)) {
21034             strength = 3;
21035         } else if (this.ClientSideMediumPassword(value)) {
21036             strength = 2;
21037         } else if (this.ClientSideWeakPassword(value)) {
21038             strength = 1;
21039         } else {
21040             strength = 0;
21041         }
21042
21043         
21044         if (strength < 2) {
21045             //this.markInvalid(this.errors.TooWeak);
21046             this.errorMsg = this.errors.TooWeak;
21047             //return false;
21048         }
21049         
21050         
21051         console.log('strength2: ' + strength);
21052         
21053         //var pm = this.trigger.child('div/div/div').dom;
21054         
21055         var pm = this.trigger.child('div/div');
21056         pm.removeClass(this.meterClass);
21057         pm.addClass(this.meterClass[strength]);
21058                 
21059         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21060                 
21061         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21062         
21063         this.errorMsg = ''; 
21064         return true;
21065     },
21066     // private
21067     CharacterSetChecks: function (type)
21068     {
21069         this.type = type;
21070         this.fResult = false;
21071     },
21072     // private
21073     isctype: function (character, type)
21074     {
21075         switch (type) {  
21076             case this.kCapitalLetter:
21077                 if (character >= 'A' && character <= 'Z') {
21078                     return true;
21079                 }
21080                 break;
21081             
21082             case this.kSmallLetter:
21083                 if (character >= 'a' && character <= 'z') {
21084                     return true;
21085                 }
21086                 break;
21087             
21088             case this.kDigit:
21089                 if (character >= '0' && character <= '9') {
21090                     return true;
21091                 }
21092                 break;
21093             
21094             case this.kPunctuation:
21095                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21096                     return true;
21097                 }
21098                 break;
21099             
21100             default:
21101                 return false;
21102         }
21103
21104     },
21105     // private
21106     IsLongEnough: function (pwd, size)
21107     {
21108         return !(pwd == null || isNaN(size) || pwd.length < size);
21109     },
21110     // private
21111     SpansEnoughCharacterSets: function (word, nb)
21112     {
21113         if (!this.IsLongEnough(word, nb))
21114         {
21115             return false;
21116         }
21117
21118         var characterSetChecks = new Array(
21119             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21120             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21121         );
21122         
21123         for (var index = 0; index < word.length; ++index) {
21124             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21125                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21126                     characterSetChecks[nCharSet].fResult = true;
21127                     break;
21128                 }
21129             }
21130         }
21131
21132         var nCharSets = 0;
21133         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21134             if (characterSetChecks[nCharSet].fResult) {
21135                 ++nCharSets;
21136             }
21137         }
21138
21139         if (nCharSets < nb) {
21140             return false;
21141         }
21142         return true;
21143     },
21144     // private
21145     ClientSideStrongPassword: function (pwd)
21146     {
21147         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21148     },
21149     // private
21150     ClientSideMediumPassword: function (pwd)
21151     {
21152         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21153     },
21154     // private
21155     ClientSideWeakPassword: function (pwd)
21156     {
21157         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21158     }
21159           
21160 })//<script type="text/javascript">
21161
21162 /*
21163  * Based  Ext JS Library 1.1.1
21164  * Copyright(c) 2006-2007, Ext JS, LLC.
21165  * LGPL
21166  *
21167  */
21168  
21169 /**
21170  * @class Roo.HtmlEditorCore
21171  * @extends Roo.Component
21172  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21173  *
21174  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21175  */
21176
21177 Roo.HtmlEditorCore = function(config){
21178     
21179     
21180     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21181     
21182     
21183     this.addEvents({
21184         /**
21185          * @event initialize
21186          * Fires when the editor is fully initialized (including the iframe)
21187          * @param {Roo.HtmlEditorCore} this
21188          */
21189         initialize: true,
21190         /**
21191          * @event activate
21192          * Fires when the editor is first receives the focus. Any insertion must wait
21193          * until after this event.
21194          * @param {Roo.HtmlEditorCore} this
21195          */
21196         activate: true,
21197          /**
21198          * @event beforesync
21199          * Fires before the textarea is updated with content from the editor iframe. Return false
21200          * to cancel the sync.
21201          * @param {Roo.HtmlEditorCore} this
21202          * @param {String} html
21203          */
21204         beforesync: true,
21205          /**
21206          * @event beforepush
21207          * Fires before the iframe editor is updated with content from the textarea. Return false
21208          * to cancel the push.
21209          * @param {Roo.HtmlEditorCore} this
21210          * @param {String} html
21211          */
21212         beforepush: true,
21213          /**
21214          * @event sync
21215          * Fires when the textarea is updated with content from the editor iframe.
21216          * @param {Roo.HtmlEditorCore} this
21217          * @param {String} html
21218          */
21219         sync: true,
21220          /**
21221          * @event push
21222          * Fires when the iframe editor is updated with content from the textarea.
21223          * @param {Roo.HtmlEditorCore} this
21224          * @param {String} html
21225          */
21226         push: true,
21227         
21228         /**
21229          * @event editorevent
21230          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21231          * @param {Roo.HtmlEditorCore} this
21232          */
21233         editorevent: true
21234         
21235     });
21236     
21237     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21238     
21239     // defaults : white / black...
21240     this.applyBlacklists();
21241     
21242     
21243     
21244 };
21245
21246
21247 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21248
21249
21250      /**
21251      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21252      */
21253     
21254     owner : false,
21255     
21256      /**
21257      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21258      *                        Roo.resizable.
21259      */
21260     resizable : false,
21261      /**
21262      * @cfg {Number} height (in pixels)
21263      */   
21264     height: 300,
21265    /**
21266      * @cfg {Number} width (in pixels)
21267      */   
21268     width: 500,
21269     
21270     /**
21271      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21272      * 
21273      */
21274     stylesheets: false,
21275     
21276     // id of frame..
21277     frameId: false,
21278     
21279     // private properties
21280     validationEvent : false,
21281     deferHeight: true,
21282     initialized : false,
21283     activated : false,
21284     sourceEditMode : false,
21285     onFocus : Roo.emptyFn,
21286     iframePad:3,
21287     hideMode:'offsets',
21288     
21289     clearUp: true,
21290     
21291     // blacklist + whitelisted elements..
21292     black: false,
21293     white: false,
21294      
21295     bodyCls : '',
21296
21297     /**
21298      * Protected method that will not generally be called directly. It
21299      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21300      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21301      */
21302     getDocMarkup : function(){
21303         // body styles..
21304         var st = '';
21305         
21306         // inherit styels from page...?? 
21307         if (this.stylesheets === false) {
21308             
21309             Roo.get(document.head).select('style').each(function(node) {
21310                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21311             });
21312             
21313             Roo.get(document.head).select('link').each(function(node) { 
21314                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21315             });
21316             
21317         } else if (!this.stylesheets.length) {
21318                 // simple..
21319                 st = '<style type="text/css">' +
21320                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21321                    '</style>';
21322         } else { 
21323             st = '<style type="text/css">' +
21324                     this.stylesheets +
21325                 '</style>';
21326         }
21327         
21328         st +=  '<style type="text/css">' +
21329             'IMG { cursor: pointer } ' +
21330         '</style>';
21331
21332         var cls = 'roo-htmleditor-body';
21333         
21334         if(this.bodyCls.length){
21335             cls += ' ' + this.bodyCls;
21336         }
21337         
21338         return '<html><head>' + st  +
21339             //<style type="text/css">' +
21340             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21341             //'</style>' +
21342             ' </head><body class="' +  cls + '"></body></html>';
21343     },
21344
21345     // private
21346     onRender : function(ct, position)
21347     {
21348         var _t = this;
21349         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21350         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21351         
21352         
21353         this.el.dom.style.border = '0 none';
21354         this.el.dom.setAttribute('tabIndex', -1);
21355         this.el.addClass('x-hidden hide');
21356         
21357         
21358         
21359         if(Roo.isIE){ // fix IE 1px bogus margin
21360             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21361         }
21362        
21363         
21364         this.frameId = Roo.id();
21365         
21366          
21367         
21368         var iframe = this.owner.wrap.createChild({
21369             tag: 'iframe',
21370             cls: 'form-control', // bootstrap..
21371             id: this.frameId,
21372             name: this.frameId,
21373             frameBorder : 'no',
21374             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21375         }, this.el
21376         );
21377         
21378         
21379         this.iframe = iframe.dom;
21380
21381          this.assignDocWin();
21382         
21383         this.doc.designMode = 'on';
21384        
21385         this.doc.open();
21386         this.doc.write(this.getDocMarkup());
21387         this.doc.close();
21388
21389         
21390         var task = { // must defer to wait for browser to be ready
21391             run : function(){
21392                 //console.log("run task?" + this.doc.readyState);
21393                 this.assignDocWin();
21394                 if(this.doc.body || this.doc.readyState == 'complete'){
21395                     try {
21396                         this.doc.designMode="on";
21397                     } catch (e) {
21398                         return;
21399                     }
21400                     Roo.TaskMgr.stop(task);
21401                     this.initEditor.defer(10, this);
21402                 }
21403             },
21404             interval : 10,
21405             duration: 10000,
21406             scope: this
21407         };
21408         Roo.TaskMgr.start(task);
21409
21410     },
21411
21412     // private
21413     onResize : function(w, h)
21414     {
21415          Roo.log('resize: ' +w + ',' + h );
21416         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21417         if(!this.iframe){
21418             return;
21419         }
21420         if(typeof w == 'number'){
21421             
21422             this.iframe.style.width = w + 'px';
21423         }
21424         if(typeof h == 'number'){
21425             
21426             this.iframe.style.height = h + 'px';
21427             if(this.doc){
21428                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21429             }
21430         }
21431         
21432     },
21433
21434     /**
21435      * Toggles the editor between standard and source edit mode.
21436      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21437      */
21438     toggleSourceEdit : function(sourceEditMode){
21439         
21440         this.sourceEditMode = sourceEditMode === true;
21441         
21442         if(this.sourceEditMode){
21443  
21444             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21445             
21446         }else{
21447             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21448             //this.iframe.className = '';
21449             this.deferFocus();
21450         }
21451         //this.setSize(this.owner.wrap.getSize());
21452         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21453     },
21454
21455     
21456   
21457
21458     /**
21459      * Protected method that will not generally be called directly. If you need/want
21460      * custom HTML cleanup, this is the method you should override.
21461      * @param {String} html The HTML to be cleaned
21462      * return {String} The cleaned HTML
21463      */
21464     cleanHtml : function(html){
21465         html = String(html);
21466         if(html.length > 5){
21467             if(Roo.isSafari){ // strip safari nonsense
21468                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21469             }
21470         }
21471         if(html == '&nbsp;'){
21472             html = '';
21473         }
21474         return html;
21475     },
21476
21477     /**
21478      * HTML Editor -> Textarea
21479      * Protected method that will not generally be called directly. Syncs the contents
21480      * of the editor iframe with the textarea.
21481      */
21482     syncValue : function(){
21483         if(this.initialized){
21484             var bd = (this.doc.body || this.doc.documentElement);
21485             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21486             var html = bd.innerHTML;
21487             if(Roo.isSafari){
21488                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21489                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21490                 if(m && m[1]){
21491                     html = '<div style="'+m[0]+'">' + html + '</div>';
21492                 }
21493             }
21494             html = this.cleanHtml(html);
21495             // fix up the special chars.. normaly like back quotes in word...
21496             // however we do not want to do this with chinese..
21497             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21498                 var cc = b.charCodeAt();
21499                 if (
21500                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21501                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21502                     (cc >= 0xf900 && cc < 0xfb00 )
21503                 ) {
21504                         return b;
21505                 }
21506                 return "&#"+cc+";" 
21507             });
21508             if(this.owner.fireEvent('beforesync', this, html) !== false){
21509                 this.el.dom.value = html;
21510                 this.owner.fireEvent('sync', this, html);
21511             }
21512         }
21513     },
21514
21515     /**
21516      * Protected method that will not generally be called directly. Pushes the value of the textarea
21517      * into the iframe editor.
21518      */
21519     pushValue : function(){
21520         if(this.initialized){
21521             var v = this.el.dom.value.trim();
21522             
21523 //            if(v.length < 1){
21524 //                v = '&#160;';
21525 //            }
21526             
21527             if(this.owner.fireEvent('beforepush', this, v) !== false){
21528                 var d = (this.doc.body || this.doc.documentElement);
21529                 d.innerHTML = v;
21530                 this.cleanUpPaste();
21531                 this.el.dom.value = d.innerHTML;
21532                 this.owner.fireEvent('push', this, v);
21533             }
21534         }
21535     },
21536
21537     // private
21538     deferFocus : function(){
21539         this.focus.defer(10, this);
21540     },
21541
21542     // doc'ed in Field
21543     focus : function(){
21544         if(this.win && !this.sourceEditMode){
21545             this.win.focus();
21546         }else{
21547             this.el.focus();
21548         }
21549     },
21550     
21551     assignDocWin: function()
21552     {
21553         var iframe = this.iframe;
21554         
21555          if(Roo.isIE){
21556             this.doc = iframe.contentWindow.document;
21557             this.win = iframe.contentWindow;
21558         } else {
21559 //            if (!Roo.get(this.frameId)) {
21560 //                return;
21561 //            }
21562 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21563 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21564             
21565             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21566                 return;
21567             }
21568             
21569             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21570             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21571         }
21572     },
21573     
21574     // private
21575     initEditor : function(){
21576         //console.log("INIT EDITOR");
21577         this.assignDocWin();
21578         
21579         
21580         
21581         this.doc.designMode="on";
21582         this.doc.open();
21583         this.doc.write(this.getDocMarkup());
21584         this.doc.close();
21585         
21586         var dbody = (this.doc.body || this.doc.documentElement);
21587         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21588         // this copies styles from the containing element into thsi one..
21589         // not sure why we need all of this..
21590         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21591         
21592         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21593         //ss['background-attachment'] = 'fixed'; // w3c
21594         dbody.bgProperties = 'fixed'; // ie
21595         //Roo.DomHelper.applyStyles(dbody, ss);
21596         Roo.EventManager.on(this.doc, {
21597             //'mousedown': this.onEditorEvent,
21598             'mouseup': this.onEditorEvent,
21599             'dblclick': this.onEditorEvent,
21600             'click': this.onEditorEvent,
21601             'keyup': this.onEditorEvent,
21602             buffer:100,
21603             scope: this
21604         });
21605         if(Roo.isGecko){
21606             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21607         }
21608         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21609             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21610         }
21611         this.initialized = true;
21612
21613         this.owner.fireEvent('initialize', this);
21614         this.pushValue();
21615     },
21616
21617     // private
21618     onDestroy : function(){
21619         
21620         
21621         
21622         if(this.rendered){
21623             
21624             //for (var i =0; i < this.toolbars.length;i++) {
21625             //    // fixme - ask toolbars for heights?
21626             //    this.toolbars[i].onDestroy();
21627            // }
21628             
21629             //this.wrap.dom.innerHTML = '';
21630             //this.wrap.remove();
21631         }
21632     },
21633
21634     // private
21635     onFirstFocus : function(){
21636         
21637         this.assignDocWin();
21638         
21639         
21640         this.activated = true;
21641          
21642     
21643         if(Roo.isGecko){ // prevent silly gecko errors
21644             this.win.focus();
21645             var s = this.win.getSelection();
21646             if(!s.focusNode || s.focusNode.nodeType != 3){
21647                 var r = s.getRangeAt(0);
21648                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21649                 r.collapse(true);
21650                 this.deferFocus();
21651             }
21652             try{
21653                 this.execCmd('useCSS', true);
21654                 this.execCmd('styleWithCSS', false);
21655             }catch(e){}
21656         }
21657         this.owner.fireEvent('activate', this);
21658     },
21659
21660     // private
21661     adjustFont: function(btn){
21662         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21663         //if(Roo.isSafari){ // safari
21664         //    adjust *= 2;
21665        // }
21666         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21667         if(Roo.isSafari){ // safari
21668             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21669             v =  (v < 10) ? 10 : v;
21670             v =  (v > 48) ? 48 : v;
21671             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21672             
21673         }
21674         
21675         
21676         v = Math.max(1, v+adjust);
21677         
21678         this.execCmd('FontSize', v  );
21679     },
21680
21681     onEditorEvent : function(e)
21682     {
21683         this.owner.fireEvent('editorevent', this, e);
21684       //  this.updateToolbar();
21685         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21686     },
21687
21688     insertTag : function(tg)
21689     {
21690         // could be a bit smarter... -> wrap the current selected tRoo..
21691         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21692             
21693             range = this.createRange(this.getSelection());
21694             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21695             wrappingNode.appendChild(range.extractContents());
21696             range.insertNode(wrappingNode);
21697
21698             return;
21699             
21700             
21701             
21702         }
21703         this.execCmd("formatblock",   tg);
21704         
21705     },
21706     
21707     insertText : function(txt)
21708     {
21709         
21710         
21711         var range = this.createRange();
21712         range.deleteContents();
21713                //alert(Sender.getAttribute('label'));
21714                
21715         range.insertNode(this.doc.createTextNode(txt));
21716     } ,
21717     
21718      
21719
21720     /**
21721      * Executes a Midas editor command on the editor document and performs necessary focus and
21722      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21723      * @param {String} cmd The Midas command
21724      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21725      */
21726     relayCmd : function(cmd, value){
21727         this.win.focus();
21728         this.execCmd(cmd, value);
21729         this.owner.fireEvent('editorevent', this);
21730         //this.updateToolbar();
21731         this.owner.deferFocus();
21732     },
21733
21734     /**
21735      * Executes a Midas editor command directly on the editor document.
21736      * For visual commands, you should use {@link #relayCmd} instead.
21737      * <b>This should only be called after the editor is initialized.</b>
21738      * @param {String} cmd The Midas command
21739      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21740      */
21741     execCmd : function(cmd, value){
21742         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21743         this.syncValue();
21744     },
21745  
21746  
21747    
21748     /**
21749      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21750      * to insert tRoo.
21751      * @param {String} text | dom node.. 
21752      */
21753     insertAtCursor : function(text)
21754     {
21755         
21756         if(!this.activated){
21757             return;
21758         }
21759         /*
21760         if(Roo.isIE){
21761             this.win.focus();
21762             var r = this.doc.selection.createRange();
21763             if(r){
21764                 r.collapse(true);
21765                 r.pasteHTML(text);
21766                 this.syncValue();
21767                 this.deferFocus();
21768             
21769             }
21770             return;
21771         }
21772         */
21773         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21774             this.win.focus();
21775             
21776             
21777             // from jquery ui (MIT licenced)
21778             var range, node;
21779             var win = this.win;
21780             
21781             if (win.getSelection && win.getSelection().getRangeAt) {
21782                 range = win.getSelection().getRangeAt(0);
21783                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21784                 range.insertNode(node);
21785             } else if (win.document.selection && win.document.selection.createRange) {
21786                 // no firefox support
21787                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21788                 win.document.selection.createRange().pasteHTML(txt);
21789             } else {
21790                 // no firefox support
21791                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21792                 this.execCmd('InsertHTML', txt);
21793             } 
21794             
21795             this.syncValue();
21796             
21797             this.deferFocus();
21798         }
21799     },
21800  // private
21801     mozKeyPress : function(e){
21802         if(e.ctrlKey){
21803             var c = e.getCharCode(), cmd;
21804           
21805             if(c > 0){
21806                 c = String.fromCharCode(c).toLowerCase();
21807                 switch(c){
21808                     case 'b':
21809                         cmd = 'bold';
21810                         break;
21811                     case 'i':
21812                         cmd = 'italic';
21813                         break;
21814                     
21815                     case 'u':
21816                         cmd = 'underline';
21817                         break;
21818                     
21819                     case 'v':
21820                         this.cleanUpPaste.defer(100, this);
21821                         return;
21822                         
21823                 }
21824                 if(cmd){
21825                     this.win.focus();
21826                     this.execCmd(cmd);
21827                     this.deferFocus();
21828                     e.preventDefault();
21829                 }
21830                 
21831             }
21832         }
21833     },
21834
21835     // private
21836     fixKeys : function(){ // load time branching for fastest keydown performance
21837         if(Roo.isIE){
21838             return function(e){
21839                 var k = e.getKey(), r;
21840                 if(k == e.TAB){
21841                     e.stopEvent();
21842                     r = this.doc.selection.createRange();
21843                     if(r){
21844                         r.collapse(true);
21845                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21846                         this.deferFocus();
21847                     }
21848                     return;
21849                 }
21850                 
21851                 if(k == e.ENTER){
21852                     r = this.doc.selection.createRange();
21853                     if(r){
21854                         var target = r.parentElement();
21855                         if(!target || target.tagName.toLowerCase() != 'li'){
21856                             e.stopEvent();
21857                             r.pasteHTML('<br />');
21858                             r.collapse(false);
21859                             r.select();
21860                         }
21861                     }
21862                 }
21863                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21864                     this.cleanUpPaste.defer(100, this);
21865                     return;
21866                 }
21867                 
21868                 
21869             };
21870         }else if(Roo.isOpera){
21871             return function(e){
21872                 var k = e.getKey();
21873                 if(k == e.TAB){
21874                     e.stopEvent();
21875                     this.win.focus();
21876                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21877                     this.deferFocus();
21878                 }
21879                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21880                     this.cleanUpPaste.defer(100, this);
21881                     return;
21882                 }
21883                 
21884             };
21885         }else if(Roo.isSafari){
21886             return function(e){
21887                 var k = e.getKey();
21888                 
21889                 if(k == e.TAB){
21890                     e.stopEvent();
21891                     this.execCmd('InsertText','\t');
21892                     this.deferFocus();
21893                     return;
21894                 }
21895                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21896                     this.cleanUpPaste.defer(100, this);
21897                     return;
21898                 }
21899                 
21900              };
21901         }
21902     }(),
21903     
21904     getAllAncestors: function()
21905     {
21906         var p = this.getSelectedNode();
21907         var a = [];
21908         if (!p) {
21909             a.push(p); // push blank onto stack..
21910             p = this.getParentElement();
21911         }
21912         
21913         
21914         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21915             a.push(p);
21916             p = p.parentNode;
21917         }
21918         a.push(this.doc.body);
21919         return a;
21920     },
21921     lastSel : false,
21922     lastSelNode : false,
21923     
21924     
21925     getSelection : function() 
21926     {
21927         this.assignDocWin();
21928         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21929     },
21930     
21931     getSelectedNode: function() 
21932     {
21933         // this may only work on Gecko!!!
21934         
21935         // should we cache this!!!!
21936         
21937         
21938         
21939          
21940         var range = this.createRange(this.getSelection()).cloneRange();
21941         
21942         if (Roo.isIE) {
21943             var parent = range.parentElement();
21944             while (true) {
21945                 var testRange = range.duplicate();
21946                 testRange.moveToElementText(parent);
21947                 if (testRange.inRange(range)) {
21948                     break;
21949                 }
21950                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21951                     break;
21952                 }
21953                 parent = parent.parentElement;
21954             }
21955             return parent;
21956         }
21957         
21958         // is ancestor a text element.
21959         var ac =  range.commonAncestorContainer;
21960         if (ac.nodeType == 3) {
21961             ac = ac.parentNode;
21962         }
21963         
21964         var ar = ac.childNodes;
21965          
21966         var nodes = [];
21967         var other_nodes = [];
21968         var has_other_nodes = false;
21969         for (var i=0;i<ar.length;i++) {
21970             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21971                 continue;
21972             }
21973             // fullly contained node.
21974             
21975             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21976                 nodes.push(ar[i]);
21977                 continue;
21978             }
21979             
21980             // probably selected..
21981             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21982                 other_nodes.push(ar[i]);
21983                 continue;
21984             }
21985             // outer..
21986             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21987                 continue;
21988             }
21989             
21990             
21991             has_other_nodes = true;
21992         }
21993         if (!nodes.length && other_nodes.length) {
21994             nodes= other_nodes;
21995         }
21996         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21997             return false;
21998         }
21999         
22000         return nodes[0];
22001     },
22002     createRange: function(sel)
22003     {
22004         // this has strange effects when using with 
22005         // top toolbar - not sure if it's a great idea.
22006         //this.editor.contentWindow.focus();
22007         if (typeof sel != "undefined") {
22008             try {
22009                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22010             } catch(e) {
22011                 return this.doc.createRange();
22012             }
22013         } else {
22014             return this.doc.createRange();
22015         }
22016     },
22017     getParentElement: function()
22018     {
22019         
22020         this.assignDocWin();
22021         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22022         
22023         var range = this.createRange(sel);
22024          
22025         try {
22026             var p = range.commonAncestorContainer;
22027             while (p.nodeType == 3) { // text node
22028                 p = p.parentNode;
22029             }
22030             return p;
22031         } catch (e) {
22032             return null;
22033         }
22034     
22035     },
22036     /***
22037      *
22038      * Range intersection.. the hard stuff...
22039      *  '-1' = before
22040      *  '0' = hits..
22041      *  '1' = after.
22042      *         [ -- selected range --- ]
22043      *   [fail]                        [fail]
22044      *
22045      *    basically..
22046      *      if end is before start or  hits it. fail.
22047      *      if start is after end or hits it fail.
22048      *
22049      *   if either hits (but other is outside. - then it's not 
22050      *   
22051      *    
22052      **/
22053     
22054     
22055     // @see http://www.thismuchiknow.co.uk/?p=64.
22056     rangeIntersectsNode : function(range, node)
22057     {
22058         var nodeRange = node.ownerDocument.createRange();
22059         try {
22060             nodeRange.selectNode(node);
22061         } catch (e) {
22062             nodeRange.selectNodeContents(node);
22063         }
22064     
22065         var rangeStartRange = range.cloneRange();
22066         rangeStartRange.collapse(true);
22067     
22068         var rangeEndRange = range.cloneRange();
22069         rangeEndRange.collapse(false);
22070     
22071         var nodeStartRange = nodeRange.cloneRange();
22072         nodeStartRange.collapse(true);
22073     
22074         var nodeEndRange = nodeRange.cloneRange();
22075         nodeEndRange.collapse(false);
22076     
22077         return rangeStartRange.compareBoundaryPoints(
22078                  Range.START_TO_START, nodeEndRange) == -1 &&
22079                rangeEndRange.compareBoundaryPoints(
22080                  Range.START_TO_START, nodeStartRange) == 1;
22081         
22082          
22083     },
22084     rangeCompareNode : function(range, node)
22085     {
22086         var nodeRange = node.ownerDocument.createRange();
22087         try {
22088             nodeRange.selectNode(node);
22089         } catch (e) {
22090             nodeRange.selectNodeContents(node);
22091         }
22092         
22093         
22094         range.collapse(true);
22095     
22096         nodeRange.collapse(true);
22097      
22098         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22099         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22100          
22101         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22102         
22103         var nodeIsBefore   =  ss == 1;
22104         var nodeIsAfter    = ee == -1;
22105         
22106         if (nodeIsBefore && nodeIsAfter) {
22107             return 0; // outer
22108         }
22109         if (!nodeIsBefore && nodeIsAfter) {
22110             return 1; //right trailed.
22111         }
22112         
22113         if (nodeIsBefore && !nodeIsAfter) {
22114             return 2;  // left trailed.
22115         }
22116         // fully contined.
22117         return 3;
22118     },
22119
22120     // private? - in a new class?
22121     cleanUpPaste :  function()
22122     {
22123         // cleans up the whole document..
22124         Roo.log('cleanuppaste');
22125         
22126         this.cleanUpChildren(this.doc.body);
22127         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22128         if (clean != this.doc.body.innerHTML) {
22129             this.doc.body.innerHTML = clean;
22130         }
22131         
22132     },
22133     
22134     cleanWordChars : function(input) {// change the chars to hex code
22135         var he = Roo.HtmlEditorCore;
22136         
22137         var output = input;
22138         Roo.each(he.swapCodes, function(sw) { 
22139             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22140             
22141             output = output.replace(swapper, sw[1]);
22142         });
22143         
22144         return output;
22145     },
22146     
22147     
22148     cleanUpChildren : function (n)
22149     {
22150         if (!n.childNodes.length) {
22151             return;
22152         }
22153         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22154            this.cleanUpChild(n.childNodes[i]);
22155         }
22156     },
22157     
22158     
22159         
22160     
22161     cleanUpChild : function (node)
22162     {
22163         var ed = this;
22164         //console.log(node);
22165         if (node.nodeName == "#text") {
22166             // clean up silly Windows -- stuff?
22167             return; 
22168         }
22169         if (node.nodeName == "#comment") {
22170             node.parentNode.removeChild(node);
22171             // clean up silly Windows -- stuff?
22172             return; 
22173         }
22174         var lcname = node.tagName.toLowerCase();
22175         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22176         // whitelist of tags..
22177         
22178         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22179             // remove node.
22180             node.parentNode.removeChild(node);
22181             return;
22182             
22183         }
22184         
22185         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22186         
22187         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22188         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22189         
22190         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22191         //    remove_keep_children = true;
22192         //}
22193         
22194         if (remove_keep_children) {
22195             this.cleanUpChildren(node);
22196             // inserts everything just before this node...
22197             while (node.childNodes.length) {
22198                 var cn = node.childNodes[0];
22199                 node.removeChild(cn);
22200                 node.parentNode.insertBefore(cn, node);
22201             }
22202             node.parentNode.removeChild(node);
22203             return;
22204         }
22205         
22206         if (!node.attributes || !node.attributes.length) {
22207             this.cleanUpChildren(node);
22208             return;
22209         }
22210         
22211         function cleanAttr(n,v)
22212         {
22213             
22214             if (v.match(/^\./) || v.match(/^\//)) {
22215                 return;
22216             }
22217             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22218                 return;
22219             }
22220             if (v.match(/^#/)) {
22221                 return;
22222             }
22223 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22224             node.removeAttribute(n);
22225             
22226         }
22227         
22228         var cwhite = this.cwhite;
22229         var cblack = this.cblack;
22230             
22231         function cleanStyle(n,v)
22232         {
22233             if (v.match(/expression/)) { //XSS?? should we even bother..
22234                 node.removeAttribute(n);
22235                 return;
22236             }
22237             
22238             var parts = v.split(/;/);
22239             var clean = [];
22240             
22241             Roo.each(parts, function(p) {
22242                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22243                 if (!p.length) {
22244                     return true;
22245                 }
22246                 var l = p.split(':').shift().replace(/\s+/g,'');
22247                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22248                 
22249                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22250 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22251                     //node.removeAttribute(n);
22252                     return true;
22253                 }
22254                 //Roo.log()
22255                 // only allow 'c whitelisted system attributes'
22256                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22257 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22258                     //node.removeAttribute(n);
22259                     return true;
22260                 }
22261                 
22262                 
22263                  
22264                 
22265                 clean.push(p);
22266                 return true;
22267             });
22268             if (clean.length) { 
22269                 node.setAttribute(n, clean.join(';'));
22270             } else {
22271                 node.removeAttribute(n);
22272             }
22273             
22274         }
22275         
22276         
22277         for (var i = node.attributes.length-1; i > -1 ; i--) {
22278             var a = node.attributes[i];
22279             //console.log(a);
22280             
22281             if (a.name.toLowerCase().substr(0,2)=='on')  {
22282                 node.removeAttribute(a.name);
22283                 continue;
22284             }
22285             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22286                 node.removeAttribute(a.name);
22287                 continue;
22288             }
22289             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22290                 cleanAttr(a.name,a.value); // fixme..
22291                 continue;
22292             }
22293             if (a.name == 'style') {
22294                 cleanStyle(a.name,a.value);
22295                 continue;
22296             }
22297             /// clean up MS crap..
22298             // tecnically this should be a list of valid class'es..
22299             
22300             
22301             if (a.name == 'class') {
22302                 if (a.value.match(/^Mso/)) {
22303                     node.className = '';
22304                 }
22305                 
22306                 if (a.value.match(/^body$/)) {
22307                     node.className = '';
22308                 }
22309                 continue;
22310             }
22311             
22312             // style cleanup!?
22313             // class cleanup?
22314             
22315         }
22316         
22317         
22318         this.cleanUpChildren(node);
22319         
22320         
22321     },
22322     
22323     /**
22324      * Clean up MS wordisms...
22325      */
22326     cleanWord : function(node)
22327     {
22328         
22329         
22330         if (!node) {
22331             this.cleanWord(this.doc.body);
22332             return;
22333         }
22334         if (node.nodeName == "#text") {
22335             // clean up silly Windows -- stuff?
22336             return; 
22337         }
22338         if (node.nodeName == "#comment") {
22339             node.parentNode.removeChild(node);
22340             // clean up silly Windows -- stuff?
22341             return; 
22342         }
22343         
22344         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22345             node.parentNode.removeChild(node);
22346             return;
22347         }
22348         
22349         // remove - but keep children..
22350         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22351             while (node.childNodes.length) {
22352                 var cn = node.childNodes[0];
22353                 node.removeChild(cn);
22354                 node.parentNode.insertBefore(cn, node);
22355             }
22356             node.parentNode.removeChild(node);
22357             this.iterateChildren(node, this.cleanWord);
22358             return;
22359         }
22360         // clean styles
22361         if (node.className.length) {
22362             
22363             var cn = node.className.split(/\W+/);
22364             var cna = [];
22365             Roo.each(cn, function(cls) {
22366                 if (cls.match(/Mso[a-zA-Z]+/)) {
22367                     return;
22368                 }
22369                 cna.push(cls);
22370             });
22371             node.className = cna.length ? cna.join(' ') : '';
22372             if (!cna.length) {
22373                 node.removeAttribute("class");
22374             }
22375         }
22376         
22377         if (node.hasAttribute("lang")) {
22378             node.removeAttribute("lang");
22379         }
22380         
22381         if (node.hasAttribute("style")) {
22382             
22383             var styles = node.getAttribute("style").split(";");
22384             var nstyle = [];
22385             Roo.each(styles, function(s) {
22386                 if (!s.match(/:/)) {
22387                     return;
22388                 }
22389                 var kv = s.split(":");
22390                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22391                     return;
22392                 }
22393                 // what ever is left... we allow.
22394                 nstyle.push(s);
22395             });
22396             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22397             if (!nstyle.length) {
22398                 node.removeAttribute('style');
22399             }
22400         }
22401         this.iterateChildren(node, this.cleanWord);
22402         
22403         
22404         
22405     },
22406     /**
22407      * iterateChildren of a Node, calling fn each time, using this as the scole..
22408      * @param {DomNode} node node to iterate children of.
22409      * @param {Function} fn method of this class to call on each item.
22410      */
22411     iterateChildren : function(node, fn)
22412     {
22413         if (!node.childNodes.length) {
22414                 return;
22415         }
22416         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22417            fn.call(this, node.childNodes[i])
22418         }
22419     },
22420     
22421     
22422     /**
22423      * cleanTableWidths.
22424      *
22425      * Quite often pasting from word etc.. results in tables with column and widths.
22426      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22427      *
22428      */
22429     cleanTableWidths : function(node)
22430     {
22431          
22432          
22433         if (!node) {
22434             this.cleanTableWidths(this.doc.body);
22435             return;
22436         }
22437         
22438         // ignore list...
22439         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22440             return; 
22441         }
22442         Roo.log(node.tagName);
22443         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22444             this.iterateChildren(node, this.cleanTableWidths);
22445             return;
22446         }
22447         if (node.hasAttribute('width')) {
22448             node.removeAttribute('width');
22449         }
22450         
22451          
22452         if (node.hasAttribute("style")) {
22453             // pretty basic...
22454             
22455             var styles = node.getAttribute("style").split(";");
22456             var nstyle = [];
22457             Roo.each(styles, function(s) {
22458                 if (!s.match(/:/)) {
22459                     return;
22460                 }
22461                 var kv = s.split(":");
22462                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22463                     return;
22464                 }
22465                 // what ever is left... we allow.
22466                 nstyle.push(s);
22467             });
22468             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22469             if (!nstyle.length) {
22470                 node.removeAttribute('style');
22471             }
22472         }
22473         
22474         this.iterateChildren(node, this.cleanTableWidths);
22475         
22476         
22477     },
22478     
22479     
22480     
22481     
22482     domToHTML : function(currentElement, depth, nopadtext) {
22483         
22484         depth = depth || 0;
22485         nopadtext = nopadtext || false;
22486     
22487         if (!currentElement) {
22488             return this.domToHTML(this.doc.body);
22489         }
22490         
22491         //Roo.log(currentElement);
22492         var j;
22493         var allText = false;
22494         var nodeName = currentElement.nodeName;
22495         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22496         
22497         if  (nodeName == '#text') {
22498             
22499             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22500         }
22501         
22502         
22503         var ret = '';
22504         if (nodeName != 'BODY') {
22505              
22506             var i = 0;
22507             // Prints the node tagName, such as <A>, <IMG>, etc
22508             if (tagName) {
22509                 var attr = [];
22510                 for(i = 0; i < currentElement.attributes.length;i++) {
22511                     // quoting?
22512                     var aname = currentElement.attributes.item(i).name;
22513                     if (!currentElement.attributes.item(i).value.length) {
22514                         continue;
22515                     }
22516                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22517                 }
22518                 
22519                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22520             } 
22521             else {
22522                 
22523                 // eack
22524             }
22525         } else {
22526             tagName = false;
22527         }
22528         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22529             return ret;
22530         }
22531         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22532             nopadtext = true;
22533         }
22534         
22535         
22536         // Traverse the tree
22537         i = 0;
22538         var currentElementChild = currentElement.childNodes.item(i);
22539         var allText = true;
22540         var innerHTML  = '';
22541         lastnode = '';
22542         while (currentElementChild) {
22543             // Formatting code (indent the tree so it looks nice on the screen)
22544             var nopad = nopadtext;
22545             if (lastnode == 'SPAN') {
22546                 nopad  = true;
22547             }
22548             // text
22549             if  (currentElementChild.nodeName == '#text') {
22550                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22551                 toadd = nopadtext ? toadd : toadd.trim();
22552                 if (!nopad && toadd.length > 80) {
22553                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22554                 }
22555                 innerHTML  += toadd;
22556                 
22557                 i++;
22558                 currentElementChild = currentElement.childNodes.item(i);
22559                 lastNode = '';
22560                 continue;
22561             }
22562             allText = false;
22563             
22564             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22565                 
22566             // Recursively traverse the tree structure of the child node
22567             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22568             lastnode = currentElementChild.nodeName;
22569             i++;
22570             currentElementChild=currentElement.childNodes.item(i);
22571         }
22572         
22573         ret += innerHTML;
22574         
22575         if (!allText) {
22576                 // The remaining code is mostly for formatting the tree
22577             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22578         }
22579         
22580         
22581         if (tagName) {
22582             ret+= "</"+tagName+">";
22583         }
22584         return ret;
22585         
22586     },
22587         
22588     applyBlacklists : function()
22589     {
22590         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22591         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22592         
22593         this.white = [];
22594         this.black = [];
22595         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22596             if (b.indexOf(tag) > -1) {
22597                 return;
22598             }
22599             this.white.push(tag);
22600             
22601         }, this);
22602         
22603         Roo.each(w, function(tag) {
22604             if (b.indexOf(tag) > -1) {
22605                 return;
22606             }
22607             if (this.white.indexOf(tag) > -1) {
22608                 return;
22609             }
22610             this.white.push(tag);
22611             
22612         }, this);
22613         
22614         
22615         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22616             if (w.indexOf(tag) > -1) {
22617                 return;
22618             }
22619             this.black.push(tag);
22620             
22621         }, this);
22622         
22623         Roo.each(b, function(tag) {
22624             if (w.indexOf(tag) > -1) {
22625                 return;
22626             }
22627             if (this.black.indexOf(tag) > -1) {
22628                 return;
22629             }
22630             this.black.push(tag);
22631             
22632         }, this);
22633         
22634         
22635         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22636         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22637         
22638         this.cwhite = [];
22639         this.cblack = [];
22640         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22641             if (b.indexOf(tag) > -1) {
22642                 return;
22643             }
22644             this.cwhite.push(tag);
22645             
22646         }, this);
22647         
22648         Roo.each(w, function(tag) {
22649             if (b.indexOf(tag) > -1) {
22650                 return;
22651             }
22652             if (this.cwhite.indexOf(tag) > -1) {
22653                 return;
22654             }
22655             this.cwhite.push(tag);
22656             
22657         }, this);
22658         
22659         
22660         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22661             if (w.indexOf(tag) > -1) {
22662                 return;
22663             }
22664             this.cblack.push(tag);
22665             
22666         }, this);
22667         
22668         Roo.each(b, function(tag) {
22669             if (w.indexOf(tag) > -1) {
22670                 return;
22671             }
22672             if (this.cblack.indexOf(tag) > -1) {
22673                 return;
22674             }
22675             this.cblack.push(tag);
22676             
22677         }, this);
22678     },
22679     
22680     setStylesheets : function(stylesheets)
22681     {
22682         if(typeof(stylesheets) == 'string'){
22683             Roo.get(this.iframe.contentDocument.head).createChild({
22684                 tag : 'link',
22685                 rel : 'stylesheet',
22686                 type : 'text/css',
22687                 href : stylesheets
22688             });
22689             
22690             return;
22691         }
22692         var _this = this;
22693      
22694         Roo.each(stylesheets, function(s) {
22695             if(!s.length){
22696                 return;
22697             }
22698             
22699             Roo.get(_this.iframe.contentDocument.head).createChild({
22700                 tag : 'link',
22701                 rel : 'stylesheet',
22702                 type : 'text/css',
22703                 href : s
22704             });
22705         });
22706
22707         
22708     },
22709     
22710     removeStylesheets : function()
22711     {
22712         var _this = this;
22713         
22714         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22715             s.remove();
22716         });
22717     },
22718     
22719     setStyle : function(style)
22720     {
22721         Roo.get(this.iframe.contentDocument.head).createChild({
22722             tag : 'style',
22723             type : 'text/css',
22724             html : style
22725         });
22726
22727         return;
22728     }
22729     
22730     // hide stuff that is not compatible
22731     /**
22732      * @event blur
22733      * @hide
22734      */
22735     /**
22736      * @event change
22737      * @hide
22738      */
22739     /**
22740      * @event focus
22741      * @hide
22742      */
22743     /**
22744      * @event specialkey
22745      * @hide
22746      */
22747     /**
22748      * @cfg {String} fieldClass @hide
22749      */
22750     /**
22751      * @cfg {String} focusClass @hide
22752      */
22753     /**
22754      * @cfg {String} autoCreate @hide
22755      */
22756     /**
22757      * @cfg {String} inputType @hide
22758      */
22759     /**
22760      * @cfg {String} invalidClass @hide
22761      */
22762     /**
22763      * @cfg {String} invalidText @hide
22764      */
22765     /**
22766      * @cfg {String} msgFx @hide
22767      */
22768     /**
22769      * @cfg {String} validateOnBlur @hide
22770      */
22771 });
22772
22773 Roo.HtmlEditorCore.white = [
22774         'area', 'br', 'img', 'input', 'hr', 'wbr',
22775         
22776        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22777        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22778        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22779        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22780        'table',   'ul',         'xmp', 
22781        
22782        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22783       'thead',   'tr', 
22784      
22785       'dir', 'menu', 'ol', 'ul', 'dl',
22786        
22787       'embed',  'object'
22788 ];
22789
22790
22791 Roo.HtmlEditorCore.black = [
22792     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22793         'applet', // 
22794         'base',   'basefont', 'bgsound', 'blink',  'body', 
22795         'frame',  'frameset', 'head',    'html',   'ilayer', 
22796         'iframe', 'layer',  'link',     'meta',    'object',   
22797         'script', 'style' ,'title',  'xml' // clean later..
22798 ];
22799 Roo.HtmlEditorCore.clean = [
22800     'script', 'style', 'title', 'xml'
22801 ];
22802 Roo.HtmlEditorCore.remove = [
22803     'font'
22804 ];
22805 // attributes..
22806
22807 Roo.HtmlEditorCore.ablack = [
22808     'on'
22809 ];
22810     
22811 Roo.HtmlEditorCore.aclean = [ 
22812     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22813 ];
22814
22815 // protocols..
22816 Roo.HtmlEditorCore.pwhite= [
22817         'http',  'https',  'mailto'
22818 ];
22819
22820 // white listed style attributes.
22821 Roo.HtmlEditorCore.cwhite= [
22822       //  'text-align', /// default is to allow most things..
22823       
22824          
22825 //        'font-size'//??
22826 ];
22827
22828 // black listed style attributes.
22829 Roo.HtmlEditorCore.cblack= [
22830       //  'font-size' -- this can be set by the project 
22831 ];
22832
22833
22834 Roo.HtmlEditorCore.swapCodes   =[ 
22835     [    8211, "--" ], 
22836     [    8212, "--" ], 
22837     [    8216,  "'" ],  
22838     [    8217, "'" ],  
22839     [    8220, '"' ],  
22840     [    8221, '"' ],  
22841     [    8226, "*" ],  
22842     [    8230, "..." ]
22843 ]; 
22844
22845     /*
22846  * - LGPL
22847  *
22848  * HtmlEditor
22849  * 
22850  */
22851
22852 /**
22853  * @class Roo.bootstrap.HtmlEditor
22854  * @extends Roo.bootstrap.TextArea
22855  * Bootstrap HtmlEditor class
22856
22857  * @constructor
22858  * Create a new HtmlEditor
22859  * @param {Object} config The config object
22860  */
22861
22862 Roo.bootstrap.HtmlEditor = function(config){
22863     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22864     if (!this.toolbars) {
22865         this.toolbars = [];
22866     }
22867     
22868     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22869     this.addEvents({
22870             /**
22871              * @event initialize
22872              * Fires when the editor is fully initialized (including the iframe)
22873              * @param {HtmlEditor} this
22874              */
22875             initialize: true,
22876             /**
22877              * @event activate
22878              * Fires when the editor is first receives the focus. Any insertion must wait
22879              * until after this event.
22880              * @param {HtmlEditor} this
22881              */
22882             activate: true,
22883              /**
22884              * @event beforesync
22885              * Fires before the textarea is updated with content from the editor iframe. Return false
22886              * to cancel the sync.
22887              * @param {HtmlEditor} this
22888              * @param {String} html
22889              */
22890             beforesync: true,
22891              /**
22892              * @event beforepush
22893              * Fires before the iframe editor is updated with content from the textarea. Return false
22894              * to cancel the push.
22895              * @param {HtmlEditor} this
22896              * @param {String} html
22897              */
22898             beforepush: true,
22899              /**
22900              * @event sync
22901              * Fires when the textarea is updated with content from the editor iframe.
22902              * @param {HtmlEditor} this
22903              * @param {String} html
22904              */
22905             sync: true,
22906              /**
22907              * @event push
22908              * Fires when the iframe editor is updated with content from the textarea.
22909              * @param {HtmlEditor} this
22910              * @param {String} html
22911              */
22912             push: true,
22913              /**
22914              * @event editmodechange
22915              * Fires when the editor switches edit modes
22916              * @param {HtmlEditor} this
22917              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22918              */
22919             editmodechange: true,
22920             /**
22921              * @event editorevent
22922              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22923              * @param {HtmlEditor} this
22924              */
22925             editorevent: true,
22926             /**
22927              * @event firstfocus
22928              * Fires when on first focus - needed by toolbars..
22929              * @param {HtmlEditor} this
22930              */
22931             firstfocus: true,
22932             /**
22933              * @event autosave
22934              * Auto save the htmlEditor value as a file into Events
22935              * @param {HtmlEditor} this
22936              */
22937             autosave: true,
22938             /**
22939              * @event savedpreview
22940              * preview the saved version of htmlEditor
22941              * @param {HtmlEditor} this
22942              */
22943             savedpreview: true
22944         });
22945 };
22946
22947
22948 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22949     
22950     
22951       /**
22952      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22953      */
22954     toolbars : false,
22955     
22956      /**
22957     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22958     */
22959     btns : [],
22960    
22961      /**
22962      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22963      *                        Roo.resizable.
22964      */
22965     resizable : false,
22966      /**
22967      * @cfg {Number} height (in pixels)
22968      */   
22969     height: 300,
22970    /**
22971      * @cfg {Number} width (in pixels)
22972      */   
22973     width: false,
22974     
22975     /**
22976      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22977      * 
22978      */
22979     stylesheets: false,
22980     
22981     // id of frame..
22982     frameId: false,
22983     
22984     // private properties
22985     validationEvent : false,
22986     deferHeight: true,
22987     initialized : false,
22988     activated : false,
22989     
22990     onFocus : Roo.emptyFn,
22991     iframePad:3,
22992     hideMode:'offsets',
22993     
22994     tbContainer : false,
22995     
22996     bodyCls : '',
22997     
22998     toolbarContainer :function() {
22999         return this.wrap.select('.x-html-editor-tb',true).first();
23000     },
23001
23002     /**
23003      * Protected method that will not generally be called directly. It
23004      * is called when the editor creates its toolbar. Override this method if you need to
23005      * add custom toolbar buttons.
23006      * @param {HtmlEditor} editor
23007      */
23008     createToolbar : function(){
23009         Roo.log('renewing');
23010         Roo.log("create toolbars");
23011         
23012         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23013         this.toolbars[0].render(this.toolbarContainer());
23014         
23015         return;
23016         
23017 //        if (!editor.toolbars || !editor.toolbars.length) {
23018 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23019 //        }
23020 //        
23021 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23022 //            editor.toolbars[i] = Roo.factory(
23023 //                    typeof(editor.toolbars[i]) == 'string' ?
23024 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23025 //                Roo.bootstrap.HtmlEditor);
23026 //            editor.toolbars[i].init(editor);
23027 //        }
23028     },
23029
23030      
23031     // private
23032     onRender : function(ct, position)
23033     {
23034        // Roo.log("Call onRender: " + this.xtype);
23035         var _t = this;
23036         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23037       
23038         this.wrap = this.inputEl().wrap({
23039             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23040         });
23041         
23042         this.editorcore.onRender(ct, position);
23043          
23044         if (this.resizable) {
23045             this.resizeEl = new Roo.Resizable(this.wrap, {
23046                 pinned : true,
23047                 wrap: true,
23048                 dynamic : true,
23049                 minHeight : this.height,
23050                 height: this.height,
23051                 handles : this.resizable,
23052                 width: this.width,
23053                 listeners : {
23054                     resize : function(r, w, h) {
23055                         _t.onResize(w,h); // -something
23056                     }
23057                 }
23058             });
23059             
23060         }
23061         this.createToolbar(this);
23062        
23063         
23064         if(!this.width && this.resizable){
23065             this.setSize(this.wrap.getSize());
23066         }
23067         if (this.resizeEl) {
23068             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23069             // should trigger onReize..
23070         }
23071         
23072     },
23073
23074     // private
23075     onResize : function(w, h)
23076     {
23077         Roo.log('resize: ' +w + ',' + h );
23078         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23079         var ew = false;
23080         var eh = false;
23081         
23082         if(this.inputEl() ){
23083             if(typeof w == 'number'){
23084                 var aw = w - this.wrap.getFrameWidth('lr');
23085                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23086                 ew = aw;
23087             }
23088             if(typeof h == 'number'){
23089                  var tbh = -11;  // fixme it needs to tool bar size!
23090                 for (var i =0; i < this.toolbars.length;i++) {
23091                     // fixme - ask toolbars for heights?
23092                     tbh += this.toolbars[i].el.getHeight();
23093                     //if (this.toolbars[i].footer) {
23094                     //    tbh += this.toolbars[i].footer.el.getHeight();
23095                     //}
23096                 }
23097               
23098                 
23099                 
23100                 
23101                 
23102                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23103                 ah -= 5; // knock a few pixes off for look..
23104                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23105                 var eh = ah;
23106             }
23107         }
23108         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23109         this.editorcore.onResize(ew,eh);
23110         
23111     },
23112
23113     /**
23114      * Toggles the editor between standard and source edit mode.
23115      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23116      */
23117     toggleSourceEdit : function(sourceEditMode)
23118     {
23119         this.editorcore.toggleSourceEdit(sourceEditMode);
23120         
23121         if(this.editorcore.sourceEditMode){
23122             Roo.log('editor - showing textarea');
23123             
23124 //            Roo.log('in');
23125 //            Roo.log(this.syncValue());
23126             this.syncValue();
23127             this.inputEl().removeClass(['hide', 'x-hidden']);
23128             this.inputEl().dom.removeAttribute('tabIndex');
23129             this.inputEl().focus();
23130         }else{
23131             Roo.log('editor - hiding textarea');
23132 //            Roo.log('out')
23133 //            Roo.log(this.pushValue()); 
23134             this.pushValue();
23135             
23136             this.inputEl().addClass(['hide', 'x-hidden']);
23137             this.inputEl().dom.setAttribute('tabIndex', -1);
23138             //this.deferFocus();
23139         }
23140          
23141         if(this.resizable){
23142             this.setSize(this.wrap.getSize());
23143         }
23144         
23145         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23146     },
23147  
23148     // private (for BoxComponent)
23149     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23150
23151     // private (for BoxComponent)
23152     getResizeEl : function(){
23153         return this.wrap;
23154     },
23155
23156     // private (for BoxComponent)
23157     getPositionEl : function(){
23158         return this.wrap;
23159     },
23160
23161     // private
23162     initEvents : function(){
23163         this.originalValue = this.getValue();
23164     },
23165
23166 //    /**
23167 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23168 //     * @method
23169 //     */
23170 //    markInvalid : Roo.emptyFn,
23171 //    /**
23172 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23173 //     * @method
23174 //     */
23175 //    clearInvalid : Roo.emptyFn,
23176
23177     setValue : function(v){
23178         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23179         this.editorcore.pushValue();
23180     },
23181
23182      
23183     // private
23184     deferFocus : function(){
23185         this.focus.defer(10, this);
23186     },
23187
23188     // doc'ed in Field
23189     focus : function(){
23190         this.editorcore.focus();
23191         
23192     },
23193       
23194
23195     // private
23196     onDestroy : function(){
23197         
23198         
23199         
23200         if(this.rendered){
23201             
23202             for (var i =0; i < this.toolbars.length;i++) {
23203                 // fixme - ask toolbars for heights?
23204                 this.toolbars[i].onDestroy();
23205             }
23206             
23207             this.wrap.dom.innerHTML = '';
23208             this.wrap.remove();
23209         }
23210     },
23211
23212     // private
23213     onFirstFocus : function(){
23214         //Roo.log("onFirstFocus");
23215         this.editorcore.onFirstFocus();
23216          for (var i =0; i < this.toolbars.length;i++) {
23217             this.toolbars[i].onFirstFocus();
23218         }
23219         
23220     },
23221     
23222     // private
23223     syncValue : function()
23224     {   
23225         this.editorcore.syncValue();
23226     },
23227     
23228     pushValue : function()
23229     {   
23230         this.editorcore.pushValue();
23231     }
23232      
23233     
23234     // hide stuff that is not compatible
23235     /**
23236      * @event blur
23237      * @hide
23238      */
23239     /**
23240      * @event change
23241      * @hide
23242      */
23243     /**
23244      * @event focus
23245      * @hide
23246      */
23247     /**
23248      * @event specialkey
23249      * @hide
23250      */
23251     /**
23252      * @cfg {String} fieldClass @hide
23253      */
23254     /**
23255      * @cfg {String} focusClass @hide
23256      */
23257     /**
23258      * @cfg {String} autoCreate @hide
23259      */
23260     /**
23261      * @cfg {String} inputType @hide
23262      */
23263     /**
23264      * @cfg {String} invalidClass @hide
23265      */
23266     /**
23267      * @cfg {String} invalidText @hide
23268      */
23269     /**
23270      * @cfg {String} msgFx @hide
23271      */
23272     /**
23273      * @cfg {String} validateOnBlur @hide
23274      */
23275 });
23276  
23277     
23278    
23279    
23280    
23281       
23282 Roo.namespace('Roo.bootstrap.htmleditor');
23283 /**
23284  * @class Roo.bootstrap.HtmlEditorToolbar1
23285  * Basic Toolbar
23286  * 
23287  * Usage:
23288  *
23289  new Roo.bootstrap.HtmlEditor({
23290     ....
23291     toolbars : [
23292         new Roo.bootstrap.HtmlEditorToolbar1({
23293             disable : { fonts: 1 , format: 1, ..., ... , ...],
23294             btns : [ .... ]
23295         })
23296     }
23297      
23298  * 
23299  * @cfg {Object} disable List of elements to disable..
23300  * @cfg {Array} btns List of additional buttons.
23301  * 
23302  * 
23303  * NEEDS Extra CSS? 
23304  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23305  */
23306  
23307 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23308 {
23309     
23310     Roo.apply(this, config);
23311     
23312     // default disabled, based on 'good practice'..
23313     this.disable = this.disable || {};
23314     Roo.applyIf(this.disable, {
23315         fontSize : true,
23316         colors : true,
23317         specialElements : true
23318     });
23319     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23320     
23321     this.editor = config.editor;
23322     this.editorcore = config.editor.editorcore;
23323     
23324     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23325     
23326     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23327     // dont call parent... till later.
23328 }
23329 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23330      
23331     bar : true,
23332     
23333     editor : false,
23334     editorcore : false,
23335     
23336     
23337     formats : [
23338         "p" ,  
23339         "h1","h2","h3","h4","h5","h6", 
23340         "pre", "code", 
23341         "abbr", "acronym", "address", "cite", "samp", "var",
23342         'div','span'
23343     ],
23344     
23345     onRender : function(ct, position)
23346     {
23347        // Roo.log("Call onRender: " + this.xtype);
23348         
23349        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23350        Roo.log(this.el);
23351        this.el.dom.style.marginBottom = '0';
23352        var _this = this;
23353        var editorcore = this.editorcore;
23354        var editor= this.editor;
23355        
23356        var children = [];
23357        var btn = function(id,cmd , toggle, handler, html){
23358        
23359             var  event = toggle ? 'toggle' : 'click';
23360        
23361             var a = {
23362                 size : 'sm',
23363                 xtype: 'Button',
23364                 xns: Roo.bootstrap,
23365                 glyphicon : id,
23366                 cmd : id || cmd,
23367                 enableToggle:toggle !== false,
23368                 html : html || '',
23369                 pressed : toggle ? false : null,
23370                 listeners : {}
23371             };
23372             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23373                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23374             };
23375             children.push(a);
23376             return a;
23377        }
23378        
23379     //    var cb_box = function...
23380         
23381         var style = {
23382                 xtype: 'Button',
23383                 size : 'sm',
23384                 xns: Roo.bootstrap,
23385                 glyphicon : 'font',
23386                 //html : 'submit'
23387                 menu : {
23388                     xtype: 'Menu',
23389                     xns: Roo.bootstrap,
23390                     items:  []
23391                 }
23392         };
23393         Roo.each(this.formats, function(f) {
23394             style.menu.items.push({
23395                 xtype :'MenuItem',
23396                 xns: Roo.bootstrap,
23397                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23398                 tagname : f,
23399                 listeners : {
23400                     click : function()
23401                     {
23402                         editorcore.insertTag(this.tagname);
23403                         editor.focus();
23404                     }
23405                 }
23406                 
23407             });
23408         });
23409         children.push(style);   
23410         
23411         btn('bold',false,true);
23412         btn('italic',false,true);
23413         btn('align-left', 'justifyleft',true);
23414         btn('align-center', 'justifycenter',true);
23415         btn('align-right' , 'justifyright',true);
23416         btn('link', false, false, function(btn) {
23417             //Roo.log("create link?");
23418             var url = prompt(this.createLinkText, this.defaultLinkValue);
23419             if(url && url != 'http:/'+'/'){
23420                 this.editorcore.relayCmd('createlink', url);
23421             }
23422         }),
23423         btn('list','insertunorderedlist',true);
23424         btn('pencil', false,true, function(btn){
23425                 Roo.log(this);
23426                 this.toggleSourceEdit(btn.pressed);
23427         });
23428         
23429         if (this.editor.btns.length > 0) {
23430             for (var i = 0; i<this.editor.btns.length; i++) {
23431                 children.push(this.editor.btns[i]);
23432             }
23433         }
23434         
23435         /*
23436         var cog = {
23437                 xtype: 'Button',
23438                 size : 'sm',
23439                 xns: Roo.bootstrap,
23440                 glyphicon : 'cog',
23441                 //html : 'submit'
23442                 menu : {
23443                     xtype: 'Menu',
23444                     xns: Roo.bootstrap,
23445                     items:  []
23446                 }
23447         };
23448         
23449         cog.menu.items.push({
23450             xtype :'MenuItem',
23451             xns: Roo.bootstrap,
23452             html : Clean styles,
23453             tagname : f,
23454             listeners : {
23455                 click : function()
23456                 {
23457                     editorcore.insertTag(this.tagname);
23458                     editor.focus();
23459                 }
23460             }
23461             
23462         });
23463        */
23464         
23465          
23466        this.xtype = 'NavSimplebar';
23467         
23468         for(var i=0;i< children.length;i++) {
23469             
23470             this.buttons.add(this.addxtypeChild(children[i]));
23471             
23472         }
23473         
23474         editor.on('editorevent', this.updateToolbar, this);
23475     },
23476     onBtnClick : function(id)
23477     {
23478        this.editorcore.relayCmd(id);
23479        this.editorcore.focus();
23480     },
23481     
23482     /**
23483      * Protected method that will not generally be called directly. It triggers
23484      * a toolbar update by reading the markup state of the current selection in the editor.
23485      */
23486     updateToolbar: function(){
23487
23488         if(!this.editorcore.activated){
23489             this.editor.onFirstFocus(); // is this neeed?
23490             return;
23491         }
23492
23493         var btns = this.buttons; 
23494         var doc = this.editorcore.doc;
23495         btns.get('bold').setActive(doc.queryCommandState('bold'));
23496         btns.get('italic').setActive(doc.queryCommandState('italic'));
23497         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23498         
23499         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23500         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23501         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23502         
23503         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23504         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23505          /*
23506         
23507         var ans = this.editorcore.getAllAncestors();
23508         if (this.formatCombo) {
23509             
23510             
23511             var store = this.formatCombo.store;
23512             this.formatCombo.setValue("");
23513             for (var i =0; i < ans.length;i++) {
23514                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23515                     // select it..
23516                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23517                     break;
23518                 }
23519             }
23520         }
23521         
23522         
23523         
23524         // hides menus... - so this cant be on a menu...
23525         Roo.bootstrap.MenuMgr.hideAll();
23526         */
23527         Roo.bootstrap.MenuMgr.hideAll();
23528         //this.editorsyncValue();
23529     },
23530     onFirstFocus: function() {
23531         this.buttons.each(function(item){
23532            item.enable();
23533         });
23534     },
23535     toggleSourceEdit : function(sourceEditMode){
23536         
23537           
23538         if(sourceEditMode){
23539             Roo.log("disabling buttons");
23540            this.buttons.each( function(item){
23541                 if(item.cmd != 'pencil'){
23542                     item.disable();
23543                 }
23544             });
23545           
23546         }else{
23547             Roo.log("enabling buttons");
23548             if(this.editorcore.initialized){
23549                 this.buttons.each( function(item){
23550                     item.enable();
23551                 });
23552             }
23553             
23554         }
23555         Roo.log("calling toggole on editor");
23556         // tell the editor that it's been pressed..
23557         this.editor.toggleSourceEdit(sourceEditMode);
23558        
23559     }
23560 });
23561
23562
23563
23564
23565
23566 /**
23567  * @class Roo.bootstrap.Table.AbstractSelectionModel
23568  * @extends Roo.util.Observable
23569  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23570  * implemented by descendant classes.  This class should not be directly instantiated.
23571  * @constructor
23572  */
23573 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23574     this.locked = false;
23575     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23576 };
23577
23578
23579 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23580     /** @ignore Called by the grid automatically. Do not call directly. */
23581     init : function(grid){
23582         this.grid = grid;
23583         this.initEvents();
23584     },
23585
23586     /**
23587      * Locks the selections.
23588      */
23589     lock : function(){
23590         this.locked = true;
23591     },
23592
23593     /**
23594      * Unlocks the selections.
23595      */
23596     unlock : function(){
23597         this.locked = false;
23598     },
23599
23600     /**
23601      * Returns true if the selections are locked.
23602      * @return {Boolean}
23603      */
23604     isLocked : function(){
23605         return this.locked;
23606     }
23607 });
23608 /**
23609  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23610  * @class Roo.bootstrap.Table.RowSelectionModel
23611  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23612  * It supports multiple selections and keyboard selection/navigation. 
23613  * @constructor
23614  * @param {Object} config
23615  */
23616
23617 Roo.bootstrap.Table.RowSelectionModel = function(config){
23618     Roo.apply(this, config);
23619     this.selections = new Roo.util.MixedCollection(false, function(o){
23620         return o.id;
23621     });
23622
23623     this.last = false;
23624     this.lastActive = false;
23625
23626     this.addEvents({
23627         /**
23628              * @event selectionchange
23629              * Fires when the selection changes
23630              * @param {SelectionModel} this
23631              */
23632             "selectionchange" : true,
23633         /**
23634              * @event afterselectionchange
23635              * Fires after the selection changes (eg. by key press or clicking)
23636              * @param {SelectionModel} this
23637              */
23638             "afterselectionchange" : true,
23639         /**
23640              * @event beforerowselect
23641              * Fires when a row is selected being selected, return false to cancel.
23642              * @param {SelectionModel} this
23643              * @param {Number} rowIndex The selected index
23644              * @param {Boolean} keepExisting False if other selections will be cleared
23645              */
23646             "beforerowselect" : true,
23647         /**
23648              * @event rowselect
23649              * Fires when a row is selected.
23650              * @param {SelectionModel} this
23651              * @param {Number} rowIndex The selected index
23652              * @param {Roo.data.Record} r The record
23653              */
23654             "rowselect" : true,
23655         /**
23656              * @event rowdeselect
23657              * Fires when a row is deselected.
23658              * @param {SelectionModel} this
23659              * @param {Number} rowIndex The selected index
23660              */
23661         "rowdeselect" : true
23662     });
23663     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23664     this.locked = false;
23665  };
23666
23667 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23668     /**
23669      * @cfg {Boolean} singleSelect
23670      * True to allow selection of only one row at a time (defaults to false)
23671      */
23672     singleSelect : false,
23673
23674     // private
23675     initEvents : function()
23676     {
23677
23678         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23679         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23680         //}else{ // allow click to work like normal
23681          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23682         //}
23683         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23684         this.grid.on("rowclick", this.handleMouseDown, this);
23685         
23686         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23687             "up" : function(e){
23688                 if(!e.shiftKey){
23689                     this.selectPrevious(e.shiftKey);
23690                 }else if(this.last !== false && this.lastActive !== false){
23691                     var last = this.last;
23692                     this.selectRange(this.last,  this.lastActive-1);
23693                     this.grid.getView().focusRow(this.lastActive);
23694                     if(last !== false){
23695                         this.last = last;
23696                     }
23697                 }else{
23698                     this.selectFirstRow();
23699                 }
23700                 this.fireEvent("afterselectionchange", this);
23701             },
23702             "down" : function(e){
23703                 if(!e.shiftKey){
23704                     this.selectNext(e.shiftKey);
23705                 }else if(this.last !== false && this.lastActive !== false){
23706                     var last = this.last;
23707                     this.selectRange(this.last,  this.lastActive+1);
23708                     this.grid.getView().focusRow(this.lastActive);
23709                     if(last !== false){
23710                         this.last = last;
23711                     }
23712                 }else{
23713                     this.selectFirstRow();
23714                 }
23715                 this.fireEvent("afterselectionchange", this);
23716             },
23717             scope: this
23718         });
23719         this.grid.store.on('load', function(){
23720             this.selections.clear();
23721         },this);
23722         /*
23723         var view = this.grid.view;
23724         view.on("refresh", this.onRefresh, this);
23725         view.on("rowupdated", this.onRowUpdated, this);
23726         view.on("rowremoved", this.onRemove, this);
23727         */
23728     },
23729
23730     // private
23731     onRefresh : function()
23732     {
23733         var ds = this.grid.store, i, v = this.grid.view;
23734         var s = this.selections;
23735         s.each(function(r){
23736             if((i = ds.indexOfId(r.id)) != -1){
23737                 v.onRowSelect(i);
23738             }else{
23739                 s.remove(r);
23740             }
23741         });
23742     },
23743
23744     // private
23745     onRemove : function(v, index, r){
23746         this.selections.remove(r);
23747     },
23748
23749     // private
23750     onRowUpdated : function(v, index, r){
23751         if(this.isSelected(r)){
23752             v.onRowSelect(index);
23753         }
23754     },
23755
23756     /**
23757      * Select records.
23758      * @param {Array} records The records to select
23759      * @param {Boolean} keepExisting (optional) True to keep existing selections
23760      */
23761     selectRecords : function(records, keepExisting)
23762     {
23763         if(!keepExisting){
23764             this.clearSelections();
23765         }
23766             var ds = this.grid.store;
23767         for(var i = 0, len = records.length; i < len; i++){
23768             this.selectRow(ds.indexOf(records[i]), true);
23769         }
23770     },
23771
23772     /**
23773      * Gets the number of selected rows.
23774      * @return {Number}
23775      */
23776     getCount : function(){
23777         return this.selections.length;
23778     },
23779
23780     /**
23781      * Selects the first row in the grid.
23782      */
23783     selectFirstRow : function(){
23784         this.selectRow(0);
23785     },
23786
23787     /**
23788      * Select the last row.
23789      * @param {Boolean} keepExisting (optional) True to keep existing selections
23790      */
23791     selectLastRow : function(keepExisting){
23792         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23793         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23794     },
23795
23796     /**
23797      * Selects the row immediately following the last selected row.
23798      * @param {Boolean} keepExisting (optional) True to keep existing selections
23799      */
23800     selectNext : function(keepExisting)
23801     {
23802             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23803             this.selectRow(this.last+1, keepExisting);
23804             this.grid.getView().focusRow(this.last);
23805         }
23806     },
23807
23808     /**
23809      * Selects the row that precedes the last selected row.
23810      * @param {Boolean} keepExisting (optional) True to keep existing selections
23811      */
23812     selectPrevious : function(keepExisting){
23813         if(this.last){
23814             this.selectRow(this.last-1, keepExisting);
23815             this.grid.getView().focusRow(this.last);
23816         }
23817     },
23818
23819     /**
23820      * Returns the selected records
23821      * @return {Array} Array of selected records
23822      */
23823     getSelections : function(){
23824         return [].concat(this.selections.items);
23825     },
23826
23827     /**
23828      * Returns the first selected record.
23829      * @return {Record}
23830      */
23831     getSelected : function(){
23832         return this.selections.itemAt(0);
23833     },
23834
23835
23836     /**
23837      * Clears all selections.
23838      */
23839     clearSelections : function(fast)
23840     {
23841         if(this.locked) {
23842             return;
23843         }
23844         if(fast !== true){
23845                 var ds = this.grid.store;
23846             var s = this.selections;
23847             s.each(function(r){
23848                 this.deselectRow(ds.indexOfId(r.id));
23849             }, this);
23850             s.clear();
23851         }else{
23852             this.selections.clear();
23853         }
23854         this.last = false;
23855     },
23856
23857
23858     /**
23859      * Selects all rows.
23860      */
23861     selectAll : function(){
23862         if(this.locked) {
23863             return;
23864         }
23865         this.selections.clear();
23866         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23867             this.selectRow(i, true);
23868         }
23869     },
23870
23871     /**
23872      * Returns True if there is a selection.
23873      * @return {Boolean}
23874      */
23875     hasSelection : function(){
23876         return this.selections.length > 0;
23877     },
23878
23879     /**
23880      * Returns True if the specified row is selected.
23881      * @param {Number/Record} record The record or index of the record to check
23882      * @return {Boolean}
23883      */
23884     isSelected : function(index){
23885             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23886         return (r && this.selections.key(r.id) ? true : false);
23887     },
23888
23889     /**
23890      * Returns True if the specified record id is selected.
23891      * @param {String} id The id of record to check
23892      * @return {Boolean}
23893      */
23894     isIdSelected : function(id){
23895         return (this.selections.key(id) ? true : false);
23896     },
23897
23898
23899     // private
23900     handleMouseDBClick : function(e, t){
23901         
23902     },
23903     // private
23904     handleMouseDown : function(e, t)
23905     {
23906             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23907         if(this.isLocked() || rowIndex < 0 ){
23908             return;
23909         };
23910         if(e.shiftKey && this.last !== false){
23911             var last = this.last;
23912             this.selectRange(last, rowIndex, e.ctrlKey);
23913             this.last = last; // reset the last
23914             t.focus();
23915     
23916         }else{
23917             var isSelected = this.isSelected(rowIndex);
23918             //Roo.log("select row:" + rowIndex);
23919             if(isSelected){
23920                 this.deselectRow(rowIndex);
23921             } else {
23922                         this.selectRow(rowIndex, true);
23923             }
23924     
23925             /*
23926                 if(e.button !== 0 && isSelected){
23927                 alert('rowIndex 2: ' + rowIndex);
23928                     view.focusRow(rowIndex);
23929                 }else if(e.ctrlKey && isSelected){
23930                     this.deselectRow(rowIndex);
23931                 }else if(!isSelected){
23932                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23933                     view.focusRow(rowIndex);
23934                 }
23935             */
23936         }
23937         this.fireEvent("afterselectionchange", this);
23938     },
23939     // private
23940     handleDragableRowClick :  function(grid, rowIndex, e) 
23941     {
23942         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23943             this.selectRow(rowIndex, false);
23944             grid.view.focusRow(rowIndex);
23945              this.fireEvent("afterselectionchange", this);
23946         }
23947     },
23948     
23949     /**
23950      * Selects multiple rows.
23951      * @param {Array} rows Array of the indexes of the row to select
23952      * @param {Boolean} keepExisting (optional) True to keep existing selections
23953      */
23954     selectRows : function(rows, keepExisting){
23955         if(!keepExisting){
23956             this.clearSelections();
23957         }
23958         for(var i = 0, len = rows.length; i < len; i++){
23959             this.selectRow(rows[i], true);
23960         }
23961     },
23962
23963     /**
23964      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23965      * @param {Number} startRow The index of the first row in the range
23966      * @param {Number} endRow The index of the last row in the range
23967      * @param {Boolean} keepExisting (optional) True to retain existing selections
23968      */
23969     selectRange : function(startRow, endRow, keepExisting){
23970         if(this.locked) {
23971             return;
23972         }
23973         if(!keepExisting){
23974             this.clearSelections();
23975         }
23976         if(startRow <= endRow){
23977             for(var i = startRow; i <= endRow; i++){
23978                 this.selectRow(i, true);
23979             }
23980         }else{
23981             for(var i = startRow; i >= endRow; i--){
23982                 this.selectRow(i, true);
23983             }
23984         }
23985     },
23986
23987     /**
23988      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23989      * @param {Number} startRow The index of the first row in the range
23990      * @param {Number} endRow The index of the last row in the range
23991      */
23992     deselectRange : function(startRow, endRow, preventViewNotify){
23993         if(this.locked) {
23994             return;
23995         }
23996         for(var i = startRow; i <= endRow; i++){
23997             this.deselectRow(i, preventViewNotify);
23998         }
23999     },
24000
24001     /**
24002      * Selects a row.
24003      * @param {Number} row The index of the row to select
24004      * @param {Boolean} keepExisting (optional) True to keep existing selections
24005      */
24006     selectRow : function(index, keepExisting, preventViewNotify)
24007     {
24008             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24009             return;
24010         }
24011         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24012             if(!keepExisting || this.singleSelect){
24013                 this.clearSelections();
24014             }
24015             
24016             var r = this.grid.store.getAt(index);
24017             //console.log('selectRow - record id :' + r.id);
24018             
24019             this.selections.add(r);
24020             this.last = this.lastActive = index;
24021             if(!preventViewNotify){
24022                 var proxy = new Roo.Element(
24023                                 this.grid.getRowDom(index)
24024                 );
24025                 proxy.addClass('bg-info info');
24026             }
24027             this.fireEvent("rowselect", this, index, r);
24028             this.fireEvent("selectionchange", this);
24029         }
24030     },
24031
24032     /**
24033      * Deselects a row.
24034      * @param {Number} row The index of the row to deselect
24035      */
24036     deselectRow : function(index, preventViewNotify)
24037     {
24038         if(this.locked) {
24039             return;
24040         }
24041         if(this.last == index){
24042             this.last = false;
24043         }
24044         if(this.lastActive == index){
24045             this.lastActive = false;
24046         }
24047         
24048         var r = this.grid.store.getAt(index);
24049         if (!r) {
24050             return;
24051         }
24052         
24053         this.selections.remove(r);
24054         //.console.log('deselectRow - record id :' + r.id);
24055         if(!preventViewNotify){
24056         
24057             var proxy = new Roo.Element(
24058                 this.grid.getRowDom(index)
24059             );
24060             proxy.removeClass('bg-info info');
24061         }
24062         this.fireEvent("rowdeselect", this, index);
24063         this.fireEvent("selectionchange", this);
24064     },
24065
24066     // private
24067     restoreLast : function(){
24068         if(this._last){
24069             this.last = this._last;
24070         }
24071     },
24072
24073     // private
24074     acceptsNav : function(row, col, cm){
24075         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24076     },
24077
24078     // private
24079     onEditorKey : function(field, e){
24080         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24081         if(k == e.TAB){
24082             e.stopEvent();
24083             ed.completeEdit();
24084             if(e.shiftKey){
24085                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24086             }else{
24087                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24088             }
24089         }else if(k == e.ENTER && !e.ctrlKey){
24090             e.stopEvent();
24091             ed.completeEdit();
24092             if(e.shiftKey){
24093                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24094             }else{
24095                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24096             }
24097         }else if(k == e.ESC){
24098             ed.cancelEdit();
24099         }
24100         if(newCell){
24101             g.startEditing(newCell[0], newCell[1]);
24102         }
24103     }
24104 });
24105 /*
24106  * Based on:
24107  * Ext JS Library 1.1.1
24108  * Copyright(c) 2006-2007, Ext JS, LLC.
24109  *
24110  * Originally Released Under LGPL - original licence link has changed is not relivant.
24111  *
24112  * Fork - LGPL
24113  * <script type="text/javascript">
24114  */
24115  
24116 /**
24117  * @class Roo.bootstrap.PagingToolbar
24118  * @extends Roo.bootstrap.NavSimplebar
24119  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24120  * @constructor
24121  * Create a new PagingToolbar
24122  * @param {Object} config The config object
24123  * @param {Roo.data.Store} store
24124  */
24125 Roo.bootstrap.PagingToolbar = function(config)
24126 {
24127     // old args format still supported... - xtype is prefered..
24128         // created from xtype...
24129     
24130     this.ds = config.dataSource;
24131     
24132     if (config.store && !this.ds) {
24133         this.store= Roo.factory(config.store, Roo.data);
24134         this.ds = this.store;
24135         this.ds.xmodule = this.xmodule || false;
24136     }
24137     
24138     this.toolbarItems = [];
24139     if (config.items) {
24140         this.toolbarItems = config.items;
24141     }
24142     
24143     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24144     
24145     this.cursor = 0;
24146     
24147     if (this.ds) { 
24148         this.bind(this.ds);
24149     }
24150     
24151     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24152     
24153 };
24154
24155 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24156     /**
24157      * @cfg {Roo.data.Store} dataSource
24158      * The underlying data store providing the paged data
24159      */
24160     /**
24161      * @cfg {String/HTMLElement/Element} container
24162      * container The id or element that will contain the toolbar
24163      */
24164     /**
24165      * @cfg {Boolean} displayInfo
24166      * True to display the displayMsg (defaults to false)
24167      */
24168     /**
24169      * @cfg {Number} pageSize
24170      * The number of records to display per page (defaults to 20)
24171      */
24172     pageSize: 20,
24173     /**
24174      * @cfg {String} displayMsg
24175      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24176      */
24177     displayMsg : 'Displaying {0} - {1} of {2}',
24178     /**
24179      * @cfg {String} emptyMsg
24180      * The message to display when no records are found (defaults to "No data to display")
24181      */
24182     emptyMsg : 'No data to display',
24183     /**
24184      * Customizable piece of the default paging text (defaults to "Page")
24185      * @type String
24186      */
24187     beforePageText : "Page",
24188     /**
24189      * Customizable piece of the default paging text (defaults to "of %0")
24190      * @type String
24191      */
24192     afterPageText : "of {0}",
24193     /**
24194      * Customizable piece of the default paging text (defaults to "First Page")
24195      * @type String
24196      */
24197     firstText : "First Page",
24198     /**
24199      * Customizable piece of the default paging text (defaults to "Previous Page")
24200      * @type String
24201      */
24202     prevText : "Previous Page",
24203     /**
24204      * Customizable piece of the default paging text (defaults to "Next Page")
24205      * @type String
24206      */
24207     nextText : "Next Page",
24208     /**
24209      * Customizable piece of the default paging text (defaults to "Last Page")
24210      * @type String
24211      */
24212     lastText : "Last Page",
24213     /**
24214      * Customizable piece of the default paging text (defaults to "Refresh")
24215      * @type String
24216      */
24217     refreshText : "Refresh",
24218
24219     buttons : false,
24220     // private
24221     onRender : function(ct, position) 
24222     {
24223         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24224         this.navgroup.parentId = this.id;
24225         this.navgroup.onRender(this.el, null);
24226         // add the buttons to the navgroup
24227         
24228         if(this.displayInfo){
24229             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24230             this.displayEl = this.el.select('.x-paging-info', true).first();
24231 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24232 //            this.displayEl = navel.el.select('span',true).first();
24233         }
24234         
24235         var _this = this;
24236         
24237         if(this.buttons){
24238             Roo.each(_this.buttons, function(e){ // this might need to use render????
24239                Roo.factory(e).onRender(_this.el, null);
24240             });
24241         }
24242             
24243         Roo.each(_this.toolbarItems, function(e) {
24244             _this.navgroup.addItem(e);
24245         });
24246         
24247         
24248         this.first = this.navgroup.addItem({
24249             tooltip: this.firstText,
24250             cls: "prev",
24251             icon : 'fa fa-backward',
24252             disabled: true,
24253             preventDefault: true,
24254             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24255         });
24256         
24257         this.prev =  this.navgroup.addItem({
24258             tooltip: this.prevText,
24259             cls: "prev",
24260             icon : 'fa fa-step-backward',
24261             disabled: true,
24262             preventDefault: true,
24263             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24264         });
24265     //this.addSeparator();
24266         
24267         
24268         var field = this.navgroup.addItem( {
24269             tagtype : 'span',
24270             cls : 'x-paging-position',
24271             
24272             html : this.beforePageText  +
24273                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24274                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24275          } ); //?? escaped?
24276         
24277         this.field = field.el.select('input', true).first();
24278         this.field.on("keydown", this.onPagingKeydown, this);
24279         this.field.on("focus", function(){this.dom.select();});
24280     
24281     
24282         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24283         //this.field.setHeight(18);
24284         //this.addSeparator();
24285         this.next = this.navgroup.addItem({
24286             tooltip: this.nextText,
24287             cls: "next",
24288             html : ' <i class="fa fa-step-forward">',
24289             disabled: true,
24290             preventDefault: true,
24291             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24292         });
24293         this.last = this.navgroup.addItem({
24294             tooltip: this.lastText,
24295             icon : 'fa fa-forward',
24296             cls: "next",
24297             disabled: true,
24298             preventDefault: true,
24299             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24300         });
24301     //this.addSeparator();
24302         this.loading = this.navgroup.addItem({
24303             tooltip: this.refreshText,
24304             icon: 'fa fa-refresh',
24305             preventDefault: true,
24306             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24307         });
24308         
24309     },
24310
24311     // private
24312     updateInfo : function(){
24313         if(this.displayEl){
24314             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24315             var msg = count == 0 ?
24316                 this.emptyMsg :
24317                 String.format(
24318                     this.displayMsg,
24319                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24320                 );
24321             this.displayEl.update(msg);
24322         }
24323     },
24324
24325     // private
24326     onLoad : function(ds, r, o)
24327     {
24328         this.cursor = o.params ? o.params.start : 0;
24329         var d = this.getPageData(),
24330             ap = d.activePage,
24331             ps = d.pages;
24332         
24333         
24334         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24335         this.field.dom.value = ap;
24336         this.first.setDisabled(ap == 1);
24337         this.prev.setDisabled(ap == 1);
24338         this.next.setDisabled(ap == ps);
24339         this.last.setDisabled(ap == ps);
24340         this.loading.enable();
24341         this.updateInfo();
24342     },
24343
24344     // private
24345     getPageData : function(){
24346         var total = this.ds.getTotalCount();
24347         return {
24348             total : total,
24349             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24350             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24351         };
24352     },
24353
24354     // private
24355     onLoadError : function(){
24356         this.loading.enable();
24357     },
24358
24359     // private
24360     onPagingKeydown : function(e){
24361         var k = e.getKey();
24362         var d = this.getPageData();
24363         if(k == e.RETURN){
24364             var v = this.field.dom.value, pageNum;
24365             if(!v || isNaN(pageNum = parseInt(v, 10))){
24366                 this.field.dom.value = d.activePage;
24367                 return;
24368             }
24369             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24370             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24371             e.stopEvent();
24372         }
24373         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))
24374         {
24375           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24376           this.field.dom.value = pageNum;
24377           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24378           e.stopEvent();
24379         }
24380         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24381         {
24382           var v = this.field.dom.value, pageNum; 
24383           var increment = (e.shiftKey) ? 10 : 1;
24384           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24385                 increment *= -1;
24386           }
24387           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24388             this.field.dom.value = d.activePage;
24389             return;
24390           }
24391           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24392           {
24393             this.field.dom.value = parseInt(v, 10) + increment;
24394             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24395             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24396           }
24397           e.stopEvent();
24398         }
24399     },
24400
24401     // private
24402     beforeLoad : function(){
24403         if(this.loading){
24404             this.loading.disable();
24405         }
24406     },
24407
24408     // private
24409     onClick : function(which){
24410         
24411         var ds = this.ds;
24412         if (!ds) {
24413             return;
24414         }
24415         
24416         switch(which){
24417             case "first":
24418                 ds.load({params:{start: 0, limit: this.pageSize}});
24419             break;
24420             case "prev":
24421                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24422             break;
24423             case "next":
24424                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24425             break;
24426             case "last":
24427                 var total = ds.getTotalCount();
24428                 var extra = total % this.pageSize;
24429                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24430                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24431             break;
24432             case "refresh":
24433                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24434             break;
24435         }
24436     },
24437
24438     /**
24439      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24440      * @param {Roo.data.Store} store The data store to unbind
24441      */
24442     unbind : function(ds){
24443         ds.un("beforeload", this.beforeLoad, this);
24444         ds.un("load", this.onLoad, this);
24445         ds.un("loadexception", this.onLoadError, this);
24446         ds.un("remove", this.updateInfo, this);
24447         ds.un("add", this.updateInfo, this);
24448         this.ds = undefined;
24449     },
24450
24451     /**
24452      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24453      * @param {Roo.data.Store} store The data store to bind
24454      */
24455     bind : function(ds){
24456         ds.on("beforeload", this.beforeLoad, this);
24457         ds.on("load", this.onLoad, this);
24458         ds.on("loadexception", this.onLoadError, this);
24459         ds.on("remove", this.updateInfo, this);
24460         ds.on("add", this.updateInfo, this);
24461         this.ds = ds;
24462     }
24463 });/*
24464  * - LGPL
24465  *
24466  * element
24467  * 
24468  */
24469
24470 /**
24471  * @class Roo.bootstrap.MessageBar
24472  * @extends Roo.bootstrap.Component
24473  * Bootstrap MessageBar class
24474  * @cfg {String} html contents of the MessageBar
24475  * @cfg {String} weight (info | success | warning | danger) default info
24476  * @cfg {String} beforeClass insert the bar before the given class
24477  * @cfg {Boolean} closable (true | false) default false
24478  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24479  * 
24480  * @constructor
24481  * Create a new Element
24482  * @param {Object} config The config object
24483  */
24484
24485 Roo.bootstrap.MessageBar = function(config){
24486     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24487 };
24488
24489 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24490     
24491     html: '',
24492     weight: 'info',
24493     closable: false,
24494     fixed: false,
24495     beforeClass: 'bootstrap-sticky-wrap',
24496     
24497     getAutoCreate : function(){
24498         
24499         var cfg = {
24500             tag: 'div',
24501             cls: 'alert alert-dismissable alert-' + this.weight,
24502             cn: [
24503                 {
24504                     tag: 'span',
24505                     cls: 'message',
24506                     html: this.html || ''
24507                 }
24508             ]
24509         };
24510         
24511         if(this.fixed){
24512             cfg.cls += ' alert-messages-fixed';
24513         }
24514         
24515         if(this.closable){
24516             cfg.cn.push({
24517                 tag: 'button',
24518                 cls: 'close',
24519                 html: 'x'
24520             });
24521         }
24522         
24523         return cfg;
24524     },
24525     
24526     onRender : function(ct, position)
24527     {
24528         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24529         
24530         if(!this.el){
24531             var cfg = Roo.apply({},  this.getAutoCreate());
24532             cfg.id = Roo.id();
24533             
24534             if (this.cls) {
24535                 cfg.cls += ' ' + this.cls;
24536             }
24537             if (this.style) {
24538                 cfg.style = this.style;
24539             }
24540             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24541             
24542             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24543         }
24544         
24545         this.el.select('>button.close').on('click', this.hide, this);
24546         
24547     },
24548     
24549     show : function()
24550     {
24551         if (!this.rendered) {
24552             this.render();
24553         }
24554         
24555         this.el.show();
24556         
24557         this.fireEvent('show', this);
24558         
24559     },
24560     
24561     hide : function()
24562     {
24563         if (!this.rendered) {
24564             this.render();
24565         }
24566         
24567         this.el.hide();
24568         
24569         this.fireEvent('hide', this);
24570     },
24571     
24572     update : function()
24573     {
24574 //        var e = this.el.dom.firstChild;
24575 //        
24576 //        if(this.closable){
24577 //            e = e.nextSibling;
24578 //        }
24579 //        
24580 //        e.data = this.html || '';
24581
24582         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24583     }
24584    
24585 });
24586
24587  
24588
24589      /*
24590  * - LGPL
24591  *
24592  * Graph
24593  * 
24594  */
24595
24596
24597 /**
24598  * @class Roo.bootstrap.Graph
24599  * @extends Roo.bootstrap.Component
24600  * Bootstrap Graph class
24601 > Prameters
24602  -sm {number} sm 4
24603  -md {number} md 5
24604  @cfg {String} graphtype  bar | vbar | pie
24605  @cfg {number} g_x coodinator | centre x (pie)
24606  @cfg {number} g_y coodinator | centre y (pie)
24607  @cfg {number} g_r radius (pie)
24608  @cfg {number} g_height height of the chart (respected by all elements in the set)
24609  @cfg {number} g_width width of the chart (respected by all elements in the set)
24610  @cfg {Object} title The title of the chart
24611     
24612  -{Array}  values
24613  -opts (object) options for the chart 
24614      o {
24615      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24616      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24617      o vgutter (number)
24618      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.
24619      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24620      o to
24621      o stretch (boolean)
24622      o }
24623  -opts (object) options for the pie
24624      o{
24625      o cut
24626      o startAngle (number)
24627      o endAngle (number)
24628      } 
24629  *
24630  * @constructor
24631  * Create a new Input
24632  * @param {Object} config The config object
24633  */
24634
24635 Roo.bootstrap.Graph = function(config){
24636     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24637     
24638     this.addEvents({
24639         // img events
24640         /**
24641          * @event click
24642          * The img click event for the img.
24643          * @param {Roo.EventObject} e
24644          */
24645         "click" : true
24646     });
24647 };
24648
24649 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24650     
24651     sm: 4,
24652     md: 5,
24653     graphtype: 'bar',
24654     g_height: 250,
24655     g_width: 400,
24656     g_x: 50,
24657     g_y: 50,
24658     g_r: 30,
24659     opts:{
24660         //g_colors: this.colors,
24661         g_type: 'soft',
24662         g_gutter: '20%'
24663
24664     },
24665     title : false,
24666
24667     getAutoCreate : function(){
24668         
24669         var cfg = {
24670             tag: 'div',
24671             html : null
24672         };
24673         
24674         
24675         return  cfg;
24676     },
24677
24678     onRender : function(ct,position){
24679         
24680         
24681         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24682         
24683         if (typeof(Raphael) == 'undefined') {
24684             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24685             return;
24686         }
24687         
24688         this.raphael = Raphael(this.el.dom);
24689         
24690                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24691                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24692                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24693                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24694                 /*
24695                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24696                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24697                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24698                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24699                 
24700                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24701                 r.barchart(330, 10, 300, 220, data1);
24702                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24703                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24704                 */
24705                 
24706                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24707                 // r.barchart(30, 30, 560, 250,  xdata, {
24708                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24709                 //     axis : "0 0 1 1",
24710                 //     axisxlabels :  xdata
24711                 //     //yvalues : cols,
24712                    
24713                 // });
24714 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24715 //        
24716 //        this.load(null,xdata,{
24717 //                axis : "0 0 1 1",
24718 //                axisxlabels :  xdata
24719 //                });
24720
24721     },
24722
24723     load : function(graphtype,xdata,opts)
24724     {
24725         this.raphael.clear();
24726         if(!graphtype) {
24727             graphtype = this.graphtype;
24728         }
24729         if(!opts){
24730             opts = this.opts;
24731         }
24732         var r = this.raphael,
24733             fin = function () {
24734                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24735             },
24736             fout = function () {
24737                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24738             },
24739             pfin = function() {
24740                 this.sector.stop();
24741                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24742
24743                 if (this.label) {
24744                     this.label[0].stop();
24745                     this.label[0].attr({ r: 7.5 });
24746                     this.label[1].attr({ "font-weight": 800 });
24747                 }
24748             },
24749             pfout = function() {
24750                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24751
24752                 if (this.label) {
24753                     this.label[0].animate({ r: 5 }, 500, "bounce");
24754                     this.label[1].attr({ "font-weight": 400 });
24755                 }
24756             };
24757
24758         switch(graphtype){
24759             case 'bar':
24760                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24761                 break;
24762             case 'hbar':
24763                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24764                 break;
24765             case 'pie':
24766 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24767 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24768 //            
24769                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24770                 
24771                 break;
24772
24773         }
24774         
24775         if(this.title){
24776             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24777         }
24778         
24779     },
24780     
24781     setTitle: function(o)
24782     {
24783         this.title = o;
24784     },
24785     
24786     initEvents: function() {
24787         
24788         if(!this.href){
24789             this.el.on('click', this.onClick, this);
24790         }
24791     },
24792     
24793     onClick : function(e)
24794     {
24795         Roo.log('img onclick');
24796         this.fireEvent('click', this, e);
24797     }
24798    
24799 });
24800
24801  
24802 /*
24803  * - LGPL
24804  *
24805  * numberBox
24806  * 
24807  */
24808 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24809
24810 /**
24811  * @class Roo.bootstrap.dash.NumberBox
24812  * @extends Roo.bootstrap.Component
24813  * Bootstrap NumberBox class
24814  * @cfg {String} headline Box headline
24815  * @cfg {String} content Box content
24816  * @cfg {String} icon Box icon
24817  * @cfg {String} footer Footer text
24818  * @cfg {String} fhref Footer href
24819  * 
24820  * @constructor
24821  * Create a new NumberBox
24822  * @param {Object} config The config object
24823  */
24824
24825
24826 Roo.bootstrap.dash.NumberBox = function(config){
24827     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24828     
24829 };
24830
24831 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24832     
24833     headline : '',
24834     content : '',
24835     icon : '',
24836     footer : '',
24837     fhref : '',
24838     ficon : '',
24839     
24840     getAutoCreate : function(){
24841         
24842         var cfg = {
24843             tag : 'div',
24844             cls : 'small-box ',
24845             cn : [
24846                 {
24847                     tag : 'div',
24848                     cls : 'inner',
24849                     cn :[
24850                         {
24851                             tag : 'h3',
24852                             cls : 'roo-headline',
24853                             html : this.headline
24854                         },
24855                         {
24856                             tag : 'p',
24857                             cls : 'roo-content',
24858                             html : this.content
24859                         }
24860                     ]
24861                 }
24862             ]
24863         };
24864         
24865         if(this.icon){
24866             cfg.cn.push({
24867                 tag : 'div',
24868                 cls : 'icon',
24869                 cn :[
24870                     {
24871                         tag : 'i',
24872                         cls : 'ion ' + this.icon
24873                     }
24874                 ]
24875             });
24876         }
24877         
24878         if(this.footer){
24879             var footer = {
24880                 tag : 'a',
24881                 cls : 'small-box-footer',
24882                 href : this.fhref || '#',
24883                 html : this.footer
24884             };
24885             
24886             cfg.cn.push(footer);
24887             
24888         }
24889         
24890         return  cfg;
24891     },
24892
24893     onRender : function(ct,position){
24894         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24895
24896
24897        
24898                 
24899     },
24900
24901     setHeadline: function (value)
24902     {
24903         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24904     },
24905     
24906     setFooter: function (value, href)
24907     {
24908         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24909         
24910         if(href){
24911             this.el.select('a.small-box-footer',true).first().attr('href', href);
24912         }
24913         
24914     },
24915
24916     setContent: function (value)
24917     {
24918         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24919     },
24920
24921     initEvents: function() 
24922     {   
24923         
24924     }
24925     
24926 });
24927
24928  
24929 /*
24930  * - LGPL
24931  *
24932  * TabBox
24933  * 
24934  */
24935 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24936
24937 /**
24938  * @class Roo.bootstrap.dash.TabBox
24939  * @extends Roo.bootstrap.Component
24940  * Bootstrap TabBox class
24941  * @cfg {String} title Title of the TabBox
24942  * @cfg {String} icon Icon of the TabBox
24943  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24944  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24945  * 
24946  * @constructor
24947  * Create a new TabBox
24948  * @param {Object} config The config object
24949  */
24950
24951
24952 Roo.bootstrap.dash.TabBox = function(config){
24953     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24954     this.addEvents({
24955         // raw events
24956         /**
24957          * @event addpane
24958          * When a pane is added
24959          * @param {Roo.bootstrap.dash.TabPane} pane
24960          */
24961         "addpane" : true,
24962         /**
24963          * @event activatepane
24964          * When a pane is activated
24965          * @param {Roo.bootstrap.dash.TabPane} pane
24966          */
24967         "activatepane" : true
24968         
24969          
24970     });
24971     
24972     this.panes = [];
24973 };
24974
24975 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24976
24977     title : '',
24978     icon : false,
24979     showtabs : true,
24980     tabScrollable : false,
24981     
24982     getChildContainer : function()
24983     {
24984         return this.el.select('.tab-content', true).first();
24985     },
24986     
24987     getAutoCreate : function(){
24988         
24989         var header = {
24990             tag: 'li',
24991             cls: 'pull-left header',
24992             html: this.title,
24993             cn : []
24994         };
24995         
24996         if(this.icon){
24997             header.cn.push({
24998                 tag: 'i',
24999                 cls: 'fa ' + this.icon
25000             });
25001         }
25002         
25003         var h = {
25004             tag: 'ul',
25005             cls: 'nav nav-tabs pull-right',
25006             cn: [
25007                 header
25008             ]
25009         };
25010         
25011         if(this.tabScrollable){
25012             h = {
25013                 tag: 'div',
25014                 cls: 'tab-header',
25015                 cn: [
25016                     {
25017                         tag: 'ul',
25018                         cls: 'nav nav-tabs pull-right',
25019                         cn: [
25020                             header
25021                         ]
25022                     }
25023                 ]
25024             };
25025         }
25026         
25027         var cfg = {
25028             tag: 'div',
25029             cls: 'nav-tabs-custom',
25030             cn: [
25031                 h,
25032                 {
25033                     tag: 'div',
25034                     cls: 'tab-content no-padding',
25035                     cn: []
25036                 }
25037             ]
25038         };
25039
25040         return  cfg;
25041     },
25042     initEvents : function()
25043     {
25044         //Roo.log('add add pane handler');
25045         this.on('addpane', this.onAddPane, this);
25046     },
25047      /**
25048      * Updates the box title
25049      * @param {String} html to set the title to.
25050      */
25051     setTitle : function(value)
25052     {
25053         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25054     },
25055     onAddPane : function(pane)
25056     {
25057         this.panes.push(pane);
25058         //Roo.log('addpane');
25059         //Roo.log(pane);
25060         // tabs are rendere left to right..
25061         if(!this.showtabs){
25062             return;
25063         }
25064         
25065         var ctr = this.el.select('.nav-tabs', true).first();
25066          
25067          
25068         var existing = ctr.select('.nav-tab',true);
25069         var qty = existing.getCount();;
25070         
25071         
25072         var tab = ctr.createChild({
25073             tag : 'li',
25074             cls : 'nav-tab' + (qty ? '' : ' active'),
25075             cn : [
25076                 {
25077                     tag : 'a',
25078                     href:'#',
25079                     html : pane.title
25080                 }
25081             ]
25082         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25083         pane.tab = tab;
25084         
25085         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25086         if (!qty) {
25087             pane.el.addClass('active');
25088         }
25089         
25090                 
25091     },
25092     onTabClick : function(ev,un,ob,pane)
25093     {
25094         //Roo.log('tab - prev default');
25095         ev.preventDefault();
25096         
25097         
25098         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25099         pane.tab.addClass('active');
25100         //Roo.log(pane.title);
25101         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25102         // technically we should have a deactivate event.. but maybe add later.
25103         // and it should not de-activate the selected tab...
25104         this.fireEvent('activatepane', pane);
25105         pane.el.addClass('active');
25106         pane.fireEvent('activate');
25107         
25108         
25109     },
25110     
25111     getActivePane : function()
25112     {
25113         var r = false;
25114         Roo.each(this.panes, function(p) {
25115             if(p.el.hasClass('active')){
25116                 r = p;
25117                 return false;
25118             }
25119             
25120             return;
25121         });
25122         
25123         return r;
25124     }
25125     
25126     
25127 });
25128
25129  
25130 /*
25131  * - LGPL
25132  *
25133  * Tab pane
25134  * 
25135  */
25136 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25137 /**
25138  * @class Roo.bootstrap.TabPane
25139  * @extends Roo.bootstrap.Component
25140  * Bootstrap TabPane class
25141  * @cfg {Boolean} active (false | true) Default false
25142  * @cfg {String} title title of panel
25143
25144  * 
25145  * @constructor
25146  * Create a new TabPane
25147  * @param {Object} config The config object
25148  */
25149
25150 Roo.bootstrap.dash.TabPane = function(config){
25151     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25152     
25153     this.addEvents({
25154         // raw events
25155         /**
25156          * @event activate
25157          * When a pane is activated
25158          * @param {Roo.bootstrap.dash.TabPane} pane
25159          */
25160         "activate" : true
25161          
25162     });
25163 };
25164
25165 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25166     
25167     active : false,
25168     title : '',
25169     
25170     // the tabBox that this is attached to.
25171     tab : false,
25172      
25173     getAutoCreate : function() 
25174     {
25175         var cfg = {
25176             tag: 'div',
25177             cls: 'tab-pane'
25178         };
25179         
25180         if(this.active){
25181             cfg.cls += ' active';
25182         }
25183         
25184         return cfg;
25185     },
25186     initEvents  : function()
25187     {
25188         //Roo.log('trigger add pane handler');
25189         this.parent().fireEvent('addpane', this)
25190     },
25191     
25192      /**
25193      * Updates the tab title 
25194      * @param {String} html to set the title to.
25195      */
25196     setTitle: function(str)
25197     {
25198         if (!this.tab) {
25199             return;
25200         }
25201         this.title = str;
25202         this.tab.select('a', true).first().dom.innerHTML = str;
25203         
25204     }
25205     
25206     
25207     
25208 });
25209
25210  
25211
25212
25213  /*
25214  * - LGPL
25215  *
25216  * menu
25217  * 
25218  */
25219 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25220
25221 /**
25222  * @class Roo.bootstrap.menu.Menu
25223  * @extends Roo.bootstrap.Component
25224  * Bootstrap Menu class - container for Menu
25225  * @cfg {String} html Text of the menu
25226  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25227  * @cfg {String} icon Font awesome icon
25228  * @cfg {String} pos Menu align to (top | bottom) default bottom
25229  * 
25230  * 
25231  * @constructor
25232  * Create a new Menu
25233  * @param {Object} config The config object
25234  */
25235
25236
25237 Roo.bootstrap.menu.Menu = function(config){
25238     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25239     
25240     this.addEvents({
25241         /**
25242          * @event beforeshow
25243          * Fires before this menu is displayed
25244          * @param {Roo.bootstrap.menu.Menu} this
25245          */
25246         beforeshow : true,
25247         /**
25248          * @event beforehide
25249          * Fires before this menu is hidden
25250          * @param {Roo.bootstrap.menu.Menu} this
25251          */
25252         beforehide : true,
25253         /**
25254          * @event show
25255          * Fires after this menu is displayed
25256          * @param {Roo.bootstrap.menu.Menu} this
25257          */
25258         show : true,
25259         /**
25260          * @event hide
25261          * Fires after this menu is hidden
25262          * @param {Roo.bootstrap.menu.Menu} this
25263          */
25264         hide : true,
25265         /**
25266          * @event click
25267          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25268          * @param {Roo.bootstrap.menu.Menu} this
25269          * @param {Roo.EventObject} e
25270          */
25271         click : true
25272     });
25273     
25274 };
25275
25276 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25277     
25278     submenu : false,
25279     html : '',
25280     weight : 'default',
25281     icon : false,
25282     pos : 'bottom',
25283     
25284     
25285     getChildContainer : function() {
25286         if(this.isSubMenu){
25287             return this.el;
25288         }
25289         
25290         return this.el.select('ul.dropdown-menu', true).first();  
25291     },
25292     
25293     getAutoCreate : function()
25294     {
25295         var text = [
25296             {
25297                 tag : 'span',
25298                 cls : 'roo-menu-text',
25299                 html : this.html
25300             }
25301         ];
25302         
25303         if(this.icon){
25304             text.unshift({
25305                 tag : 'i',
25306                 cls : 'fa ' + this.icon
25307             })
25308         }
25309         
25310         
25311         var cfg = {
25312             tag : 'div',
25313             cls : 'btn-group',
25314             cn : [
25315                 {
25316                     tag : 'button',
25317                     cls : 'dropdown-button btn btn-' + this.weight,
25318                     cn : text
25319                 },
25320                 {
25321                     tag : 'button',
25322                     cls : 'dropdown-toggle btn btn-' + this.weight,
25323                     cn : [
25324                         {
25325                             tag : 'span',
25326                             cls : 'caret'
25327                         }
25328                     ]
25329                 },
25330                 {
25331                     tag : 'ul',
25332                     cls : 'dropdown-menu'
25333                 }
25334             ]
25335             
25336         };
25337         
25338         if(this.pos == 'top'){
25339             cfg.cls += ' dropup';
25340         }
25341         
25342         if(this.isSubMenu){
25343             cfg = {
25344                 tag : 'ul',
25345                 cls : 'dropdown-menu'
25346             }
25347         }
25348         
25349         return cfg;
25350     },
25351     
25352     onRender : function(ct, position)
25353     {
25354         this.isSubMenu = ct.hasClass('dropdown-submenu');
25355         
25356         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25357     },
25358     
25359     initEvents : function() 
25360     {
25361         if(this.isSubMenu){
25362             return;
25363         }
25364         
25365         this.hidden = true;
25366         
25367         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25368         this.triggerEl.on('click', this.onTriggerPress, this);
25369         
25370         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25371         this.buttonEl.on('click', this.onClick, this);
25372         
25373     },
25374     
25375     list : function()
25376     {
25377         if(this.isSubMenu){
25378             return this.el;
25379         }
25380         
25381         return this.el.select('ul.dropdown-menu', true).first();
25382     },
25383     
25384     onClick : function(e)
25385     {
25386         this.fireEvent("click", this, e);
25387     },
25388     
25389     onTriggerPress  : function(e)
25390     {   
25391         if (this.isVisible()) {
25392             this.hide();
25393         } else {
25394             this.show();
25395         }
25396     },
25397     
25398     isVisible : function(){
25399         return !this.hidden;
25400     },
25401     
25402     show : function()
25403     {
25404         this.fireEvent("beforeshow", this);
25405         
25406         this.hidden = false;
25407         this.el.addClass('open');
25408         
25409         Roo.get(document).on("mouseup", this.onMouseUp, this);
25410         
25411         this.fireEvent("show", this);
25412         
25413         
25414     },
25415     
25416     hide : function()
25417     {
25418         this.fireEvent("beforehide", this);
25419         
25420         this.hidden = true;
25421         this.el.removeClass('open');
25422         
25423         Roo.get(document).un("mouseup", this.onMouseUp);
25424         
25425         this.fireEvent("hide", this);
25426     },
25427     
25428     onMouseUp : function()
25429     {
25430         this.hide();
25431     }
25432     
25433 });
25434
25435  
25436  /*
25437  * - LGPL
25438  *
25439  * menu item
25440  * 
25441  */
25442 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25443
25444 /**
25445  * @class Roo.bootstrap.menu.Item
25446  * @extends Roo.bootstrap.Component
25447  * Bootstrap MenuItem class
25448  * @cfg {Boolean} submenu (true | false) default false
25449  * @cfg {String} html text of the item
25450  * @cfg {String} href the link
25451  * @cfg {Boolean} disable (true | false) default false
25452  * @cfg {Boolean} preventDefault (true | false) default true
25453  * @cfg {String} icon Font awesome icon
25454  * @cfg {String} pos Submenu align to (left | right) default right 
25455  * 
25456  * 
25457  * @constructor
25458  * Create a new Item
25459  * @param {Object} config The config object
25460  */
25461
25462
25463 Roo.bootstrap.menu.Item = function(config){
25464     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25465     this.addEvents({
25466         /**
25467          * @event mouseover
25468          * Fires when the mouse is hovering over this menu
25469          * @param {Roo.bootstrap.menu.Item} this
25470          * @param {Roo.EventObject} e
25471          */
25472         mouseover : true,
25473         /**
25474          * @event mouseout
25475          * Fires when the mouse exits this menu
25476          * @param {Roo.bootstrap.menu.Item} this
25477          * @param {Roo.EventObject} e
25478          */
25479         mouseout : true,
25480         // raw events
25481         /**
25482          * @event click
25483          * The raw click event for the entire grid.
25484          * @param {Roo.EventObject} e
25485          */
25486         click : true
25487     });
25488 };
25489
25490 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25491     
25492     submenu : false,
25493     href : '',
25494     html : '',
25495     preventDefault: true,
25496     disable : false,
25497     icon : false,
25498     pos : 'right',
25499     
25500     getAutoCreate : function()
25501     {
25502         var text = [
25503             {
25504                 tag : 'span',
25505                 cls : 'roo-menu-item-text',
25506                 html : this.html
25507             }
25508         ];
25509         
25510         if(this.icon){
25511             text.unshift({
25512                 tag : 'i',
25513                 cls : 'fa ' + this.icon
25514             })
25515         }
25516         
25517         var cfg = {
25518             tag : 'li',
25519             cn : [
25520                 {
25521                     tag : 'a',
25522                     href : this.href || '#',
25523                     cn : text
25524                 }
25525             ]
25526         };
25527         
25528         if(this.disable){
25529             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25530         }
25531         
25532         if(this.submenu){
25533             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25534             
25535             if(this.pos == 'left'){
25536                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25537             }
25538         }
25539         
25540         return cfg;
25541     },
25542     
25543     initEvents : function() 
25544     {
25545         this.el.on('mouseover', this.onMouseOver, this);
25546         this.el.on('mouseout', this.onMouseOut, this);
25547         
25548         this.el.select('a', true).first().on('click', this.onClick, this);
25549         
25550     },
25551     
25552     onClick : function(e)
25553     {
25554         if(this.preventDefault){
25555             e.preventDefault();
25556         }
25557         
25558         this.fireEvent("click", this, e);
25559     },
25560     
25561     onMouseOver : function(e)
25562     {
25563         if(this.submenu && this.pos == 'left'){
25564             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25565         }
25566         
25567         this.fireEvent("mouseover", this, e);
25568     },
25569     
25570     onMouseOut : function(e)
25571     {
25572         this.fireEvent("mouseout", this, e);
25573     }
25574 });
25575
25576  
25577
25578  /*
25579  * - LGPL
25580  *
25581  * menu separator
25582  * 
25583  */
25584 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25585
25586 /**
25587  * @class Roo.bootstrap.menu.Separator
25588  * @extends Roo.bootstrap.Component
25589  * Bootstrap Separator class
25590  * 
25591  * @constructor
25592  * Create a new Separator
25593  * @param {Object} config The config object
25594  */
25595
25596
25597 Roo.bootstrap.menu.Separator = function(config){
25598     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25599 };
25600
25601 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25602     
25603     getAutoCreate : function(){
25604         var cfg = {
25605             tag : 'li',
25606             cls: 'divider'
25607         };
25608         
25609         return cfg;
25610     }
25611    
25612 });
25613
25614  
25615
25616  /*
25617  * - LGPL
25618  *
25619  * Tooltip
25620  * 
25621  */
25622
25623 /**
25624  * @class Roo.bootstrap.Tooltip
25625  * Bootstrap Tooltip class
25626  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25627  * to determine which dom element triggers the tooltip.
25628  * 
25629  * It needs to add support for additional attributes like tooltip-position
25630  * 
25631  * @constructor
25632  * Create a new Toolti
25633  * @param {Object} config The config object
25634  */
25635
25636 Roo.bootstrap.Tooltip = function(config){
25637     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25638     
25639     this.alignment = Roo.bootstrap.Tooltip.alignment;
25640     
25641     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25642         this.alignment = config.alignment;
25643     }
25644     
25645 };
25646
25647 Roo.apply(Roo.bootstrap.Tooltip, {
25648     /**
25649      * @function init initialize tooltip monitoring.
25650      * @static
25651      */
25652     currentEl : false,
25653     currentTip : false,
25654     currentRegion : false,
25655     
25656     //  init : delay?
25657     
25658     init : function()
25659     {
25660         Roo.get(document).on('mouseover', this.enter ,this);
25661         Roo.get(document).on('mouseout', this.leave, this);
25662          
25663         
25664         this.currentTip = new Roo.bootstrap.Tooltip();
25665     },
25666     
25667     enter : function(ev)
25668     {
25669         var dom = ev.getTarget();
25670         
25671         //Roo.log(['enter',dom]);
25672         var el = Roo.fly(dom);
25673         if (this.currentEl) {
25674             //Roo.log(dom);
25675             //Roo.log(this.currentEl);
25676             //Roo.log(this.currentEl.contains(dom));
25677             if (this.currentEl == el) {
25678                 return;
25679             }
25680             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25681                 return;
25682             }
25683
25684         }
25685         
25686         if (this.currentTip.el) {
25687             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25688         }    
25689         //Roo.log(ev);
25690         
25691         if(!el || el.dom == document){
25692             return;
25693         }
25694         
25695         var bindEl = el;
25696         
25697         // you can not look for children, as if el is the body.. then everythign is the child..
25698         if (!el.attr('tooltip')) { //
25699             if (!el.select("[tooltip]").elements.length) {
25700                 return;
25701             }
25702             // is the mouse over this child...?
25703             bindEl = el.select("[tooltip]").first();
25704             var xy = ev.getXY();
25705             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25706                 //Roo.log("not in region.");
25707                 return;
25708             }
25709             //Roo.log("child element over..");
25710             
25711         }
25712         this.currentEl = bindEl;
25713         this.currentTip.bind(bindEl);
25714         this.currentRegion = Roo.lib.Region.getRegion(dom);
25715         this.currentTip.enter();
25716         
25717     },
25718     leave : function(ev)
25719     {
25720         var dom = ev.getTarget();
25721         //Roo.log(['leave',dom]);
25722         if (!this.currentEl) {
25723             return;
25724         }
25725         
25726         
25727         if (dom != this.currentEl.dom) {
25728             return;
25729         }
25730         var xy = ev.getXY();
25731         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25732             return;
25733         }
25734         // only activate leave if mouse cursor is outside... bounding box..
25735         
25736         
25737         
25738         
25739         if (this.currentTip) {
25740             this.currentTip.leave();
25741         }
25742         //Roo.log('clear currentEl');
25743         this.currentEl = false;
25744         
25745         
25746     },
25747     alignment : {
25748         'left' : ['r-l', [-2,0], 'right'],
25749         'right' : ['l-r', [2,0], 'left'],
25750         'bottom' : ['t-b', [0,2], 'top'],
25751         'top' : [ 'b-t', [0,-2], 'bottom']
25752     }
25753     
25754 });
25755
25756
25757 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25758     
25759     
25760     bindEl : false,
25761     
25762     delay : null, // can be { show : 300 , hide: 500}
25763     
25764     timeout : null,
25765     
25766     hoverState : null, //???
25767     
25768     placement : 'bottom', 
25769     
25770     alignment : false,
25771     
25772     getAutoCreate : function(){
25773     
25774         var cfg = {
25775            cls : 'tooltip',
25776            role : 'tooltip',
25777            cn : [
25778                 {
25779                     cls : 'tooltip-arrow'
25780                 },
25781                 {
25782                     cls : 'tooltip-inner'
25783                 }
25784            ]
25785         };
25786         
25787         return cfg;
25788     },
25789     bind : function(el)
25790     {
25791         this.bindEl = el;
25792     },
25793       
25794     
25795     enter : function () {
25796        
25797         if (this.timeout != null) {
25798             clearTimeout(this.timeout);
25799         }
25800         
25801         this.hoverState = 'in';
25802          //Roo.log("enter - show");
25803         if (!this.delay || !this.delay.show) {
25804             this.show();
25805             return;
25806         }
25807         var _t = this;
25808         this.timeout = setTimeout(function () {
25809             if (_t.hoverState == 'in') {
25810                 _t.show();
25811             }
25812         }, this.delay.show);
25813     },
25814     leave : function()
25815     {
25816         clearTimeout(this.timeout);
25817     
25818         this.hoverState = 'out';
25819          if (!this.delay || !this.delay.hide) {
25820             this.hide();
25821             return;
25822         }
25823        
25824         var _t = this;
25825         this.timeout = setTimeout(function () {
25826             //Roo.log("leave - timeout");
25827             
25828             if (_t.hoverState == 'out') {
25829                 _t.hide();
25830                 Roo.bootstrap.Tooltip.currentEl = false;
25831             }
25832         }, delay);
25833     },
25834     
25835     show : function (msg)
25836     {
25837         if (!this.el) {
25838             this.render(document.body);
25839         }
25840         // set content.
25841         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25842         
25843         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25844         
25845         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25846         
25847         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25848         
25849         var placement = typeof this.placement == 'function' ?
25850             this.placement.call(this, this.el, on_el) :
25851             this.placement;
25852             
25853         var autoToken = /\s?auto?\s?/i;
25854         var autoPlace = autoToken.test(placement);
25855         if (autoPlace) {
25856             placement = placement.replace(autoToken, '') || 'top';
25857         }
25858         
25859         //this.el.detach()
25860         //this.el.setXY([0,0]);
25861         this.el.show();
25862         //this.el.dom.style.display='block';
25863         
25864         //this.el.appendTo(on_el);
25865         
25866         var p = this.getPosition();
25867         var box = this.el.getBox();
25868         
25869         if (autoPlace) {
25870             // fixme..
25871         }
25872         
25873         var align = this.alignment[placement];
25874         
25875         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25876         
25877         if(placement == 'top' || placement == 'bottom'){
25878             if(xy[0] < 0){
25879                 placement = 'right';
25880             }
25881             
25882             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25883                 placement = 'left';
25884             }
25885             
25886             var scroll = Roo.select('body', true).first().getScroll();
25887             
25888             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25889                 placement = 'top';
25890             }
25891             
25892         }
25893         
25894         this.el.alignTo(this.bindEl, align[0],align[1]);
25895         //var arrow = this.el.select('.arrow',true).first();
25896         //arrow.set(align[2], 
25897         
25898         this.el.addClass(placement);
25899         
25900         this.el.addClass('in fade');
25901         
25902         this.hoverState = null;
25903         
25904         if (this.el.hasClass('fade')) {
25905             // fade it?
25906         }
25907         
25908     },
25909     hide : function()
25910     {
25911          
25912         if (!this.el) {
25913             return;
25914         }
25915         //this.el.setXY([0,0]);
25916         this.el.removeClass('in');
25917         //this.el.hide();
25918         
25919     }
25920     
25921 });
25922  
25923
25924  /*
25925  * - LGPL
25926  *
25927  * Location Picker
25928  * 
25929  */
25930
25931 /**
25932  * @class Roo.bootstrap.LocationPicker
25933  * @extends Roo.bootstrap.Component
25934  * Bootstrap LocationPicker class
25935  * @cfg {Number} latitude Position when init default 0
25936  * @cfg {Number} longitude Position when init default 0
25937  * @cfg {Number} zoom default 15
25938  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25939  * @cfg {Boolean} mapTypeControl default false
25940  * @cfg {Boolean} disableDoubleClickZoom default false
25941  * @cfg {Boolean} scrollwheel default true
25942  * @cfg {Boolean} streetViewControl default false
25943  * @cfg {Number} radius default 0
25944  * @cfg {String} locationName
25945  * @cfg {Boolean} draggable default true
25946  * @cfg {Boolean} enableAutocomplete default false
25947  * @cfg {Boolean} enableReverseGeocode default true
25948  * @cfg {String} markerTitle
25949  * 
25950  * @constructor
25951  * Create a new LocationPicker
25952  * @param {Object} config The config object
25953  */
25954
25955
25956 Roo.bootstrap.LocationPicker = function(config){
25957     
25958     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25959     
25960     this.addEvents({
25961         /**
25962          * @event initial
25963          * Fires when the picker initialized.
25964          * @param {Roo.bootstrap.LocationPicker} this
25965          * @param {Google Location} location
25966          */
25967         initial : true,
25968         /**
25969          * @event positionchanged
25970          * Fires when the picker position changed.
25971          * @param {Roo.bootstrap.LocationPicker} this
25972          * @param {Google Location} location
25973          */
25974         positionchanged : true,
25975         /**
25976          * @event resize
25977          * Fires when the map resize.
25978          * @param {Roo.bootstrap.LocationPicker} this
25979          */
25980         resize : true,
25981         /**
25982          * @event show
25983          * Fires when the map show.
25984          * @param {Roo.bootstrap.LocationPicker} this
25985          */
25986         show : true,
25987         /**
25988          * @event hide
25989          * Fires when the map hide.
25990          * @param {Roo.bootstrap.LocationPicker} this
25991          */
25992         hide : true,
25993         /**
25994          * @event mapClick
25995          * Fires when click the map.
25996          * @param {Roo.bootstrap.LocationPicker} this
25997          * @param {Map event} e
25998          */
25999         mapClick : true,
26000         /**
26001          * @event mapRightClick
26002          * Fires when right click the map.
26003          * @param {Roo.bootstrap.LocationPicker} this
26004          * @param {Map event} e
26005          */
26006         mapRightClick : true,
26007         /**
26008          * @event markerClick
26009          * Fires when click the marker.
26010          * @param {Roo.bootstrap.LocationPicker} this
26011          * @param {Map event} e
26012          */
26013         markerClick : true,
26014         /**
26015          * @event markerRightClick
26016          * Fires when right click the marker.
26017          * @param {Roo.bootstrap.LocationPicker} this
26018          * @param {Map event} e
26019          */
26020         markerRightClick : true,
26021         /**
26022          * @event OverlayViewDraw
26023          * Fires when OverlayView Draw
26024          * @param {Roo.bootstrap.LocationPicker} this
26025          */
26026         OverlayViewDraw : true,
26027         /**
26028          * @event OverlayViewOnAdd
26029          * Fires when OverlayView Draw
26030          * @param {Roo.bootstrap.LocationPicker} this
26031          */
26032         OverlayViewOnAdd : true,
26033         /**
26034          * @event OverlayViewOnRemove
26035          * Fires when OverlayView Draw
26036          * @param {Roo.bootstrap.LocationPicker} this
26037          */
26038         OverlayViewOnRemove : true,
26039         /**
26040          * @event OverlayViewShow
26041          * Fires when OverlayView Draw
26042          * @param {Roo.bootstrap.LocationPicker} this
26043          * @param {Pixel} cpx
26044          */
26045         OverlayViewShow : true,
26046         /**
26047          * @event OverlayViewHide
26048          * Fires when OverlayView Draw
26049          * @param {Roo.bootstrap.LocationPicker} this
26050          */
26051         OverlayViewHide : true,
26052         /**
26053          * @event loadexception
26054          * Fires when load google lib failed.
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          */
26057         loadexception : true
26058     });
26059         
26060 };
26061
26062 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26063     
26064     gMapContext: false,
26065     
26066     latitude: 0,
26067     longitude: 0,
26068     zoom: 15,
26069     mapTypeId: false,
26070     mapTypeControl: false,
26071     disableDoubleClickZoom: false,
26072     scrollwheel: true,
26073     streetViewControl: false,
26074     radius: 0,
26075     locationName: '',
26076     draggable: true,
26077     enableAutocomplete: false,
26078     enableReverseGeocode: true,
26079     markerTitle: '',
26080     
26081     getAutoCreate: function()
26082     {
26083
26084         var cfg = {
26085             tag: 'div',
26086             cls: 'roo-location-picker'
26087         };
26088         
26089         return cfg
26090     },
26091     
26092     initEvents: function(ct, position)
26093     {       
26094         if(!this.el.getWidth() || this.isApplied()){
26095             return;
26096         }
26097         
26098         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26099         
26100         this.initial();
26101     },
26102     
26103     initial: function()
26104     {
26105         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26106             this.fireEvent('loadexception', this);
26107             return;
26108         }
26109         
26110         if(!this.mapTypeId){
26111             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26112         }
26113         
26114         this.gMapContext = this.GMapContext();
26115         
26116         this.initOverlayView();
26117         
26118         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26119         
26120         var _this = this;
26121                 
26122         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26123             _this.setPosition(_this.gMapContext.marker.position);
26124         });
26125         
26126         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26127             _this.fireEvent('mapClick', this, event);
26128             
26129         });
26130
26131         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26132             _this.fireEvent('mapRightClick', this, event);
26133             
26134         });
26135         
26136         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26137             _this.fireEvent('markerClick', this, event);
26138             
26139         });
26140
26141         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26142             _this.fireEvent('markerRightClick', this, event);
26143             
26144         });
26145         
26146         this.setPosition(this.gMapContext.location);
26147         
26148         this.fireEvent('initial', this, this.gMapContext.location);
26149     },
26150     
26151     initOverlayView: function()
26152     {
26153         var _this = this;
26154         
26155         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26156             
26157             draw: function()
26158             {
26159                 _this.fireEvent('OverlayViewDraw', _this);
26160             },
26161             
26162             onAdd: function()
26163             {
26164                 _this.fireEvent('OverlayViewOnAdd', _this);
26165             },
26166             
26167             onRemove: function()
26168             {
26169                 _this.fireEvent('OverlayViewOnRemove', _this);
26170             },
26171             
26172             show: function(cpx)
26173             {
26174                 _this.fireEvent('OverlayViewShow', _this, cpx);
26175             },
26176             
26177             hide: function()
26178             {
26179                 _this.fireEvent('OverlayViewHide', _this);
26180             }
26181             
26182         });
26183     },
26184     
26185     fromLatLngToContainerPixel: function(event)
26186     {
26187         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26188     },
26189     
26190     isApplied: function() 
26191     {
26192         return this.getGmapContext() == false ? false : true;
26193     },
26194     
26195     getGmapContext: function() 
26196     {
26197         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26198     },
26199     
26200     GMapContext: function() 
26201     {
26202         var position = new google.maps.LatLng(this.latitude, this.longitude);
26203         
26204         var _map = new google.maps.Map(this.el.dom, {
26205             center: position,
26206             zoom: this.zoom,
26207             mapTypeId: this.mapTypeId,
26208             mapTypeControl: this.mapTypeControl,
26209             disableDoubleClickZoom: this.disableDoubleClickZoom,
26210             scrollwheel: this.scrollwheel,
26211             streetViewControl: this.streetViewControl,
26212             locationName: this.locationName,
26213             draggable: this.draggable,
26214             enableAutocomplete: this.enableAutocomplete,
26215             enableReverseGeocode: this.enableReverseGeocode
26216         });
26217         
26218         var _marker = new google.maps.Marker({
26219             position: position,
26220             map: _map,
26221             title: this.markerTitle,
26222             draggable: this.draggable
26223         });
26224         
26225         return {
26226             map: _map,
26227             marker: _marker,
26228             circle: null,
26229             location: position,
26230             radius: this.radius,
26231             locationName: this.locationName,
26232             addressComponents: {
26233                 formatted_address: null,
26234                 addressLine1: null,
26235                 addressLine2: null,
26236                 streetName: null,
26237                 streetNumber: null,
26238                 city: null,
26239                 district: null,
26240                 state: null,
26241                 stateOrProvince: null
26242             },
26243             settings: this,
26244             domContainer: this.el.dom,
26245             geodecoder: new google.maps.Geocoder()
26246         };
26247     },
26248     
26249     drawCircle: function(center, radius, options) 
26250     {
26251         if (this.gMapContext.circle != null) {
26252             this.gMapContext.circle.setMap(null);
26253         }
26254         if (radius > 0) {
26255             radius *= 1;
26256             options = Roo.apply({}, options, {
26257                 strokeColor: "#0000FF",
26258                 strokeOpacity: .35,
26259                 strokeWeight: 2,
26260                 fillColor: "#0000FF",
26261                 fillOpacity: .2
26262             });
26263             
26264             options.map = this.gMapContext.map;
26265             options.radius = radius;
26266             options.center = center;
26267             this.gMapContext.circle = new google.maps.Circle(options);
26268             return this.gMapContext.circle;
26269         }
26270         
26271         return null;
26272     },
26273     
26274     setPosition: function(location) 
26275     {
26276         this.gMapContext.location = location;
26277         this.gMapContext.marker.setPosition(location);
26278         this.gMapContext.map.panTo(location);
26279         this.drawCircle(location, this.gMapContext.radius, {});
26280         
26281         var _this = this;
26282         
26283         if (this.gMapContext.settings.enableReverseGeocode) {
26284             this.gMapContext.geodecoder.geocode({
26285                 latLng: this.gMapContext.location
26286             }, function(results, status) {
26287                 
26288                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26289                     _this.gMapContext.locationName = results[0].formatted_address;
26290                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26291                     
26292                     _this.fireEvent('positionchanged', this, location);
26293                 }
26294             });
26295             
26296             return;
26297         }
26298         
26299         this.fireEvent('positionchanged', this, location);
26300     },
26301     
26302     resize: function()
26303     {
26304         google.maps.event.trigger(this.gMapContext.map, "resize");
26305         
26306         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26307         
26308         this.fireEvent('resize', this);
26309     },
26310     
26311     setPositionByLatLng: function(latitude, longitude)
26312     {
26313         this.setPosition(new google.maps.LatLng(latitude, longitude));
26314     },
26315     
26316     getCurrentPosition: function() 
26317     {
26318         return {
26319             latitude: this.gMapContext.location.lat(),
26320             longitude: this.gMapContext.location.lng()
26321         };
26322     },
26323     
26324     getAddressName: function() 
26325     {
26326         return this.gMapContext.locationName;
26327     },
26328     
26329     getAddressComponents: function() 
26330     {
26331         return this.gMapContext.addressComponents;
26332     },
26333     
26334     address_component_from_google_geocode: function(address_components) 
26335     {
26336         var result = {};
26337         
26338         for (var i = 0; i < address_components.length; i++) {
26339             var component = address_components[i];
26340             if (component.types.indexOf("postal_code") >= 0) {
26341                 result.postalCode = component.short_name;
26342             } else if (component.types.indexOf("street_number") >= 0) {
26343                 result.streetNumber = component.short_name;
26344             } else if (component.types.indexOf("route") >= 0) {
26345                 result.streetName = component.short_name;
26346             } else if (component.types.indexOf("neighborhood") >= 0) {
26347                 result.city = component.short_name;
26348             } else if (component.types.indexOf("locality") >= 0) {
26349                 result.city = component.short_name;
26350             } else if (component.types.indexOf("sublocality") >= 0) {
26351                 result.district = component.short_name;
26352             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26353                 result.stateOrProvince = component.short_name;
26354             } else if (component.types.indexOf("country") >= 0) {
26355                 result.country = component.short_name;
26356             }
26357         }
26358         
26359         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26360         result.addressLine2 = "";
26361         return result;
26362     },
26363     
26364     setZoomLevel: function(zoom)
26365     {
26366         this.gMapContext.map.setZoom(zoom);
26367     },
26368     
26369     show: function()
26370     {
26371         if(!this.el){
26372             return;
26373         }
26374         
26375         this.el.show();
26376         
26377         this.resize();
26378         
26379         this.fireEvent('show', this);
26380     },
26381     
26382     hide: function()
26383     {
26384         if(!this.el){
26385             return;
26386         }
26387         
26388         this.el.hide();
26389         
26390         this.fireEvent('hide', this);
26391     }
26392     
26393 });
26394
26395 Roo.apply(Roo.bootstrap.LocationPicker, {
26396     
26397     OverlayView : function(map, options)
26398     {
26399         options = options || {};
26400         
26401         this.setMap(map);
26402     }
26403     
26404     
26405 });/*
26406  * - LGPL
26407  *
26408  * Alert
26409  * 
26410  */
26411
26412 /**
26413  * @class Roo.bootstrap.Alert
26414  * @extends Roo.bootstrap.Component
26415  * Bootstrap Alert class
26416  * @cfg {String} title The title of alert
26417  * @cfg {String} html The content of alert
26418  * @cfg {String} weight (  success | info | warning | danger )
26419  * @cfg {String} faicon font-awesomeicon
26420  * 
26421  * @constructor
26422  * Create a new alert
26423  * @param {Object} config The config object
26424  */
26425
26426
26427 Roo.bootstrap.Alert = function(config){
26428     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26429     
26430 };
26431
26432 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26433     
26434     title: '',
26435     html: '',
26436     weight: false,
26437     faicon: false,
26438     
26439     getAutoCreate : function()
26440     {
26441         
26442         var cfg = {
26443             tag : 'div',
26444             cls : 'alert',
26445             cn : [
26446                 {
26447                     tag : 'i',
26448                     cls : 'roo-alert-icon'
26449                     
26450                 },
26451                 {
26452                     tag : 'b',
26453                     cls : 'roo-alert-title',
26454                     html : this.title
26455                 },
26456                 {
26457                     tag : 'span',
26458                     cls : 'roo-alert-text',
26459                     html : this.html
26460                 }
26461             ]
26462         };
26463         
26464         if(this.faicon){
26465             cfg.cn[0].cls += ' fa ' + this.faicon;
26466         }
26467         
26468         if(this.weight){
26469             cfg.cls += ' alert-' + this.weight;
26470         }
26471         
26472         return cfg;
26473     },
26474     
26475     initEvents: function() 
26476     {
26477         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26478     },
26479     
26480     setTitle : function(str)
26481     {
26482         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26483     },
26484     
26485     setText : function(str)
26486     {
26487         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26488     },
26489     
26490     setWeight : function(weight)
26491     {
26492         if(this.weight){
26493             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26494         }
26495         
26496         this.weight = weight;
26497         
26498         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26499     },
26500     
26501     setIcon : function(icon)
26502     {
26503         if(this.faicon){
26504             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26505         }
26506         
26507         this.faicon = icon;
26508         
26509         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26510     },
26511     
26512     hide: function() 
26513     {
26514         this.el.hide();   
26515     },
26516     
26517     show: function() 
26518     {  
26519         this.el.show();   
26520     }
26521     
26522 });
26523
26524  
26525 /*
26526 * Licence: LGPL
26527 */
26528
26529 /**
26530  * @class Roo.bootstrap.UploadCropbox
26531  * @extends Roo.bootstrap.Component
26532  * Bootstrap UploadCropbox class
26533  * @cfg {String} emptyText show when image has been loaded
26534  * @cfg {String} rotateNotify show when image too small to rotate
26535  * @cfg {Number} errorTimeout default 3000
26536  * @cfg {Number} minWidth default 300
26537  * @cfg {Number} minHeight default 300
26538  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26539  * @cfg {Boolean} isDocument (true|false) default false
26540  * @cfg {String} url action url
26541  * @cfg {String} paramName default 'imageUpload'
26542  * @cfg {String} method default POST
26543  * @cfg {Boolean} loadMask (true|false) default true
26544  * @cfg {Boolean} loadingText default 'Loading...'
26545  * 
26546  * @constructor
26547  * Create a new UploadCropbox
26548  * @param {Object} config The config object
26549  */
26550
26551 Roo.bootstrap.UploadCropbox = function(config){
26552     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26553     
26554     this.addEvents({
26555         /**
26556          * @event beforeselectfile
26557          * Fire before select file
26558          * @param {Roo.bootstrap.UploadCropbox} this
26559          */
26560         "beforeselectfile" : true,
26561         /**
26562          * @event initial
26563          * Fire after initEvent
26564          * @param {Roo.bootstrap.UploadCropbox} this
26565          */
26566         "initial" : true,
26567         /**
26568          * @event crop
26569          * Fire after initEvent
26570          * @param {Roo.bootstrap.UploadCropbox} this
26571          * @param {String} data
26572          */
26573         "crop" : true,
26574         /**
26575          * @event prepare
26576          * Fire when preparing the file data
26577          * @param {Roo.bootstrap.UploadCropbox} this
26578          * @param {Object} file
26579          */
26580         "prepare" : true,
26581         /**
26582          * @event exception
26583          * Fire when get exception
26584          * @param {Roo.bootstrap.UploadCropbox} this
26585          * @param {XMLHttpRequest} xhr
26586          */
26587         "exception" : true,
26588         /**
26589          * @event beforeloadcanvas
26590          * Fire before load the canvas
26591          * @param {Roo.bootstrap.UploadCropbox} this
26592          * @param {String} src
26593          */
26594         "beforeloadcanvas" : true,
26595         /**
26596          * @event trash
26597          * Fire when trash image
26598          * @param {Roo.bootstrap.UploadCropbox} this
26599          */
26600         "trash" : true,
26601         /**
26602          * @event download
26603          * Fire when download the image
26604          * @param {Roo.bootstrap.UploadCropbox} this
26605          */
26606         "download" : true,
26607         /**
26608          * @event footerbuttonclick
26609          * Fire when footerbuttonclick
26610          * @param {Roo.bootstrap.UploadCropbox} this
26611          * @param {String} type
26612          */
26613         "footerbuttonclick" : true,
26614         /**
26615          * @event resize
26616          * Fire when resize
26617          * @param {Roo.bootstrap.UploadCropbox} this
26618          */
26619         "resize" : true,
26620         /**
26621          * @event rotate
26622          * Fire when rotate the image
26623          * @param {Roo.bootstrap.UploadCropbox} this
26624          * @param {String} pos
26625          */
26626         "rotate" : true,
26627         /**
26628          * @event inspect
26629          * Fire when inspect the file
26630          * @param {Roo.bootstrap.UploadCropbox} this
26631          * @param {Object} file
26632          */
26633         "inspect" : true,
26634         /**
26635          * @event upload
26636          * Fire when xhr upload the file
26637          * @param {Roo.bootstrap.UploadCropbox} this
26638          * @param {Object} data
26639          */
26640         "upload" : true,
26641         /**
26642          * @event arrange
26643          * Fire when arrange the file data
26644          * @param {Roo.bootstrap.UploadCropbox} this
26645          * @param {Object} formData
26646          */
26647         "arrange" : true
26648     });
26649     
26650     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26651 };
26652
26653 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26654     
26655     emptyText : 'Click to upload image',
26656     rotateNotify : 'Image is too small to rotate',
26657     errorTimeout : 3000,
26658     scale : 0,
26659     baseScale : 1,
26660     rotate : 0,
26661     dragable : false,
26662     pinching : false,
26663     mouseX : 0,
26664     mouseY : 0,
26665     cropData : false,
26666     minWidth : 300,
26667     minHeight : 300,
26668     file : false,
26669     exif : {},
26670     baseRotate : 1,
26671     cropType : 'image/jpeg',
26672     buttons : false,
26673     canvasLoaded : false,
26674     isDocument : false,
26675     method : 'POST',
26676     paramName : 'imageUpload',
26677     loadMask : true,
26678     loadingText : 'Loading...',
26679     maskEl : false,
26680     
26681     getAutoCreate : function()
26682     {
26683         var cfg = {
26684             tag : 'div',
26685             cls : 'roo-upload-cropbox',
26686             cn : [
26687                 {
26688                     tag : 'input',
26689                     cls : 'roo-upload-cropbox-selector',
26690                     type : 'file'
26691                 },
26692                 {
26693                     tag : 'div',
26694                     cls : 'roo-upload-cropbox-body',
26695                     style : 'cursor:pointer',
26696                     cn : [
26697                         {
26698                             tag : 'div',
26699                             cls : 'roo-upload-cropbox-preview'
26700                         },
26701                         {
26702                             tag : 'div',
26703                             cls : 'roo-upload-cropbox-thumb'
26704                         },
26705                         {
26706                             tag : 'div',
26707                             cls : 'roo-upload-cropbox-empty-notify',
26708                             html : this.emptyText
26709                         },
26710                         {
26711                             tag : 'div',
26712                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26713                             html : this.rotateNotify
26714                         }
26715                     ]
26716                 },
26717                 {
26718                     tag : 'div',
26719                     cls : 'roo-upload-cropbox-footer',
26720                     cn : {
26721                         tag : 'div',
26722                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26723                         cn : []
26724                     }
26725                 }
26726             ]
26727         };
26728         
26729         return cfg;
26730     },
26731     
26732     onRender : function(ct, position)
26733     {
26734         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26735         
26736         if (this.buttons.length) {
26737             
26738             Roo.each(this.buttons, function(bb) {
26739                 
26740                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26741                 
26742                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26743                 
26744             }, this);
26745         }
26746         
26747         if(this.loadMask){
26748             this.maskEl = this.el;
26749         }
26750     },
26751     
26752     initEvents : function()
26753     {
26754         this.urlAPI = (window.createObjectURL && window) || 
26755                                 (window.URL && URL.revokeObjectURL && URL) || 
26756                                 (window.webkitURL && webkitURL);
26757                         
26758         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26759         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26760         
26761         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26762         this.selectorEl.hide();
26763         
26764         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26765         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26766         
26767         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26768         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26769         this.thumbEl.hide();
26770         
26771         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26772         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26773         
26774         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26775         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26776         this.errorEl.hide();
26777         
26778         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26779         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26780         this.footerEl.hide();
26781         
26782         this.setThumbBoxSize();
26783         
26784         this.bind();
26785         
26786         this.resize();
26787         
26788         this.fireEvent('initial', this);
26789     },
26790
26791     bind : function()
26792     {
26793         var _this = this;
26794         
26795         window.addEventListener("resize", function() { _this.resize(); } );
26796         
26797         this.bodyEl.on('click', this.beforeSelectFile, this);
26798         
26799         if(Roo.isTouch){
26800             this.bodyEl.on('touchstart', this.onTouchStart, this);
26801             this.bodyEl.on('touchmove', this.onTouchMove, this);
26802             this.bodyEl.on('touchend', this.onTouchEnd, this);
26803         }
26804         
26805         if(!Roo.isTouch){
26806             this.bodyEl.on('mousedown', this.onMouseDown, this);
26807             this.bodyEl.on('mousemove', this.onMouseMove, this);
26808             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26809             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26810             Roo.get(document).on('mouseup', this.onMouseUp, this);
26811         }
26812         
26813         this.selectorEl.on('change', this.onFileSelected, this);
26814     },
26815     
26816     reset : function()
26817     {    
26818         this.scale = 0;
26819         this.baseScale = 1;
26820         this.rotate = 0;
26821         this.baseRotate = 1;
26822         this.dragable = false;
26823         this.pinching = false;
26824         this.mouseX = 0;
26825         this.mouseY = 0;
26826         this.cropData = false;
26827         this.notifyEl.dom.innerHTML = this.emptyText;
26828         
26829         this.selectorEl.dom.value = '';
26830         
26831     },
26832     
26833     resize : function()
26834     {
26835         if(this.fireEvent('resize', this) != false){
26836             this.setThumbBoxPosition();
26837             this.setCanvasPosition();
26838         }
26839     },
26840     
26841     onFooterButtonClick : function(e, el, o, type)
26842     {
26843         switch (type) {
26844             case 'rotate-left' :
26845                 this.onRotateLeft(e);
26846                 break;
26847             case 'rotate-right' :
26848                 this.onRotateRight(e);
26849                 break;
26850             case 'picture' :
26851                 this.beforeSelectFile(e);
26852                 break;
26853             case 'trash' :
26854                 this.trash(e);
26855                 break;
26856             case 'crop' :
26857                 this.crop(e);
26858                 break;
26859             case 'download' :
26860                 this.download(e);
26861                 break;
26862             default :
26863                 break;
26864         }
26865         
26866         this.fireEvent('footerbuttonclick', this, type);
26867     },
26868     
26869     beforeSelectFile : function(e)
26870     {
26871         e.preventDefault();
26872         
26873         if(this.fireEvent('beforeselectfile', this) != false){
26874             this.selectorEl.dom.click();
26875         }
26876     },
26877     
26878     onFileSelected : function(e)
26879     {
26880         e.preventDefault();
26881         
26882         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26883             return;
26884         }
26885         
26886         var file = this.selectorEl.dom.files[0];
26887         
26888         if(this.fireEvent('inspect', this, file) != false){
26889             this.prepare(file);
26890         }
26891         
26892     },
26893     
26894     trash : function(e)
26895     {
26896         this.fireEvent('trash', this);
26897     },
26898     
26899     download : function(e)
26900     {
26901         this.fireEvent('download', this);
26902     },
26903     
26904     loadCanvas : function(src)
26905     {   
26906         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26907             
26908             this.reset();
26909             
26910             this.imageEl = document.createElement('img');
26911             
26912             var _this = this;
26913             
26914             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26915             
26916             this.imageEl.src = src;
26917         }
26918     },
26919     
26920     onLoadCanvas : function()
26921     {   
26922         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26923         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26924         
26925         this.bodyEl.un('click', this.beforeSelectFile, this);
26926         
26927         this.notifyEl.hide();
26928         this.thumbEl.show();
26929         this.footerEl.show();
26930         
26931         this.baseRotateLevel();
26932         
26933         if(this.isDocument){
26934             this.setThumbBoxSize();
26935         }
26936         
26937         this.setThumbBoxPosition();
26938         
26939         this.baseScaleLevel();
26940         
26941         this.draw();
26942         
26943         this.resize();
26944         
26945         this.canvasLoaded = true;
26946         
26947         if(this.loadMask){
26948             this.maskEl.unmask();
26949         }
26950         
26951     },
26952     
26953     setCanvasPosition : function()
26954     {   
26955         if(!this.canvasEl){
26956             return;
26957         }
26958         
26959         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26960         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26961         
26962         this.previewEl.setLeft(pw);
26963         this.previewEl.setTop(ph);
26964         
26965     },
26966     
26967     onMouseDown : function(e)
26968     {   
26969         e.stopEvent();
26970         
26971         this.dragable = true;
26972         this.pinching = false;
26973         
26974         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26975             this.dragable = false;
26976             return;
26977         }
26978         
26979         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26980         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26981         
26982     },
26983     
26984     onMouseMove : function(e)
26985     {   
26986         e.stopEvent();
26987         
26988         if(!this.canvasLoaded){
26989             return;
26990         }
26991         
26992         if (!this.dragable){
26993             return;
26994         }
26995         
26996         var minX = Math.ceil(this.thumbEl.getLeft(true));
26997         var minY = Math.ceil(this.thumbEl.getTop(true));
26998         
26999         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27000         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27001         
27002         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27003         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27004         
27005         x = x - this.mouseX;
27006         y = y - this.mouseY;
27007         
27008         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27009         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27010         
27011         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27012         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27013         
27014         this.previewEl.setLeft(bgX);
27015         this.previewEl.setTop(bgY);
27016         
27017         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27018         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27019     },
27020     
27021     onMouseUp : function(e)
27022     {   
27023         e.stopEvent();
27024         
27025         this.dragable = false;
27026     },
27027     
27028     onMouseWheel : function(e)
27029     {   
27030         e.stopEvent();
27031         
27032         this.startScale = this.scale;
27033         
27034         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27035         
27036         if(!this.zoomable()){
27037             this.scale = this.startScale;
27038             return;
27039         }
27040         
27041         this.draw();
27042         
27043         return;
27044     },
27045     
27046     zoomable : function()
27047     {
27048         var minScale = this.thumbEl.getWidth() / this.minWidth;
27049         
27050         if(this.minWidth < this.minHeight){
27051             minScale = this.thumbEl.getHeight() / this.minHeight;
27052         }
27053         
27054         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27055         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27056         
27057         if(
27058                 this.isDocument &&
27059                 (this.rotate == 0 || this.rotate == 180) && 
27060                 (
27061                     width > this.imageEl.OriginWidth || 
27062                     height > this.imageEl.OriginHeight ||
27063                     (width < this.minWidth && height < this.minHeight)
27064                 )
27065         ){
27066             return false;
27067         }
27068         
27069         if(
27070                 this.isDocument &&
27071                 (this.rotate == 90 || this.rotate == 270) && 
27072                 (
27073                     width > this.imageEl.OriginWidth || 
27074                     height > this.imageEl.OriginHeight ||
27075                     (width < this.minHeight && height < this.minWidth)
27076                 )
27077         ){
27078             return false;
27079         }
27080         
27081         if(
27082                 !this.isDocument &&
27083                 (this.rotate == 0 || this.rotate == 180) && 
27084                 (
27085                     width < this.minWidth || 
27086                     width > this.imageEl.OriginWidth || 
27087                     height < this.minHeight || 
27088                     height > this.imageEl.OriginHeight
27089                 )
27090         ){
27091             return false;
27092         }
27093         
27094         if(
27095                 !this.isDocument &&
27096                 (this.rotate == 90 || this.rotate == 270) && 
27097                 (
27098                     width < this.minHeight || 
27099                     width > this.imageEl.OriginWidth || 
27100                     height < this.minWidth || 
27101                     height > this.imageEl.OriginHeight
27102                 )
27103         ){
27104             return false;
27105         }
27106         
27107         return true;
27108         
27109     },
27110     
27111     onRotateLeft : function(e)
27112     {   
27113         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27114             
27115             var minScale = this.thumbEl.getWidth() / this.minWidth;
27116             
27117             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27118             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27119             
27120             this.startScale = this.scale;
27121             
27122             while (this.getScaleLevel() < minScale){
27123             
27124                 this.scale = this.scale + 1;
27125                 
27126                 if(!this.zoomable()){
27127                     break;
27128                 }
27129                 
27130                 if(
27131                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27132                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27133                 ){
27134                     continue;
27135                 }
27136                 
27137                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27138
27139                 this.draw();
27140                 
27141                 return;
27142             }
27143             
27144             this.scale = this.startScale;
27145             
27146             this.onRotateFail();
27147             
27148             return false;
27149         }
27150         
27151         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27152
27153         if(this.isDocument){
27154             this.setThumbBoxSize();
27155             this.setThumbBoxPosition();
27156             this.setCanvasPosition();
27157         }
27158         
27159         this.draw();
27160         
27161         this.fireEvent('rotate', this, 'left');
27162         
27163     },
27164     
27165     onRotateRight : function(e)
27166     {
27167         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27168             
27169             var minScale = this.thumbEl.getWidth() / this.minWidth;
27170         
27171             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27172             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27173             
27174             this.startScale = this.scale;
27175             
27176             while (this.getScaleLevel() < minScale){
27177             
27178                 this.scale = this.scale + 1;
27179                 
27180                 if(!this.zoomable()){
27181                     break;
27182                 }
27183                 
27184                 if(
27185                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27186                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27187                 ){
27188                     continue;
27189                 }
27190                 
27191                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27192
27193                 this.draw();
27194                 
27195                 return;
27196             }
27197             
27198             this.scale = this.startScale;
27199             
27200             this.onRotateFail();
27201             
27202             return false;
27203         }
27204         
27205         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27206
27207         if(this.isDocument){
27208             this.setThumbBoxSize();
27209             this.setThumbBoxPosition();
27210             this.setCanvasPosition();
27211         }
27212         
27213         this.draw();
27214         
27215         this.fireEvent('rotate', this, 'right');
27216     },
27217     
27218     onRotateFail : function()
27219     {
27220         this.errorEl.show(true);
27221         
27222         var _this = this;
27223         
27224         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27225     },
27226     
27227     draw : function()
27228     {
27229         this.previewEl.dom.innerHTML = '';
27230         
27231         var canvasEl = document.createElement("canvas");
27232         
27233         var contextEl = canvasEl.getContext("2d");
27234         
27235         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27236         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27237         var center = this.imageEl.OriginWidth / 2;
27238         
27239         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27240             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27241             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27242             center = this.imageEl.OriginHeight / 2;
27243         }
27244         
27245         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27246         
27247         contextEl.translate(center, center);
27248         contextEl.rotate(this.rotate * Math.PI / 180);
27249
27250         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27251         
27252         this.canvasEl = document.createElement("canvas");
27253         
27254         this.contextEl = this.canvasEl.getContext("2d");
27255         
27256         switch (this.rotate) {
27257             case 0 :
27258                 
27259                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27260                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27261                 
27262                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27263                 
27264                 break;
27265             case 90 : 
27266                 
27267                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27268                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27269                 
27270                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27271                     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);
27272                     break;
27273                 }
27274                 
27275                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27276                 
27277                 break;
27278             case 180 :
27279                 
27280                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27281                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27282                 
27283                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27284                     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);
27285                     break;
27286                 }
27287                 
27288                 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);
27289                 
27290                 break;
27291             case 270 :
27292                 
27293                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27294                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27295         
27296                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27297                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27298                     break;
27299                 }
27300                 
27301                 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);
27302                 
27303                 break;
27304             default : 
27305                 break;
27306         }
27307         
27308         this.previewEl.appendChild(this.canvasEl);
27309         
27310         this.setCanvasPosition();
27311     },
27312     
27313     crop : function()
27314     {
27315         if(!this.canvasLoaded){
27316             return;
27317         }
27318         
27319         var imageCanvas = document.createElement("canvas");
27320         
27321         var imageContext = imageCanvas.getContext("2d");
27322         
27323         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27324         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27325         
27326         var center = imageCanvas.width / 2;
27327         
27328         imageContext.translate(center, center);
27329         
27330         imageContext.rotate(this.rotate * Math.PI / 180);
27331         
27332         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27333         
27334         var canvas = document.createElement("canvas");
27335         
27336         var context = canvas.getContext("2d");
27337                 
27338         canvas.width = this.minWidth;
27339         canvas.height = this.minHeight;
27340
27341         switch (this.rotate) {
27342             case 0 :
27343                 
27344                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27345                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27346                 
27347                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27348                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27349                 
27350                 var targetWidth = this.minWidth - 2 * x;
27351                 var targetHeight = this.minHeight - 2 * y;
27352                 
27353                 var scale = 1;
27354                 
27355                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27356                     scale = targetWidth / width;
27357                 }
27358                 
27359                 if(x > 0 && y == 0){
27360                     scale = targetHeight / height;
27361                 }
27362                 
27363                 if(x > 0 && y > 0){
27364                     scale = targetWidth / width;
27365                     
27366                     if(width < height){
27367                         scale = targetHeight / height;
27368                     }
27369                 }
27370                 
27371                 context.scale(scale, scale);
27372                 
27373                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27374                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27375
27376                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27377                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27378
27379                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27380                 
27381                 break;
27382             case 90 : 
27383                 
27384                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27385                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27386                 
27387                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27388                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27389                 
27390                 var targetWidth = this.minWidth - 2 * x;
27391                 var targetHeight = this.minHeight - 2 * y;
27392                 
27393                 var scale = 1;
27394                 
27395                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27396                     scale = targetWidth / width;
27397                 }
27398                 
27399                 if(x > 0 && y == 0){
27400                     scale = targetHeight / height;
27401                 }
27402                 
27403                 if(x > 0 && y > 0){
27404                     scale = targetWidth / width;
27405                     
27406                     if(width < height){
27407                         scale = targetHeight / height;
27408                     }
27409                 }
27410                 
27411                 context.scale(scale, scale);
27412                 
27413                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27414                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27415
27416                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27417                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27418                 
27419                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27420                 
27421                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27422                 
27423                 break;
27424             case 180 :
27425                 
27426                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27427                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27428                 
27429                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27430                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27431                 
27432                 var targetWidth = this.minWidth - 2 * x;
27433                 var targetHeight = this.minHeight - 2 * y;
27434                 
27435                 var scale = 1;
27436                 
27437                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27438                     scale = targetWidth / width;
27439                 }
27440                 
27441                 if(x > 0 && y == 0){
27442                     scale = targetHeight / height;
27443                 }
27444                 
27445                 if(x > 0 && y > 0){
27446                     scale = targetWidth / width;
27447                     
27448                     if(width < height){
27449                         scale = targetHeight / height;
27450                     }
27451                 }
27452                 
27453                 context.scale(scale, scale);
27454                 
27455                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27456                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27457
27458                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27459                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27460
27461                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27462                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27463                 
27464                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27465                 
27466                 break;
27467             case 270 :
27468                 
27469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27471                 
27472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27474                 
27475                 var targetWidth = this.minWidth - 2 * x;
27476                 var targetHeight = this.minHeight - 2 * y;
27477                 
27478                 var scale = 1;
27479                 
27480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27481                     scale = targetWidth / width;
27482                 }
27483                 
27484                 if(x > 0 && y == 0){
27485                     scale = targetHeight / height;
27486                 }
27487                 
27488                 if(x > 0 && y > 0){
27489                     scale = targetWidth / width;
27490                     
27491                     if(width < height){
27492                         scale = targetHeight / height;
27493                     }
27494                 }
27495                 
27496                 context.scale(scale, scale);
27497                 
27498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27500
27501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27503                 
27504                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27505                 
27506                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27507                 
27508                 break;
27509             default : 
27510                 break;
27511         }
27512         
27513         this.cropData = canvas.toDataURL(this.cropType);
27514         
27515         if(this.fireEvent('crop', this, this.cropData) !== false){
27516             this.process(this.file, this.cropData);
27517         }
27518         
27519         return;
27520         
27521     },
27522     
27523     setThumbBoxSize : function()
27524     {
27525         var width, height;
27526         
27527         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27528             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27529             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27530             
27531             this.minWidth = width;
27532             this.minHeight = height;
27533             
27534             if(this.rotate == 90 || this.rotate == 270){
27535                 this.minWidth = height;
27536                 this.minHeight = width;
27537             }
27538         }
27539         
27540         height = 300;
27541         width = Math.ceil(this.minWidth * height / this.minHeight);
27542         
27543         if(this.minWidth > this.minHeight){
27544             width = 300;
27545             height = Math.ceil(this.minHeight * width / this.minWidth);
27546         }
27547         
27548         this.thumbEl.setStyle({
27549             width : width + 'px',
27550             height : height + 'px'
27551         });
27552
27553         return;
27554             
27555     },
27556     
27557     setThumbBoxPosition : function()
27558     {
27559         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27560         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27561         
27562         this.thumbEl.setLeft(x);
27563         this.thumbEl.setTop(y);
27564         
27565     },
27566     
27567     baseRotateLevel : function()
27568     {
27569         this.baseRotate = 1;
27570         
27571         if(
27572                 typeof(this.exif) != 'undefined' &&
27573                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27574                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27575         ){
27576             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27577         }
27578         
27579         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27580         
27581     },
27582     
27583     baseScaleLevel : function()
27584     {
27585         var width, height;
27586         
27587         if(this.isDocument){
27588             
27589             if(this.baseRotate == 6 || this.baseRotate == 8){
27590             
27591                 height = this.thumbEl.getHeight();
27592                 this.baseScale = height / this.imageEl.OriginWidth;
27593
27594                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27595                     width = this.thumbEl.getWidth();
27596                     this.baseScale = width / this.imageEl.OriginHeight;
27597                 }
27598
27599                 return;
27600             }
27601
27602             height = this.thumbEl.getHeight();
27603             this.baseScale = height / this.imageEl.OriginHeight;
27604
27605             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27606                 width = this.thumbEl.getWidth();
27607                 this.baseScale = width / this.imageEl.OriginWidth;
27608             }
27609
27610             return;
27611         }
27612         
27613         if(this.baseRotate == 6 || this.baseRotate == 8){
27614             
27615             width = this.thumbEl.getHeight();
27616             this.baseScale = width / this.imageEl.OriginHeight;
27617             
27618             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27619                 height = this.thumbEl.getWidth();
27620                 this.baseScale = height / this.imageEl.OriginHeight;
27621             }
27622             
27623             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27624                 height = this.thumbEl.getWidth();
27625                 this.baseScale = height / this.imageEl.OriginHeight;
27626                 
27627                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27628                     width = this.thumbEl.getHeight();
27629                     this.baseScale = width / this.imageEl.OriginWidth;
27630                 }
27631             }
27632             
27633             return;
27634         }
27635         
27636         width = this.thumbEl.getWidth();
27637         this.baseScale = width / this.imageEl.OriginWidth;
27638         
27639         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27640             height = this.thumbEl.getHeight();
27641             this.baseScale = height / this.imageEl.OriginHeight;
27642         }
27643         
27644         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27645             
27646             height = this.thumbEl.getHeight();
27647             this.baseScale = height / this.imageEl.OriginHeight;
27648             
27649             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27650                 width = this.thumbEl.getWidth();
27651                 this.baseScale = width / this.imageEl.OriginWidth;
27652             }
27653             
27654         }
27655         
27656         return;
27657     },
27658     
27659     getScaleLevel : function()
27660     {
27661         return this.baseScale * Math.pow(1.1, this.scale);
27662     },
27663     
27664     onTouchStart : function(e)
27665     {
27666         if(!this.canvasLoaded){
27667             this.beforeSelectFile(e);
27668             return;
27669         }
27670         
27671         var touches = e.browserEvent.touches;
27672         
27673         if(!touches){
27674             return;
27675         }
27676         
27677         if(touches.length == 1){
27678             this.onMouseDown(e);
27679             return;
27680         }
27681         
27682         if(touches.length != 2){
27683             return;
27684         }
27685         
27686         var coords = [];
27687         
27688         for(var i = 0, finger; finger = touches[i]; i++){
27689             coords.push(finger.pageX, finger.pageY);
27690         }
27691         
27692         var x = Math.pow(coords[0] - coords[2], 2);
27693         var y = Math.pow(coords[1] - coords[3], 2);
27694         
27695         this.startDistance = Math.sqrt(x + y);
27696         
27697         this.startScale = this.scale;
27698         
27699         this.pinching = true;
27700         this.dragable = false;
27701         
27702     },
27703     
27704     onTouchMove : function(e)
27705     {
27706         if(!this.pinching && !this.dragable){
27707             return;
27708         }
27709         
27710         var touches = e.browserEvent.touches;
27711         
27712         if(!touches){
27713             return;
27714         }
27715         
27716         if(this.dragable){
27717             this.onMouseMove(e);
27718             return;
27719         }
27720         
27721         var coords = [];
27722         
27723         for(var i = 0, finger; finger = touches[i]; i++){
27724             coords.push(finger.pageX, finger.pageY);
27725         }
27726         
27727         var x = Math.pow(coords[0] - coords[2], 2);
27728         var y = Math.pow(coords[1] - coords[3], 2);
27729         
27730         this.endDistance = Math.sqrt(x + y);
27731         
27732         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27733         
27734         if(!this.zoomable()){
27735             this.scale = this.startScale;
27736             return;
27737         }
27738         
27739         this.draw();
27740         
27741     },
27742     
27743     onTouchEnd : function(e)
27744     {
27745         this.pinching = false;
27746         this.dragable = false;
27747         
27748     },
27749     
27750     process : function(file, crop)
27751     {
27752         if(this.loadMask){
27753             this.maskEl.mask(this.loadingText);
27754         }
27755         
27756         this.xhr = new XMLHttpRequest();
27757         
27758         file.xhr = this.xhr;
27759
27760         this.xhr.open(this.method, this.url, true);
27761         
27762         var headers = {
27763             "Accept": "application/json",
27764             "Cache-Control": "no-cache",
27765             "X-Requested-With": "XMLHttpRequest"
27766         };
27767         
27768         for (var headerName in headers) {
27769             var headerValue = headers[headerName];
27770             if (headerValue) {
27771                 this.xhr.setRequestHeader(headerName, headerValue);
27772             }
27773         }
27774         
27775         var _this = this;
27776         
27777         this.xhr.onload = function()
27778         {
27779             _this.xhrOnLoad(_this.xhr);
27780         }
27781         
27782         this.xhr.onerror = function()
27783         {
27784             _this.xhrOnError(_this.xhr);
27785         }
27786         
27787         var formData = new FormData();
27788
27789         formData.append('returnHTML', 'NO');
27790         
27791         if(crop){
27792             formData.append('crop', crop);
27793         }
27794         
27795         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27796             formData.append(this.paramName, file, file.name);
27797         }
27798         
27799         if(typeof(file.filename) != 'undefined'){
27800             formData.append('filename', file.filename);
27801         }
27802         
27803         if(typeof(file.mimetype) != 'undefined'){
27804             formData.append('mimetype', file.mimetype);
27805         }
27806         
27807         if(this.fireEvent('arrange', this, formData) != false){
27808             this.xhr.send(formData);
27809         };
27810     },
27811     
27812     xhrOnLoad : function(xhr)
27813     {
27814         if(this.loadMask){
27815             this.maskEl.unmask();
27816         }
27817         
27818         if (xhr.readyState !== 4) {
27819             this.fireEvent('exception', this, xhr);
27820             return;
27821         }
27822
27823         var response = Roo.decode(xhr.responseText);
27824         
27825         if(!response.success){
27826             this.fireEvent('exception', this, xhr);
27827             return;
27828         }
27829         
27830         var response = Roo.decode(xhr.responseText);
27831         
27832         this.fireEvent('upload', this, response);
27833         
27834     },
27835     
27836     xhrOnError : function()
27837     {
27838         if(this.loadMask){
27839             this.maskEl.unmask();
27840         }
27841         
27842         Roo.log('xhr on error');
27843         
27844         var response = Roo.decode(xhr.responseText);
27845           
27846         Roo.log(response);
27847         
27848     },
27849     
27850     prepare : function(file)
27851     {   
27852         if(this.loadMask){
27853             this.maskEl.mask(this.loadingText);
27854         }
27855         
27856         this.file = false;
27857         this.exif = {};
27858         
27859         if(typeof(file) === 'string'){
27860             this.loadCanvas(file);
27861             return;
27862         }
27863         
27864         if(!file || !this.urlAPI){
27865             return;
27866         }
27867         
27868         this.file = file;
27869         this.cropType = file.type;
27870         
27871         var _this = this;
27872         
27873         if(this.fireEvent('prepare', this, this.file) != false){
27874             
27875             var reader = new FileReader();
27876             
27877             reader.onload = function (e) {
27878                 if (e.target.error) {
27879                     Roo.log(e.target.error);
27880                     return;
27881                 }
27882                 
27883                 var buffer = e.target.result,
27884                     dataView = new DataView(buffer),
27885                     offset = 2,
27886                     maxOffset = dataView.byteLength - 4,
27887                     markerBytes,
27888                     markerLength;
27889                 
27890                 if (dataView.getUint16(0) === 0xffd8) {
27891                     while (offset < maxOffset) {
27892                         markerBytes = dataView.getUint16(offset);
27893                         
27894                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27895                             markerLength = dataView.getUint16(offset + 2) + 2;
27896                             if (offset + markerLength > dataView.byteLength) {
27897                                 Roo.log('Invalid meta data: Invalid segment size.');
27898                                 break;
27899                             }
27900                             
27901                             if(markerBytes == 0xffe1){
27902                                 _this.parseExifData(
27903                                     dataView,
27904                                     offset,
27905                                     markerLength
27906                                 );
27907                             }
27908                             
27909                             offset += markerLength;
27910                             
27911                             continue;
27912                         }
27913                         
27914                         break;
27915                     }
27916                     
27917                 }
27918                 
27919                 var url = _this.urlAPI.createObjectURL(_this.file);
27920                 
27921                 _this.loadCanvas(url);
27922                 
27923                 return;
27924             }
27925             
27926             reader.readAsArrayBuffer(this.file);
27927             
27928         }
27929         
27930     },
27931     
27932     parseExifData : function(dataView, offset, length)
27933     {
27934         var tiffOffset = offset + 10,
27935             littleEndian,
27936             dirOffset;
27937     
27938         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27939             // No Exif data, might be XMP data instead
27940             return;
27941         }
27942         
27943         // Check for the ASCII code for "Exif" (0x45786966):
27944         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27945             // No Exif data, might be XMP data instead
27946             return;
27947         }
27948         if (tiffOffset + 8 > dataView.byteLength) {
27949             Roo.log('Invalid Exif data: Invalid segment size.');
27950             return;
27951         }
27952         // Check for the two null bytes:
27953         if (dataView.getUint16(offset + 8) !== 0x0000) {
27954             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27955             return;
27956         }
27957         // Check the byte alignment:
27958         switch (dataView.getUint16(tiffOffset)) {
27959         case 0x4949:
27960             littleEndian = true;
27961             break;
27962         case 0x4D4D:
27963             littleEndian = false;
27964             break;
27965         default:
27966             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27967             return;
27968         }
27969         // Check for the TIFF tag marker (0x002A):
27970         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27971             Roo.log('Invalid Exif data: Missing TIFF marker.');
27972             return;
27973         }
27974         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27975         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27976         
27977         this.parseExifTags(
27978             dataView,
27979             tiffOffset,
27980             tiffOffset + dirOffset,
27981             littleEndian
27982         );
27983     },
27984     
27985     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27986     {
27987         var tagsNumber,
27988             dirEndOffset,
27989             i;
27990         if (dirOffset + 6 > dataView.byteLength) {
27991             Roo.log('Invalid Exif data: Invalid directory offset.');
27992             return;
27993         }
27994         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27995         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27996         if (dirEndOffset + 4 > dataView.byteLength) {
27997             Roo.log('Invalid Exif data: Invalid directory size.');
27998             return;
27999         }
28000         for (i = 0; i < tagsNumber; i += 1) {
28001             this.parseExifTag(
28002                 dataView,
28003                 tiffOffset,
28004                 dirOffset + 2 + 12 * i, // tag offset
28005                 littleEndian
28006             );
28007         }
28008         // Return the offset to the next directory:
28009         return dataView.getUint32(dirEndOffset, littleEndian);
28010     },
28011     
28012     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28013     {
28014         var tag = dataView.getUint16(offset, littleEndian);
28015         
28016         this.exif[tag] = this.getExifValue(
28017             dataView,
28018             tiffOffset,
28019             offset,
28020             dataView.getUint16(offset + 2, littleEndian), // tag type
28021             dataView.getUint32(offset + 4, littleEndian), // tag length
28022             littleEndian
28023         );
28024     },
28025     
28026     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28027     {
28028         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28029             tagSize,
28030             dataOffset,
28031             values,
28032             i,
28033             str,
28034             c;
28035     
28036         if (!tagType) {
28037             Roo.log('Invalid Exif data: Invalid tag type.');
28038             return;
28039         }
28040         
28041         tagSize = tagType.size * length;
28042         // Determine if the value is contained in the dataOffset bytes,
28043         // or if the value at the dataOffset is a pointer to the actual data:
28044         dataOffset = tagSize > 4 ?
28045                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28046         if (dataOffset + tagSize > dataView.byteLength) {
28047             Roo.log('Invalid Exif data: Invalid data offset.');
28048             return;
28049         }
28050         if (length === 1) {
28051             return tagType.getValue(dataView, dataOffset, littleEndian);
28052         }
28053         values = [];
28054         for (i = 0; i < length; i += 1) {
28055             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28056         }
28057         
28058         if (tagType.ascii) {
28059             str = '';
28060             // Concatenate the chars:
28061             for (i = 0; i < values.length; i += 1) {
28062                 c = values[i];
28063                 // Ignore the terminating NULL byte(s):
28064                 if (c === '\u0000') {
28065                     break;
28066                 }
28067                 str += c;
28068             }
28069             return str;
28070         }
28071         return values;
28072     }
28073     
28074 });
28075
28076 Roo.apply(Roo.bootstrap.UploadCropbox, {
28077     tags : {
28078         'Orientation': 0x0112
28079     },
28080     
28081     Orientation: {
28082             1: 0, //'top-left',
28083 //            2: 'top-right',
28084             3: 180, //'bottom-right',
28085 //            4: 'bottom-left',
28086 //            5: 'left-top',
28087             6: 90, //'right-top',
28088 //            7: 'right-bottom',
28089             8: 270 //'left-bottom'
28090     },
28091     
28092     exifTagTypes : {
28093         // byte, 8-bit unsigned int:
28094         1: {
28095             getValue: function (dataView, dataOffset) {
28096                 return dataView.getUint8(dataOffset);
28097             },
28098             size: 1
28099         },
28100         // ascii, 8-bit byte:
28101         2: {
28102             getValue: function (dataView, dataOffset) {
28103                 return String.fromCharCode(dataView.getUint8(dataOffset));
28104             },
28105             size: 1,
28106             ascii: true
28107         },
28108         // short, 16 bit int:
28109         3: {
28110             getValue: function (dataView, dataOffset, littleEndian) {
28111                 return dataView.getUint16(dataOffset, littleEndian);
28112             },
28113             size: 2
28114         },
28115         // long, 32 bit int:
28116         4: {
28117             getValue: function (dataView, dataOffset, littleEndian) {
28118                 return dataView.getUint32(dataOffset, littleEndian);
28119             },
28120             size: 4
28121         },
28122         // rational = two long values, first is numerator, second is denominator:
28123         5: {
28124             getValue: function (dataView, dataOffset, littleEndian) {
28125                 return dataView.getUint32(dataOffset, littleEndian) /
28126                     dataView.getUint32(dataOffset + 4, littleEndian);
28127             },
28128             size: 8
28129         },
28130         // slong, 32 bit signed int:
28131         9: {
28132             getValue: function (dataView, dataOffset, littleEndian) {
28133                 return dataView.getInt32(dataOffset, littleEndian);
28134             },
28135             size: 4
28136         },
28137         // srational, two slongs, first is numerator, second is denominator:
28138         10: {
28139             getValue: function (dataView, dataOffset, littleEndian) {
28140                 return dataView.getInt32(dataOffset, littleEndian) /
28141                     dataView.getInt32(dataOffset + 4, littleEndian);
28142             },
28143             size: 8
28144         }
28145     },
28146     
28147     footer : {
28148         STANDARD : [
28149             {
28150                 tag : 'div',
28151                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28152                 action : 'rotate-left',
28153                 cn : [
28154                     {
28155                         tag : 'button',
28156                         cls : 'btn btn-default',
28157                         html : '<i class="fa fa-undo"></i>'
28158                     }
28159                 ]
28160             },
28161             {
28162                 tag : 'div',
28163                 cls : 'btn-group roo-upload-cropbox-picture',
28164                 action : 'picture',
28165                 cn : [
28166                     {
28167                         tag : 'button',
28168                         cls : 'btn btn-default',
28169                         html : '<i class="fa fa-picture-o"></i>'
28170                     }
28171                 ]
28172             },
28173             {
28174                 tag : 'div',
28175                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28176                 action : 'rotate-right',
28177                 cn : [
28178                     {
28179                         tag : 'button',
28180                         cls : 'btn btn-default',
28181                         html : '<i class="fa fa-repeat"></i>'
28182                     }
28183                 ]
28184             }
28185         ],
28186         DOCUMENT : [
28187             {
28188                 tag : 'div',
28189                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28190                 action : 'rotate-left',
28191                 cn : [
28192                     {
28193                         tag : 'button',
28194                         cls : 'btn btn-default',
28195                         html : '<i class="fa fa-undo"></i>'
28196                     }
28197                 ]
28198             },
28199             {
28200                 tag : 'div',
28201                 cls : 'btn-group roo-upload-cropbox-download',
28202                 action : 'download',
28203                 cn : [
28204                     {
28205                         tag : 'button',
28206                         cls : 'btn btn-default',
28207                         html : '<i class="fa fa-download"></i>'
28208                     }
28209                 ]
28210             },
28211             {
28212                 tag : 'div',
28213                 cls : 'btn-group roo-upload-cropbox-crop',
28214                 action : 'crop',
28215                 cn : [
28216                     {
28217                         tag : 'button',
28218                         cls : 'btn btn-default',
28219                         html : '<i class="fa fa-crop"></i>'
28220                     }
28221                 ]
28222             },
28223             {
28224                 tag : 'div',
28225                 cls : 'btn-group roo-upload-cropbox-trash',
28226                 action : 'trash',
28227                 cn : [
28228                     {
28229                         tag : 'button',
28230                         cls : 'btn btn-default',
28231                         html : '<i class="fa fa-trash"></i>'
28232                     }
28233                 ]
28234             },
28235             {
28236                 tag : 'div',
28237                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28238                 action : 'rotate-right',
28239                 cn : [
28240                     {
28241                         tag : 'button',
28242                         cls : 'btn btn-default',
28243                         html : '<i class="fa fa-repeat"></i>'
28244                     }
28245                 ]
28246             }
28247         ],
28248         ROTATOR : [
28249             {
28250                 tag : 'div',
28251                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28252                 action : 'rotate-left',
28253                 cn : [
28254                     {
28255                         tag : 'button',
28256                         cls : 'btn btn-default',
28257                         html : '<i class="fa fa-undo"></i>'
28258                     }
28259                 ]
28260             },
28261             {
28262                 tag : 'div',
28263                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28264                 action : 'rotate-right',
28265                 cn : [
28266                     {
28267                         tag : 'button',
28268                         cls : 'btn btn-default',
28269                         html : '<i class="fa fa-repeat"></i>'
28270                     }
28271                 ]
28272             }
28273         ]
28274     }
28275 });
28276
28277 /*
28278 * Licence: LGPL
28279 */
28280
28281 /**
28282  * @class Roo.bootstrap.DocumentManager
28283  * @extends Roo.bootstrap.Component
28284  * Bootstrap DocumentManager class
28285  * @cfg {String} paramName default 'imageUpload'
28286  * @cfg {String} toolTipName default 'filename'
28287  * @cfg {String} method default POST
28288  * @cfg {String} url action url
28289  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28290  * @cfg {Boolean} multiple multiple upload default true
28291  * @cfg {Number} thumbSize default 300
28292  * @cfg {String} fieldLabel
28293  * @cfg {Number} labelWidth default 4
28294  * @cfg {String} labelAlign (left|top) default left
28295  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28296 * @cfg {Number} labellg set the width of label (1-12)
28297  * @cfg {Number} labelmd set the width of label (1-12)
28298  * @cfg {Number} labelsm set the width of label (1-12)
28299  * @cfg {Number} labelxs set the width of label (1-12)
28300  * 
28301  * @constructor
28302  * Create a new DocumentManager
28303  * @param {Object} config The config object
28304  */
28305
28306 Roo.bootstrap.DocumentManager = function(config){
28307     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28308     
28309     this.files = [];
28310     this.delegates = [];
28311     
28312     this.addEvents({
28313         /**
28314          * @event initial
28315          * Fire when initial the DocumentManager
28316          * @param {Roo.bootstrap.DocumentManager} this
28317          */
28318         "initial" : true,
28319         /**
28320          * @event inspect
28321          * inspect selected file
28322          * @param {Roo.bootstrap.DocumentManager} this
28323          * @param {File} file
28324          */
28325         "inspect" : true,
28326         /**
28327          * @event exception
28328          * Fire when xhr load exception
28329          * @param {Roo.bootstrap.DocumentManager} this
28330          * @param {XMLHttpRequest} xhr
28331          */
28332         "exception" : true,
28333         /**
28334          * @event afterupload
28335          * Fire when xhr load exception
28336          * @param {Roo.bootstrap.DocumentManager} this
28337          * @param {XMLHttpRequest} xhr
28338          */
28339         "afterupload" : true,
28340         /**
28341          * @event prepare
28342          * prepare the form data
28343          * @param {Roo.bootstrap.DocumentManager} this
28344          * @param {Object} formData
28345          */
28346         "prepare" : true,
28347         /**
28348          * @event remove
28349          * Fire when remove the file
28350          * @param {Roo.bootstrap.DocumentManager} this
28351          * @param {Object} file
28352          */
28353         "remove" : true,
28354         /**
28355          * @event refresh
28356          * Fire after refresh the file
28357          * @param {Roo.bootstrap.DocumentManager} this
28358          */
28359         "refresh" : true,
28360         /**
28361          * @event click
28362          * Fire after click the image
28363          * @param {Roo.bootstrap.DocumentManager} this
28364          * @param {Object} file
28365          */
28366         "click" : true,
28367         /**
28368          * @event edit
28369          * Fire when upload a image and editable set to true
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          * @param {Object} file
28372          */
28373         "edit" : true,
28374         /**
28375          * @event beforeselectfile
28376          * Fire before select file
28377          * @param {Roo.bootstrap.DocumentManager} this
28378          */
28379         "beforeselectfile" : true,
28380         /**
28381          * @event process
28382          * Fire before process file
28383          * @param {Roo.bootstrap.DocumentManager} this
28384          * @param {Object} file
28385          */
28386         "process" : true
28387         
28388     });
28389 };
28390
28391 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28392     
28393     boxes : 0,
28394     inputName : '',
28395     thumbSize : 300,
28396     multiple : true,
28397     files : false,
28398     method : 'POST',
28399     url : '',
28400     paramName : 'imageUpload',
28401     toolTipName : 'filename',
28402     fieldLabel : '',
28403     labelWidth : 4,
28404     labelAlign : 'left',
28405     editable : true,
28406     delegates : false,
28407     xhr : false, 
28408     
28409     labellg : 0,
28410     labelmd : 0,
28411     labelsm : 0,
28412     labelxs : 0,
28413     
28414     getAutoCreate : function()
28415     {   
28416         var managerWidget = {
28417             tag : 'div',
28418             cls : 'roo-document-manager',
28419             cn : [
28420                 {
28421                     tag : 'input',
28422                     cls : 'roo-document-manager-selector',
28423                     type : 'file'
28424                 },
28425                 {
28426                     tag : 'div',
28427                     cls : 'roo-document-manager-uploader',
28428                     cn : [
28429                         {
28430                             tag : 'div',
28431                             cls : 'roo-document-manager-upload-btn',
28432                             html : '<i class="fa fa-plus"></i>'
28433                         }
28434                     ]
28435                     
28436                 }
28437             ]
28438         };
28439         
28440         var content = [
28441             {
28442                 tag : 'div',
28443                 cls : 'column col-md-12',
28444                 cn : managerWidget
28445             }
28446         ];
28447         
28448         if(this.fieldLabel.length){
28449             
28450             content = [
28451                 {
28452                     tag : 'div',
28453                     cls : 'column col-md-12',
28454                     html : this.fieldLabel
28455                 },
28456                 {
28457                     tag : 'div',
28458                     cls : 'column col-md-12',
28459                     cn : managerWidget
28460                 }
28461             ];
28462
28463             if(this.labelAlign == 'left'){
28464                 content = [
28465                     {
28466                         tag : 'div',
28467                         cls : 'column',
28468                         html : this.fieldLabel
28469                     },
28470                     {
28471                         tag : 'div',
28472                         cls : 'column',
28473                         cn : managerWidget
28474                     }
28475                 ];
28476                 
28477                 if(this.labelWidth > 12){
28478                     content[0].style = "width: " + this.labelWidth + 'px';
28479                 }
28480
28481                 if(this.labelWidth < 13 && this.labelmd == 0){
28482                     this.labelmd = this.labelWidth;
28483                 }
28484
28485                 if(this.labellg > 0){
28486                     content[0].cls += ' col-lg-' + this.labellg;
28487                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28488                 }
28489
28490                 if(this.labelmd > 0){
28491                     content[0].cls += ' col-md-' + this.labelmd;
28492                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28493                 }
28494
28495                 if(this.labelsm > 0){
28496                     content[0].cls += ' col-sm-' + this.labelsm;
28497                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28498                 }
28499
28500                 if(this.labelxs > 0){
28501                     content[0].cls += ' col-xs-' + this.labelxs;
28502                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28503                 }
28504                 
28505             }
28506         }
28507         
28508         var cfg = {
28509             tag : 'div',
28510             cls : 'row clearfix',
28511             cn : content
28512         };
28513         
28514         return cfg;
28515         
28516     },
28517     
28518     initEvents : function()
28519     {
28520         this.managerEl = this.el.select('.roo-document-manager', true).first();
28521         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28522         
28523         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28524         this.selectorEl.hide();
28525         
28526         if(this.multiple){
28527             this.selectorEl.attr('multiple', 'multiple');
28528         }
28529         
28530         this.selectorEl.on('change', this.onFileSelected, this);
28531         
28532         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28533         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28534         
28535         this.uploader.on('click', this.onUploaderClick, this);
28536         
28537         this.renderProgressDialog();
28538         
28539         var _this = this;
28540         
28541         window.addEventListener("resize", function() { _this.refresh(); } );
28542         
28543         this.fireEvent('initial', this);
28544     },
28545     
28546     renderProgressDialog : function()
28547     {
28548         var _this = this;
28549         
28550         this.progressDialog = new Roo.bootstrap.Modal({
28551             cls : 'roo-document-manager-progress-dialog',
28552             allow_close : false,
28553             title : '',
28554             buttons : [
28555                 {
28556                     name  :'cancel',
28557                     weight : 'danger',
28558                     html : 'Cancel'
28559                 }
28560             ], 
28561             listeners : { 
28562                 btnclick : function() {
28563                     _this.uploadCancel();
28564                     this.hide();
28565                 }
28566             }
28567         });
28568          
28569         this.progressDialog.render(Roo.get(document.body));
28570          
28571         this.progress = new Roo.bootstrap.Progress({
28572             cls : 'roo-document-manager-progress',
28573             active : true,
28574             striped : true
28575         });
28576         
28577         this.progress.render(this.progressDialog.getChildContainer());
28578         
28579         this.progressBar = new Roo.bootstrap.ProgressBar({
28580             cls : 'roo-document-manager-progress-bar',
28581             aria_valuenow : 0,
28582             aria_valuemin : 0,
28583             aria_valuemax : 12,
28584             panel : 'success'
28585         });
28586         
28587         this.progressBar.render(this.progress.getChildContainer());
28588     },
28589     
28590     onUploaderClick : function(e)
28591     {
28592         e.preventDefault();
28593      
28594         if(this.fireEvent('beforeselectfile', this) != false){
28595             this.selectorEl.dom.click();
28596         }
28597         
28598     },
28599     
28600     onFileSelected : function(e)
28601     {
28602         e.preventDefault();
28603         
28604         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28605             return;
28606         }
28607         
28608         Roo.each(this.selectorEl.dom.files, function(file){
28609             if(this.fireEvent('inspect', this, file) != false){
28610                 this.files.push(file);
28611             }
28612         }, this);
28613         
28614         this.queue();
28615         
28616     },
28617     
28618     queue : function()
28619     {
28620         this.selectorEl.dom.value = '';
28621         
28622         if(!this.files.length){
28623             return;
28624         }
28625         
28626         if(this.boxes > 0 && this.files.length > this.boxes){
28627             this.files = this.files.slice(0, this.boxes);
28628         }
28629         
28630         this.uploader.show();
28631         
28632         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28633             this.uploader.hide();
28634         }
28635         
28636         var _this = this;
28637         
28638         var files = [];
28639         
28640         var docs = [];
28641         
28642         Roo.each(this.files, function(file){
28643             
28644             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28645                 var f = this.renderPreview(file);
28646                 files.push(f);
28647                 return;
28648             }
28649             
28650             if(file.type.indexOf('image') != -1){
28651                 this.delegates.push(
28652                     (function(){
28653                         _this.process(file);
28654                     }).createDelegate(this)
28655                 );
28656         
28657                 return;
28658             }
28659             
28660             docs.push(
28661                 (function(){
28662                     _this.process(file);
28663                 }).createDelegate(this)
28664             );
28665             
28666         }, this);
28667         
28668         this.files = files;
28669         
28670         this.delegates = this.delegates.concat(docs);
28671         
28672         if(!this.delegates.length){
28673             this.refresh();
28674             return;
28675         }
28676         
28677         this.progressBar.aria_valuemax = this.delegates.length;
28678         
28679         this.arrange();
28680         
28681         return;
28682     },
28683     
28684     arrange : function()
28685     {
28686         if(!this.delegates.length){
28687             this.progressDialog.hide();
28688             this.refresh();
28689             return;
28690         }
28691         
28692         var delegate = this.delegates.shift();
28693         
28694         this.progressDialog.show();
28695         
28696         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28697         
28698         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28699         
28700         delegate();
28701     },
28702     
28703     refresh : function()
28704     {
28705         this.uploader.show();
28706         
28707         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28708             this.uploader.hide();
28709         }
28710         
28711         Roo.isTouch ? this.closable(false) : this.closable(true);
28712         
28713         this.fireEvent('refresh', this);
28714     },
28715     
28716     onRemove : function(e, el, o)
28717     {
28718         e.preventDefault();
28719         
28720         this.fireEvent('remove', this, o);
28721         
28722     },
28723     
28724     remove : function(o)
28725     {
28726         var files = [];
28727         
28728         Roo.each(this.files, function(file){
28729             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28730                 files.push(file);
28731                 return;
28732             }
28733
28734             o.target.remove();
28735
28736         }, this);
28737         
28738         this.files = files;
28739         
28740         this.refresh();
28741     },
28742     
28743     clear : function()
28744     {
28745         Roo.each(this.files, function(file){
28746             if(!file.target){
28747                 return;
28748             }
28749             
28750             file.target.remove();
28751
28752         }, this);
28753         
28754         this.files = [];
28755         
28756         this.refresh();
28757     },
28758     
28759     onClick : function(e, el, o)
28760     {
28761         e.preventDefault();
28762         
28763         this.fireEvent('click', this, o);
28764         
28765     },
28766     
28767     closable : function(closable)
28768     {
28769         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28770             
28771             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28772             
28773             if(closable){
28774                 el.show();
28775                 return;
28776             }
28777             
28778             el.hide();
28779             
28780         }, this);
28781     },
28782     
28783     xhrOnLoad : function(xhr)
28784     {
28785         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28786             el.remove();
28787         }, this);
28788         
28789         if (xhr.readyState !== 4) {
28790             this.arrange();
28791             this.fireEvent('exception', this, xhr);
28792             return;
28793         }
28794
28795         var response = Roo.decode(xhr.responseText);
28796         
28797         if(!response.success){
28798             this.arrange();
28799             this.fireEvent('exception', this, xhr);
28800             return;
28801         }
28802         
28803         var file = this.renderPreview(response.data);
28804         
28805         this.files.push(file);
28806         
28807         this.arrange();
28808         
28809         this.fireEvent('afterupload', this, xhr);
28810         
28811     },
28812     
28813     xhrOnError : function(xhr)
28814     {
28815         Roo.log('xhr on error');
28816         
28817         var response = Roo.decode(xhr.responseText);
28818           
28819         Roo.log(response);
28820         
28821         this.arrange();
28822     },
28823     
28824     process : function(file)
28825     {
28826         if(this.fireEvent('process', this, file) !== false){
28827             if(this.editable && file.type.indexOf('image') != -1){
28828                 this.fireEvent('edit', this, file);
28829                 return;
28830             }
28831
28832             this.uploadStart(file, false);
28833
28834             return;
28835         }
28836         
28837     },
28838     
28839     uploadStart : function(file, crop)
28840     {
28841         this.xhr = new XMLHttpRequest();
28842         
28843         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28844             this.arrange();
28845             return;
28846         }
28847         
28848         file.xhr = this.xhr;
28849             
28850         this.managerEl.createChild({
28851             tag : 'div',
28852             cls : 'roo-document-manager-loading',
28853             cn : [
28854                 {
28855                     tag : 'div',
28856                     tooltip : file.name,
28857                     cls : 'roo-document-manager-thumb',
28858                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28859                 }
28860             ]
28861
28862         });
28863
28864         this.xhr.open(this.method, this.url, true);
28865         
28866         var headers = {
28867             "Accept": "application/json",
28868             "Cache-Control": "no-cache",
28869             "X-Requested-With": "XMLHttpRequest"
28870         };
28871         
28872         for (var headerName in headers) {
28873             var headerValue = headers[headerName];
28874             if (headerValue) {
28875                 this.xhr.setRequestHeader(headerName, headerValue);
28876             }
28877         }
28878         
28879         var _this = this;
28880         
28881         this.xhr.onload = function()
28882         {
28883             _this.xhrOnLoad(_this.xhr);
28884         }
28885         
28886         this.xhr.onerror = function()
28887         {
28888             _this.xhrOnError(_this.xhr);
28889         }
28890         
28891         var formData = new FormData();
28892
28893         formData.append('returnHTML', 'NO');
28894         
28895         if(crop){
28896             formData.append('crop', crop);
28897         }
28898         
28899         formData.append(this.paramName, file, file.name);
28900         
28901         var options = {
28902             file : file, 
28903             manually : false
28904         };
28905         
28906         if(this.fireEvent('prepare', this, formData, options) != false){
28907             
28908             if(options.manually){
28909                 return;
28910             }
28911             
28912             this.xhr.send(formData);
28913             return;
28914         };
28915         
28916         this.uploadCancel();
28917     },
28918     
28919     uploadCancel : function()
28920     {
28921         if (this.xhr) {
28922             this.xhr.abort();
28923         }
28924         
28925         this.delegates = [];
28926         
28927         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28928             el.remove();
28929         }, this);
28930         
28931         this.arrange();
28932     },
28933     
28934     renderPreview : function(file)
28935     {
28936         if(typeof(file.target) != 'undefined' && file.target){
28937             return file;
28938         }
28939         
28940         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28941         
28942         var previewEl = this.managerEl.createChild({
28943             tag : 'div',
28944             cls : 'roo-document-manager-preview',
28945             cn : [
28946                 {
28947                     tag : 'div',
28948                     tooltip : file[this.toolTipName],
28949                     cls : 'roo-document-manager-thumb',
28950                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28951                 },
28952                 {
28953                     tag : 'button',
28954                     cls : 'close',
28955                     html : '<i class="fa fa-times-circle"></i>'
28956                 }
28957             ]
28958         });
28959
28960         var close = previewEl.select('button.close', true).first();
28961
28962         close.on('click', this.onRemove, this, file);
28963
28964         file.target = previewEl;
28965
28966         var image = previewEl.select('img', true).first();
28967         
28968         var _this = this;
28969         
28970         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28971         
28972         image.on('click', this.onClick, this, file);
28973         
28974         return file;
28975         
28976     },
28977     
28978     onPreviewLoad : function(file, image)
28979     {
28980         if(typeof(file.target) == 'undefined' || !file.target){
28981             return;
28982         }
28983         
28984         var width = image.dom.naturalWidth || image.dom.width;
28985         var height = image.dom.naturalHeight || image.dom.height;
28986         
28987         if(width > height){
28988             file.target.addClass('wide');
28989             return;
28990         }
28991         
28992         file.target.addClass('tall');
28993         return;
28994         
28995     },
28996     
28997     uploadFromSource : function(file, crop)
28998     {
28999         this.xhr = new XMLHttpRequest();
29000         
29001         this.managerEl.createChild({
29002             tag : 'div',
29003             cls : 'roo-document-manager-loading',
29004             cn : [
29005                 {
29006                     tag : 'div',
29007                     tooltip : file.name,
29008                     cls : 'roo-document-manager-thumb',
29009                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29010                 }
29011             ]
29012
29013         });
29014
29015         this.xhr.open(this.method, this.url, true);
29016         
29017         var headers = {
29018             "Accept": "application/json",
29019             "Cache-Control": "no-cache",
29020             "X-Requested-With": "XMLHttpRequest"
29021         };
29022         
29023         for (var headerName in headers) {
29024             var headerValue = headers[headerName];
29025             if (headerValue) {
29026                 this.xhr.setRequestHeader(headerName, headerValue);
29027             }
29028         }
29029         
29030         var _this = this;
29031         
29032         this.xhr.onload = function()
29033         {
29034             _this.xhrOnLoad(_this.xhr);
29035         }
29036         
29037         this.xhr.onerror = function()
29038         {
29039             _this.xhrOnError(_this.xhr);
29040         }
29041         
29042         var formData = new FormData();
29043
29044         formData.append('returnHTML', 'NO');
29045         
29046         formData.append('crop', crop);
29047         
29048         if(typeof(file.filename) != 'undefined'){
29049             formData.append('filename', file.filename);
29050         }
29051         
29052         if(typeof(file.mimetype) != 'undefined'){
29053             formData.append('mimetype', file.mimetype);
29054         }
29055         
29056         Roo.log(formData);
29057         
29058         if(this.fireEvent('prepare', this, formData) != false){
29059             this.xhr.send(formData);
29060         };
29061     }
29062 });
29063
29064 /*
29065 * Licence: LGPL
29066 */
29067
29068 /**
29069  * @class Roo.bootstrap.DocumentViewer
29070  * @extends Roo.bootstrap.Component
29071  * Bootstrap DocumentViewer class
29072  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29073  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29074  * 
29075  * @constructor
29076  * Create a new DocumentViewer
29077  * @param {Object} config The config object
29078  */
29079
29080 Roo.bootstrap.DocumentViewer = function(config){
29081     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29082     
29083     this.addEvents({
29084         /**
29085          * @event initial
29086          * Fire after initEvent
29087          * @param {Roo.bootstrap.DocumentViewer} this
29088          */
29089         "initial" : true,
29090         /**
29091          * @event click
29092          * Fire after click
29093          * @param {Roo.bootstrap.DocumentViewer} this
29094          */
29095         "click" : true,
29096         /**
29097          * @event download
29098          * Fire after download button
29099          * @param {Roo.bootstrap.DocumentViewer} this
29100          */
29101         "download" : true,
29102         /**
29103          * @event trash
29104          * Fire after trash button
29105          * @param {Roo.bootstrap.DocumentViewer} this
29106          */
29107         "trash" : true
29108         
29109     });
29110 };
29111
29112 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29113     
29114     showDownload : true,
29115     
29116     showTrash : true,
29117     
29118     getAutoCreate : function()
29119     {
29120         var cfg = {
29121             tag : 'div',
29122             cls : 'roo-document-viewer',
29123             cn : [
29124                 {
29125                     tag : 'div',
29126                     cls : 'roo-document-viewer-body',
29127                     cn : [
29128                         {
29129                             tag : 'div',
29130                             cls : 'roo-document-viewer-thumb',
29131                             cn : [
29132                                 {
29133                                     tag : 'img',
29134                                     cls : 'roo-document-viewer-image'
29135                                 }
29136                             ]
29137                         }
29138                     ]
29139                 },
29140                 {
29141                     tag : 'div',
29142                     cls : 'roo-document-viewer-footer',
29143                     cn : {
29144                         tag : 'div',
29145                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29146                         cn : [
29147                             {
29148                                 tag : 'div',
29149                                 cls : 'btn-group roo-document-viewer-download',
29150                                 cn : [
29151                                     {
29152                                         tag : 'button',
29153                                         cls : 'btn btn-default',
29154                                         html : '<i class="fa fa-download"></i>'
29155                                     }
29156                                 ]
29157                             },
29158                             {
29159                                 tag : 'div',
29160                                 cls : 'btn-group roo-document-viewer-trash',
29161                                 cn : [
29162                                     {
29163                                         tag : 'button',
29164                                         cls : 'btn btn-default',
29165                                         html : '<i class="fa fa-trash"></i>'
29166                                     }
29167                                 ]
29168                             }
29169                         ]
29170                     }
29171                 }
29172             ]
29173         };
29174         
29175         return cfg;
29176     },
29177     
29178     initEvents : function()
29179     {
29180         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29181         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29182         
29183         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29184         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29185         
29186         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29187         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29188         
29189         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29190         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29191         
29192         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29193         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29194         
29195         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29196         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29197         
29198         this.bodyEl.on('click', this.onClick, this);
29199         this.downloadBtn.on('click', this.onDownload, this);
29200         this.trashBtn.on('click', this.onTrash, this);
29201         
29202         this.downloadBtn.hide();
29203         this.trashBtn.hide();
29204         
29205         if(this.showDownload){
29206             this.downloadBtn.show();
29207         }
29208         
29209         if(this.showTrash){
29210             this.trashBtn.show();
29211         }
29212         
29213         if(!this.showDownload && !this.showTrash) {
29214             this.footerEl.hide();
29215         }
29216         
29217     },
29218     
29219     initial : function()
29220     {
29221         this.fireEvent('initial', this);
29222         
29223     },
29224     
29225     onClick : function(e)
29226     {
29227         e.preventDefault();
29228         
29229         this.fireEvent('click', this);
29230     },
29231     
29232     onDownload : function(e)
29233     {
29234         e.preventDefault();
29235         
29236         this.fireEvent('download', this);
29237     },
29238     
29239     onTrash : function(e)
29240     {
29241         e.preventDefault();
29242         
29243         this.fireEvent('trash', this);
29244     }
29245     
29246 });
29247 /*
29248  * - LGPL
29249  *
29250  * nav progress bar
29251  * 
29252  */
29253
29254 /**
29255  * @class Roo.bootstrap.NavProgressBar
29256  * @extends Roo.bootstrap.Component
29257  * Bootstrap NavProgressBar class
29258  * 
29259  * @constructor
29260  * Create a new nav progress bar
29261  * @param {Object} config The config object
29262  */
29263
29264 Roo.bootstrap.NavProgressBar = function(config){
29265     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29266
29267     this.bullets = this.bullets || [];
29268    
29269 //    Roo.bootstrap.NavProgressBar.register(this);
29270      this.addEvents({
29271         /**
29272              * @event changed
29273              * Fires when the active item changes
29274              * @param {Roo.bootstrap.NavProgressBar} this
29275              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29276              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29277          */
29278         'changed': true
29279      });
29280     
29281 };
29282
29283 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29284     
29285     bullets : [],
29286     barItems : [],
29287     
29288     getAutoCreate : function()
29289     {
29290         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29291         
29292         cfg = {
29293             tag : 'div',
29294             cls : 'roo-navigation-bar-group',
29295             cn : [
29296                 {
29297                     tag : 'div',
29298                     cls : 'roo-navigation-top-bar'
29299                 },
29300                 {
29301                     tag : 'div',
29302                     cls : 'roo-navigation-bullets-bar',
29303                     cn : [
29304                         {
29305                             tag : 'ul',
29306                             cls : 'roo-navigation-bar'
29307                         }
29308                     ]
29309                 },
29310                 
29311                 {
29312                     tag : 'div',
29313                     cls : 'roo-navigation-bottom-bar'
29314                 }
29315             ]
29316             
29317         };
29318         
29319         return cfg;
29320         
29321     },
29322     
29323     initEvents: function() 
29324     {
29325         
29326     },
29327     
29328     onRender : function(ct, position) 
29329     {
29330         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29331         
29332         if(this.bullets.length){
29333             Roo.each(this.bullets, function(b){
29334                this.addItem(b);
29335             }, this);
29336         }
29337         
29338         this.format();
29339         
29340     },
29341     
29342     addItem : function(cfg)
29343     {
29344         var item = new Roo.bootstrap.NavProgressItem(cfg);
29345         
29346         item.parentId = this.id;
29347         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29348         
29349         if(cfg.html){
29350             var top = new Roo.bootstrap.Element({
29351                 tag : 'div',
29352                 cls : 'roo-navigation-bar-text'
29353             });
29354             
29355             var bottom = new Roo.bootstrap.Element({
29356                 tag : 'div',
29357                 cls : 'roo-navigation-bar-text'
29358             });
29359             
29360             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29361             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29362             
29363             var topText = new Roo.bootstrap.Element({
29364                 tag : 'span',
29365                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29366             });
29367             
29368             var bottomText = new Roo.bootstrap.Element({
29369                 tag : 'span',
29370                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29371             });
29372             
29373             topText.onRender(top.el, null);
29374             bottomText.onRender(bottom.el, null);
29375             
29376             item.topEl = top;
29377             item.bottomEl = bottom;
29378         }
29379         
29380         this.barItems.push(item);
29381         
29382         return item;
29383     },
29384     
29385     getActive : function()
29386     {
29387         var active = false;
29388         
29389         Roo.each(this.barItems, function(v){
29390             
29391             if (!v.isActive()) {
29392                 return;
29393             }
29394             
29395             active = v;
29396             return false;
29397             
29398         });
29399         
29400         return active;
29401     },
29402     
29403     setActiveItem : function(item)
29404     {
29405         var prev = false;
29406         
29407         Roo.each(this.barItems, function(v){
29408             if (v.rid == item.rid) {
29409                 return ;
29410             }
29411             
29412             if (v.isActive()) {
29413                 v.setActive(false);
29414                 prev = v;
29415             }
29416         });
29417
29418         item.setActive(true);
29419         
29420         this.fireEvent('changed', this, item, prev);
29421     },
29422     
29423     getBarItem: function(rid)
29424     {
29425         var ret = false;
29426         
29427         Roo.each(this.barItems, function(e) {
29428             if (e.rid != rid) {
29429                 return;
29430             }
29431             
29432             ret =  e;
29433             return false;
29434         });
29435         
29436         return ret;
29437     },
29438     
29439     indexOfItem : function(item)
29440     {
29441         var index = false;
29442         
29443         Roo.each(this.barItems, function(v, i){
29444             
29445             if (v.rid != item.rid) {
29446                 return;
29447             }
29448             
29449             index = i;
29450             return false
29451         });
29452         
29453         return index;
29454     },
29455     
29456     setActiveNext : function()
29457     {
29458         var i = this.indexOfItem(this.getActive());
29459         
29460         if (i > this.barItems.length) {
29461             return;
29462         }
29463         
29464         this.setActiveItem(this.barItems[i+1]);
29465     },
29466     
29467     setActivePrev : function()
29468     {
29469         var i = this.indexOfItem(this.getActive());
29470         
29471         if (i  < 1) {
29472             return;
29473         }
29474         
29475         this.setActiveItem(this.barItems[i-1]);
29476     },
29477     
29478     format : function()
29479     {
29480         if(!this.barItems.length){
29481             return;
29482         }
29483      
29484         var width = 100 / this.barItems.length;
29485         
29486         Roo.each(this.barItems, function(i){
29487             i.el.setStyle('width', width + '%');
29488             i.topEl.el.setStyle('width', width + '%');
29489             i.bottomEl.el.setStyle('width', width + '%');
29490         }, this);
29491         
29492     }
29493     
29494 });
29495 /*
29496  * - LGPL
29497  *
29498  * Nav Progress Item
29499  * 
29500  */
29501
29502 /**
29503  * @class Roo.bootstrap.NavProgressItem
29504  * @extends Roo.bootstrap.Component
29505  * Bootstrap NavProgressItem class
29506  * @cfg {String} rid the reference id
29507  * @cfg {Boolean} active (true|false) Is item active default false
29508  * @cfg {Boolean} disabled (true|false) Is item active default false
29509  * @cfg {String} html
29510  * @cfg {String} position (top|bottom) text position default bottom
29511  * @cfg {String} icon show icon instead of number
29512  * 
29513  * @constructor
29514  * Create a new NavProgressItem
29515  * @param {Object} config The config object
29516  */
29517 Roo.bootstrap.NavProgressItem = function(config){
29518     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29519     this.addEvents({
29520         // raw events
29521         /**
29522          * @event click
29523          * The raw click event for the entire grid.
29524          * @param {Roo.bootstrap.NavProgressItem} this
29525          * @param {Roo.EventObject} e
29526          */
29527         "click" : true
29528     });
29529    
29530 };
29531
29532 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29533     
29534     rid : '',
29535     active : false,
29536     disabled : false,
29537     html : '',
29538     position : 'bottom',
29539     icon : false,
29540     
29541     getAutoCreate : function()
29542     {
29543         var iconCls = 'roo-navigation-bar-item-icon';
29544         
29545         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29546         
29547         var cfg = {
29548             tag: 'li',
29549             cls: 'roo-navigation-bar-item',
29550             cn : [
29551                 {
29552                     tag : 'i',
29553                     cls : iconCls
29554                 }
29555             ]
29556         };
29557         
29558         if(this.active){
29559             cfg.cls += ' active';
29560         }
29561         if(this.disabled){
29562             cfg.cls += ' disabled';
29563         }
29564         
29565         return cfg;
29566     },
29567     
29568     disable : function()
29569     {
29570         this.setDisabled(true);
29571     },
29572     
29573     enable : function()
29574     {
29575         this.setDisabled(false);
29576     },
29577     
29578     initEvents: function() 
29579     {
29580         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29581         
29582         this.iconEl.on('click', this.onClick, this);
29583     },
29584     
29585     onClick : function(e)
29586     {
29587         e.preventDefault();
29588         
29589         if(this.disabled){
29590             return;
29591         }
29592         
29593         if(this.fireEvent('click', this, e) === false){
29594             return;
29595         };
29596         
29597         this.parent().setActiveItem(this);
29598     },
29599     
29600     isActive: function () 
29601     {
29602         return this.active;
29603     },
29604     
29605     setActive : function(state)
29606     {
29607         if(this.active == state){
29608             return;
29609         }
29610         
29611         this.active = state;
29612         
29613         if (state) {
29614             this.el.addClass('active');
29615             return;
29616         }
29617         
29618         this.el.removeClass('active');
29619         
29620         return;
29621     },
29622     
29623     setDisabled : function(state)
29624     {
29625         if(this.disabled == state){
29626             return;
29627         }
29628         
29629         this.disabled = state;
29630         
29631         if (state) {
29632             this.el.addClass('disabled');
29633             return;
29634         }
29635         
29636         this.el.removeClass('disabled');
29637     },
29638     
29639     tooltipEl : function()
29640     {
29641         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29642     }
29643 });
29644  
29645
29646  /*
29647  * - LGPL
29648  *
29649  * FieldLabel
29650  * 
29651  */
29652
29653 /**
29654  * @class Roo.bootstrap.FieldLabel
29655  * @extends Roo.bootstrap.Component
29656  * Bootstrap FieldLabel class
29657  * @cfg {String} html contents of the element
29658  * @cfg {String} tag tag of the element default label
29659  * @cfg {String} cls class of the element
29660  * @cfg {String} target label target 
29661  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29662  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29663  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29664  * @cfg {String} iconTooltip default "This field is required"
29665  * 
29666  * @constructor
29667  * Create a new FieldLabel
29668  * @param {Object} config The config object
29669  */
29670
29671 Roo.bootstrap.FieldLabel = function(config){
29672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29673     
29674     this.addEvents({
29675             /**
29676              * @event invalid
29677              * Fires after the field has been marked as invalid.
29678              * @param {Roo.form.FieldLabel} this
29679              * @param {String} msg The validation message
29680              */
29681             invalid : true,
29682             /**
29683              * @event valid
29684              * Fires after the field has been validated with no errors.
29685              * @param {Roo.form.FieldLabel} this
29686              */
29687             valid : true
29688         });
29689 };
29690
29691 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29692     
29693     tag: 'label',
29694     cls: '',
29695     html: '',
29696     target: '',
29697     allowBlank : true,
29698     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29699     validClass : 'text-success fa fa-lg fa-check',
29700     iconTooltip : 'This field is required',
29701     
29702     getAutoCreate : function(){
29703         
29704         var cfg = {
29705             tag : this.tag,
29706             cls : 'roo-bootstrap-field-label ' + this.cls,
29707             for : this.target,
29708             cn : [
29709                 {
29710                     tag : 'i',
29711                     cls : '',
29712                     tooltip : this.iconTooltip
29713                 },
29714                 {
29715                     tag : 'span',
29716                     html : this.html
29717                 }
29718             ] 
29719         };
29720         
29721         return cfg;
29722     },
29723     
29724     initEvents: function() 
29725     {
29726         Roo.bootstrap.Element.superclass.initEvents.call(this);
29727         
29728         this.iconEl = this.el.select('i', true).first();
29729         
29730         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29731         
29732         Roo.bootstrap.FieldLabel.register(this);
29733     },
29734     
29735     /**
29736      * Mark this field as valid
29737      */
29738     markValid : function()
29739     {
29740         this.iconEl.show();
29741         
29742         this.iconEl.removeClass(this.invalidClass);
29743         
29744         this.iconEl.addClass(this.validClass);
29745         
29746         this.fireEvent('valid', this);
29747     },
29748     
29749     /**
29750      * Mark this field as invalid
29751      * @param {String} msg The validation message
29752      */
29753     markInvalid : function(msg)
29754     {
29755         this.iconEl.show();
29756         
29757         this.iconEl.removeClass(this.validClass);
29758         
29759         this.iconEl.addClass(this.invalidClass);
29760         
29761         this.fireEvent('invalid', this, msg);
29762     }
29763     
29764    
29765 });
29766
29767 Roo.apply(Roo.bootstrap.FieldLabel, {
29768     
29769     groups: {},
29770     
29771      /**
29772     * register a FieldLabel Group
29773     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29774     */
29775     register : function(label)
29776     {
29777         if(this.groups.hasOwnProperty(label.target)){
29778             return;
29779         }
29780      
29781         this.groups[label.target] = label;
29782         
29783     },
29784     /**
29785     * fetch a FieldLabel Group based on the target
29786     * @param {string} target
29787     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29788     */
29789     get: function(target) {
29790         if (typeof(this.groups[target]) == 'undefined') {
29791             return false;
29792         }
29793         
29794         return this.groups[target] ;
29795     }
29796 });
29797
29798  
29799
29800  /*
29801  * - LGPL
29802  *
29803  * page DateSplitField.
29804  * 
29805  */
29806
29807
29808 /**
29809  * @class Roo.bootstrap.DateSplitField
29810  * @extends Roo.bootstrap.Component
29811  * Bootstrap DateSplitField class
29812  * @cfg {string} fieldLabel - the label associated
29813  * @cfg {Number} labelWidth set the width of label (0-12)
29814  * @cfg {String} labelAlign (top|left)
29815  * @cfg {Boolean} dayAllowBlank (true|false) default false
29816  * @cfg {Boolean} monthAllowBlank (true|false) default false
29817  * @cfg {Boolean} yearAllowBlank (true|false) default false
29818  * @cfg {string} dayPlaceholder 
29819  * @cfg {string} monthPlaceholder
29820  * @cfg {string} yearPlaceholder
29821  * @cfg {string} dayFormat default 'd'
29822  * @cfg {string} monthFormat default 'm'
29823  * @cfg {string} yearFormat default 'Y'
29824  * @cfg {Number} labellg set the width of label (1-12)
29825  * @cfg {Number} labelmd set the width of label (1-12)
29826  * @cfg {Number} labelsm set the width of label (1-12)
29827  * @cfg {Number} labelxs set the width of label (1-12)
29828
29829  *     
29830  * @constructor
29831  * Create a new DateSplitField
29832  * @param {Object} config The config object
29833  */
29834
29835 Roo.bootstrap.DateSplitField = function(config){
29836     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29837     
29838     this.addEvents({
29839         // raw events
29840          /**
29841          * @event years
29842          * getting the data of years
29843          * @param {Roo.bootstrap.DateSplitField} this
29844          * @param {Object} years
29845          */
29846         "years" : true,
29847         /**
29848          * @event days
29849          * getting the data of days
29850          * @param {Roo.bootstrap.DateSplitField} this
29851          * @param {Object} days
29852          */
29853         "days" : true,
29854         /**
29855          * @event invalid
29856          * Fires after the field has been marked as invalid.
29857          * @param {Roo.form.Field} this
29858          * @param {String} msg The validation message
29859          */
29860         invalid : true,
29861        /**
29862          * @event valid
29863          * Fires after the field has been validated with no errors.
29864          * @param {Roo.form.Field} this
29865          */
29866         valid : true
29867     });
29868 };
29869
29870 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29871     
29872     fieldLabel : '',
29873     labelAlign : 'top',
29874     labelWidth : 3,
29875     dayAllowBlank : false,
29876     monthAllowBlank : false,
29877     yearAllowBlank : false,
29878     dayPlaceholder : '',
29879     monthPlaceholder : '',
29880     yearPlaceholder : '',
29881     dayFormat : 'd',
29882     monthFormat : 'm',
29883     yearFormat : 'Y',
29884     isFormField : true,
29885     labellg : 0,
29886     labelmd : 0,
29887     labelsm : 0,
29888     labelxs : 0,
29889     
29890     getAutoCreate : function()
29891     {
29892         var cfg = {
29893             tag : 'div',
29894             cls : 'row roo-date-split-field-group',
29895             cn : [
29896                 {
29897                     tag : 'input',
29898                     type : 'hidden',
29899                     cls : 'form-hidden-field roo-date-split-field-group-value',
29900                     name : this.name
29901                 }
29902             ]
29903         };
29904         
29905         var labelCls = 'col-md-12';
29906         var contentCls = 'col-md-4';
29907         
29908         if(this.fieldLabel){
29909             
29910             var label = {
29911                 tag : 'div',
29912                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29913                 cn : [
29914                     {
29915                         tag : 'label',
29916                         html : this.fieldLabel
29917                     }
29918                 ]
29919             };
29920             
29921             if(this.labelAlign == 'left'){
29922             
29923                 if(this.labelWidth > 12){
29924                     label.style = "width: " + this.labelWidth + 'px';
29925                 }
29926
29927                 if(this.labelWidth < 13 && this.labelmd == 0){
29928                     this.labelmd = this.labelWidth;
29929                 }
29930
29931                 if(this.labellg > 0){
29932                     labelCls = ' col-lg-' + this.labellg;
29933                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29934                 }
29935
29936                 if(this.labelmd > 0){
29937                     labelCls = ' col-md-' + this.labelmd;
29938                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29939                 }
29940
29941                 if(this.labelsm > 0){
29942                     labelCls = ' col-sm-' + this.labelsm;
29943                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29944                 }
29945
29946                 if(this.labelxs > 0){
29947                     labelCls = ' col-xs-' + this.labelxs;
29948                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29949                 }
29950             }
29951             
29952             label.cls += ' ' + labelCls;
29953             
29954             cfg.cn.push(label);
29955         }
29956         
29957         Roo.each(['day', 'month', 'year'], function(t){
29958             cfg.cn.push({
29959                 tag : 'div',
29960                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29961             });
29962         }, this);
29963         
29964         return cfg;
29965     },
29966     
29967     inputEl: function ()
29968     {
29969         return this.el.select('.roo-date-split-field-group-value', true).first();
29970     },
29971     
29972     onRender : function(ct, position) 
29973     {
29974         var _this = this;
29975         
29976         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29977         
29978         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29979         
29980         this.dayField = new Roo.bootstrap.ComboBox({
29981             allowBlank : this.dayAllowBlank,
29982             alwaysQuery : true,
29983             displayField : 'value',
29984             editable : false,
29985             fieldLabel : '',
29986             forceSelection : true,
29987             mode : 'local',
29988             placeholder : this.dayPlaceholder,
29989             selectOnFocus : true,
29990             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29991             triggerAction : 'all',
29992             typeAhead : true,
29993             valueField : 'value',
29994             store : new Roo.data.SimpleStore({
29995                 data : (function() {    
29996                     var days = [];
29997                     _this.fireEvent('days', _this, days);
29998                     return days;
29999                 })(),
30000                 fields : [ 'value' ]
30001             }),
30002             listeners : {
30003                 select : function (_self, record, index)
30004                 {
30005                     _this.setValue(_this.getValue());
30006                 }
30007             }
30008         });
30009
30010         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30011         
30012         this.monthField = new Roo.bootstrap.MonthField({
30013             after : '<i class=\"fa fa-calendar\"></i>',
30014             allowBlank : this.monthAllowBlank,
30015             placeholder : this.monthPlaceholder,
30016             readOnly : true,
30017             listeners : {
30018                 render : function (_self)
30019                 {
30020                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30021                         e.preventDefault();
30022                         _self.focus();
30023                     });
30024                 },
30025                 select : function (_self, oldvalue, newvalue)
30026                 {
30027                     _this.setValue(_this.getValue());
30028                 }
30029             }
30030         });
30031         
30032         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30033         
30034         this.yearField = new Roo.bootstrap.ComboBox({
30035             allowBlank : this.yearAllowBlank,
30036             alwaysQuery : true,
30037             displayField : 'value',
30038             editable : false,
30039             fieldLabel : '',
30040             forceSelection : true,
30041             mode : 'local',
30042             placeholder : this.yearPlaceholder,
30043             selectOnFocus : true,
30044             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30045             triggerAction : 'all',
30046             typeAhead : true,
30047             valueField : 'value',
30048             store : new Roo.data.SimpleStore({
30049                 data : (function() {
30050                     var years = [];
30051                     _this.fireEvent('years', _this, years);
30052                     return years;
30053                 })(),
30054                 fields : [ 'value' ]
30055             }),
30056             listeners : {
30057                 select : function (_self, record, index)
30058                 {
30059                     _this.setValue(_this.getValue());
30060                 }
30061             }
30062         });
30063
30064         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30065     },
30066     
30067     setValue : function(v, format)
30068     {
30069         this.inputEl.dom.value = v;
30070         
30071         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30072         
30073         var d = Date.parseDate(v, f);
30074         
30075         if(!d){
30076             this.validate();
30077             return;
30078         }
30079         
30080         this.setDay(d.format(this.dayFormat));
30081         this.setMonth(d.format(this.monthFormat));
30082         this.setYear(d.format(this.yearFormat));
30083         
30084         this.validate();
30085         
30086         return;
30087     },
30088     
30089     setDay : function(v)
30090     {
30091         this.dayField.setValue(v);
30092         this.inputEl.dom.value = this.getValue();
30093         this.validate();
30094         return;
30095     },
30096     
30097     setMonth : function(v)
30098     {
30099         this.monthField.setValue(v, true);
30100         this.inputEl.dom.value = this.getValue();
30101         this.validate();
30102         return;
30103     },
30104     
30105     setYear : function(v)
30106     {
30107         this.yearField.setValue(v);
30108         this.inputEl.dom.value = this.getValue();
30109         this.validate();
30110         return;
30111     },
30112     
30113     getDay : function()
30114     {
30115         return this.dayField.getValue();
30116     },
30117     
30118     getMonth : function()
30119     {
30120         return this.monthField.getValue();
30121     },
30122     
30123     getYear : function()
30124     {
30125         return this.yearField.getValue();
30126     },
30127     
30128     getValue : function()
30129     {
30130         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30131         
30132         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30133         
30134         return date;
30135     },
30136     
30137     reset : function()
30138     {
30139         this.setDay('');
30140         this.setMonth('');
30141         this.setYear('');
30142         this.inputEl.dom.value = '';
30143         this.validate();
30144         return;
30145     },
30146     
30147     validate : function()
30148     {
30149         var d = this.dayField.validate();
30150         var m = this.monthField.validate();
30151         var y = this.yearField.validate();
30152         
30153         var valid = true;
30154         
30155         if(
30156                 (!this.dayAllowBlank && !d) ||
30157                 (!this.monthAllowBlank && !m) ||
30158                 (!this.yearAllowBlank && !y)
30159         ){
30160             valid = false;
30161         }
30162         
30163         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30164             return valid;
30165         }
30166         
30167         if(valid){
30168             this.markValid();
30169             return valid;
30170         }
30171         
30172         this.markInvalid();
30173         
30174         return valid;
30175     },
30176     
30177     markValid : function()
30178     {
30179         
30180         var label = this.el.select('label', true).first();
30181         var icon = this.el.select('i.fa-star', true).first();
30182
30183         if(label && icon){
30184             icon.remove();
30185         }
30186         
30187         this.fireEvent('valid', this);
30188     },
30189     
30190      /**
30191      * Mark this field as invalid
30192      * @param {String} msg The validation message
30193      */
30194     markInvalid : function(msg)
30195     {
30196         
30197         var label = this.el.select('label', true).first();
30198         var icon = this.el.select('i.fa-star', true).first();
30199
30200         if(label && !icon){
30201             this.el.select('.roo-date-split-field-label', true).createChild({
30202                 tag : 'i',
30203                 cls : 'text-danger fa fa-lg fa-star',
30204                 tooltip : 'This field is required',
30205                 style : 'margin-right:5px;'
30206             }, label, true);
30207         }
30208         
30209         this.fireEvent('invalid', this, msg);
30210     },
30211     
30212     clearInvalid : function()
30213     {
30214         var label = this.el.select('label', true).first();
30215         var icon = this.el.select('i.fa-star', true).first();
30216
30217         if(label && icon){
30218             icon.remove();
30219         }
30220         
30221         this.fireEvent('valid', this);
30222     },
30223     
30224     getName: function()
30225     {
30226         return this.name;
30227     }
30228     
30229 });
30230
30231  /**
30232  *
30233  * This is based on 
30234  * http://masonry.desandro.com
30235  *
30236  * The idea is to render all the bricks based on vertical width...
30237  *
30238  * The original code extends 'outlayer' - we might need to use that....
30239  * 
30240  */
30241
30242
30243 /**
30244  * @class Roo.bootstrap.LayoutMasonry
30245  * @extends Roo.bootstrap.Component
30246  * Bootstrap Layout Masonry class
30247  * 
30248  * @constructor
30249  * Create a new Element
30250  * @param {Object} config The config object
30251  */
30252
30253 Roo.bootstrap.LayoutMasonry = function(config){
30254     
30255     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30256     
30257     this.bricks = [];
30258     
30259     Roo.bootstrap.LayoutMasonry.register(this);
30260     
30261     this.addEvents({
30262         // raw events
30263         /**
30264          * @event layout
30265          * Fire after layout the items
30266          * @param {Roo.bootstrap.LayoutMasonry} this
30267          * @param {Roo.EventObject} e
30268          */
30269         "layout" : true
30270     });
30271     
30272 };
30273
30274 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30275     
30276     /**
30277      * @cfg {Boolean} isLayoutInstant = no animation?
30278      */   
30279     isLayoutInstant : false, // needed?
30280    
30281     /**
30282      * @cfg {Number} boxWidth  width of the columns
30283      */   
30284     boxWidth : 450,
30285     
30286       /**
30287      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30288      */   
30289     boxHeight : 0,
30290     
30291     /**
30292      * @cfg {Number} padWidth padding below box..
30293      */   
30294     padWidth : 10, 
30295     
30296     /**
30297      * @cfg {Number} gutter gutter width..
30298      */   
30299     gutter : 10,
30300     
30301      /**
30302      * @cfg {Number} maxCols maximum number of columns
30303      */   
30304     
30305     maxCols: 0,
30306     
30307     /**
30308      * @cfg {Boolean} isAutoInitial defalut true
30309      */   
30310     isAutoInitial : true, 
30311     
30312     containerWidth: 0,
30313     
30314     /**
30315      * @cfg {Boolean} isHorizontal defalut false
30316      */   
30317     isHorizontal : false, 
30318
30319     currentSize : null,
30320     
30321     tag: 'div',
30322     
30323     cls: '',
30324     
30325     bricks: null, //CompositeElement
30326     
30327     cols : 1,
30328     
30329     _isLayoutInited : false,
30330     
30331 //    isAlternative : false, // only use for vertical layout...
30332     
30333     /**
30334      * @cfg {Number} alternativePadWidth padding below box..
30335      */   
30336     alternativePadWidth : 50,
30337     
30338     selectedBrick : [],
30339     
30340     getAutoCreate : function(){
30341         
30342         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30343         
30344         var cfg = {
30345             tag: this.tag,
30346             cls: 'blog-masonary-wrapper ' + this.cls,
30347             cn : {
30348                 cls : 'mas-boxes masonary'
30349             }
30350         };
30351         
30352         return cfg;
30353     },
30354     
30355     getChildContainer: function( )
30356     {
30357         if (this.boxesEl) {
30358             return this.boxesEl;
30359         }
30360         
30361         this.boxesEl = this.el.select('.mas-boxes').first();
30362         
30363         return this.boxesEl;
30364     },
30365     
30366     
30367     initEvents : function()
30368     {
30369         var _this = this;
30370         
30371         if(this.isAutoInitial){
30372             Roo.log('hook children rendered');
30373             this.on('childrenrendered', function() {
30374                 Roo.log('children rendered');
30375                 _this.initial();
30376             } ,this);
30377         }
30378     },
30379     
30380     initial : function()
30381     {
30382         this.selectedBrick = [];
30383         
30384         this.currentSize = this.el.getBox(true);
30385         
30386         Roo.EventManager.onWindowResize(this.resize, this); 
30387
30388         if(!this.isAutoInitial){
30389             this.layout();
30390             return;
30391         }
30392         
30393         this.layout();
30394         
30395         return;
30396         //this.layout.defer(500,this);
30397         
30398     },
30399     
30400     resize : function()
30401     {
30402         var cs = this.el.getBox(true);
30403         
30404         if (
30405                 this.currentSize.width == cs.width && 
30406                 this.currentSize.x == cs.x && 
30407                 this.currentSize.height == cs.height && 
30408                 this.currentSize.y == cs.y 
30409         ) {
30410             Roo.log("no change in with or X or Y");
30411             return;
30412         }
30413         
30414         this.currentSize = cs;
30415         
30416         this.layout();
30417         
30418     },
30419     
30420     layout : function()
30421     {   
30422         this._resetLayout();
30423         
30424         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30425         
30426         this.layoutItems( isInstant );
30427       
30428         this._isLayoutInited = true;
30429         
30430         this.fireEvent('layout', this);
30431         
30432     },
30433     
30434     _resetLayout : function()
30435     {
30436         if(this.isHorizontal){
30437             this.horizontalMeasureColumns();
30438             return;
30439         }
30440         
30441         this.verticalMeasureColumns();
30442         
30443     },
30444     
30445     verticalMeasureColumns : function()
30446     {
30447         this.getContainerWidth();
30448         
30449 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30450 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30451 //            return;
30452 //        }
30453         
30454         var boxWidth = this.boxWidth + this.padWidth;
30455         
30456         if(this.containerWidth < this.boxWidth){
30457             boxWidth = this.containerWidth
30458         }
30459         
30460         var containerWidth = this.containerWidth;
30461         
30462         var cols = Math.floor(containerWidth / boxWidth);
30463         
30464         this.cols = Math.max( cols, 1 );
30465         
30466         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30467         
30468         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30469         
30470         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30471         
30472         this.colWidth = boxWidth + avail - this.padWidth;
30473         
30474         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30475         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30476     },
30477     
30478     horizontalMeasureColumns : function()
30479     {
30480         this.getContainerWidth();
30481         
30482         var boxWidth = this.boxWidth;
30483         
30484         if(this.containerWidth < boxWidth){
30485             boxWidth = this.containerWidth;
30486         }
30487         
30488         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30489         
30490         this.el.setHeight(boxWidth);
30491         
30492     },
30493     
30494     getContainerWidth : function()
30495     {
30496         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30497     },
30498     
30499     layoutItems : function( isInstant )
30500     {
30501         Roo.log(this.bricks);
30502         
30503         var items = Roo.apply([], this.bricks);
30504         
30505         if(this.isHorizontal){
30506             this._horizontalLayoutItems( items , isInstant );
30507             return;
30508         }
30509         
30510 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30511 //            this._verticalAlternativeLayoutItems( items , isInstant );
30512 //            return;
30513 //        }
30514         
30515         this._verticalLayoutItems( items , isInstant );
30516         
30517     },
30518     
30519     _verticalLayoutItems : function ( items , isInstant)
30520     {
30521         if ( !items || !items.length ) {
30522             return;
30523         }
30524         
30525         var standard = [
30526             ['xs', 'xs', 'xs', 'tall'],
30527             ['xs', 'xs', 'tall'],
30528             ['xs', 'xs', 'sm'],
30529             ['xs', 'xs', 'xs'],
30530             ['xs', 'tall'],
30531             ['xs', 'sm'],
30532             ['xs', 'xs'],
30533             ['xs'],
30534             
30535             ['sm', 'xs', 'xs'],
30536             ['sm', 'xs'],
30537             ['sm'],
30538             
30539             ['tall', 'xs', 'xs', 'xs'],
30540             ['tall', 'xs', 'xs'],
30541             ['tall', 'xs'],
30542             ['tall']
30543             
30544         ];
30545         
30546         var queue = [];
30547         
30548         var boxes = [];
30549         
30550         var box = [];
30551         
30552         Roo.each(items, function(item, k){
30553             
30554             switch (item.size) {
30555                 // these layouts take up a full box,
30556                 case 'md' :
30557                 case 'md-left' :
30558                 case 'md-right' :
30559                 case 'wide' :
30560                     
30561                     if(box.length){
30562                         boxes.push(box);
30563                         box = [];
30564                     }
30565                     
30566                     boxes.push([item]);
30567                     
30568                     break;
30569                     
30570                 case 'xs' :
30571                 case 'sm' :
30572                 case 'tall' :
30573                     
30574                     box.push(item);
30575                     
30576                     break;
30577                 default :
30578                     break;
30579                     
30580             }
30581             
30582         }, this);
30583         
30584         if(box.length){
30585             boxes.push(box);
30586             box = [];
30587         }
30588         
30589         var filterPattern = function(box, length)
30590         {
30591             if(!box.length){
30592                 return;
30593             }
30594             
30595             var match = false;
30596             
30597             var pattern = box.slice(0, length);
30598             
30599             var format = [];
30600             
30601             Roo.each(pattern, function(i){
30602                 format.push(i.size);
30603             }, this);
30604             
30605             Roo.each(standard, function(s){
30606                 
30607                 if(String(s) != String(format)){
30608                     return;
30609                 }
30610                 
30611                 match = true;
30612                 return false;
30613                 
30614             }, this);
30615             
30616             if(!match && length == 1){
30617                 return;
30618             }
30619             
30620             if(!match){
30621                 filterPattern(box, length - 1);
30622                 return;
30623             }
30624                 
30625             queue.push(pattern);
30626
30627             box = box.slice(length, box.length);
30628
30629             filterPattern(box, 4);
30630
30631             return;
30632             
30633         }
30634         
30635         Roo.each(boxes, function(box, k){
30636             
30637             if(!box.length){
30638                 return;
30639             }
30640             
30641             if(box.length == 1){
30642                 queue.push(box);
30643                 return;
30644             }
30645             
30646             filterPattern(box, 4);
30647             
30648         }, this);
30649         
30650         this._processVerticalLayoutQueue( queue, isInstant );
30651         
30652     },
30653     
30654 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30655 //    {
30656 //        if ( !items || !items.length ) {
30657 //            return;
30658 //        }
30659 //
30660 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30661 //        
30662 //    },
30663     
30664     _horizontalLayoutItems : function ( items , isInstant)
30665     {
30666         if ( !items || !items.length || items.length < 3) {
30667             return;
30668         }
30669         
30670         items.reverse();
30671         
30672         var eItems = items.slice(0, 3);
30673         
30674         items = items.slice(3, items.length);
30675         
30676         var standard = [
30677             ['xs', 'xs', 'xs', 'wide'],
30678             ['xs', 'xs', 'wide'],
30679             ['xs', 'xs', 'sm'],
30680             ['xs', 'xs', 'xs'],
30681             ['xs', 'wide'],
30682             ['xs', 'sm'],
30683             ['xs', 'xs'],
30684             ['xs'],
30685             
30686             ['sm', 'xs', 'xs'],
30687             ['sm', 'xs'],
30688             ['sm'],
30689             
30690             ['wide', 'xs', 'xs', 'xs'],
30691             ['wide', 'xs', 'xs'],
30692             ['wide', 'xs'],
30693             ['wide'],
30694             
30695             ['wide-thin']
30696         ];
30697         
30698         var queue = [];
30699         
30700         var boxes = [];
30701         
30702         var box = [];
30703         
30704         Roo.each(items, function(item, k){
30705             
30706             switch (item.size) {
30707                 case 'md' :
30708                 case 'md-left' :
30709                 case 'md-right' :
30710                 case 'tall' :
30711                     
30712                     if(box.length){
30713                         boxes.push(box);
30714                         box = [];
30715                     }
30716                     
30717                     boxes.push([item]);
30718                     
30719                     break;
30720                     
30721                 case 'xs' :
30722                 case 'sm' :
30723                 case 'wide' :
30724                 case 'wide-thin' :
30725                     
30726                     box.push(item);
30727                     
30728                     break;
30729                 default :
30730                     break;
30731                     
30732             }
30733             
30734         }, this);
30735         
30736         if(box.length){
30737             boxes.push(box);
30738             box = [];
30739         }
30740         
30741         var filterPattern = function(box, length)
30742         {
30743             if(!box.length){
30744                 return;
30745             }
30746             
30747             var match = false;
30748             
30749             var pattern = box.slice(0, length);
30750             
30751             var format = [];
30752             
30753             Roo.each(pattern, function(i){
30754                 format.push(i.size);
30755             }, this);
30756             
30757             Roo.each(standard, function(s){
30758                 
30759                 if(String(s) != String(format)){
30760                     return;
30761                 }
30762                 
30763                 match = true;
30764                 return false;
30765                 
30766             }, this);
30767             
30768             if(!match && length == 1){
30769                 return;
30770             }
30771             
30772             if(!match){
30773                 filterPattern(box, length - 1);
30774                 return;
30775             }
30776                 
30777             queue.push(pattern);
30778
30779             box = box.slice(length, box.length);
30780
30781             filterPattern(box, 4);
30782
30783             return;
30784             
30785         }
30786         
30787         Roo.each(boxes, function(box, k){
30788             
30789             if(!box.length){
30790                 return;
30791             }
30792             
30793             if(box.length == 1){
30794                 queue.push(box);
30795                 return;
30796             }
30797             
30798             filterPattern(box, 4);
30799             
30800         }, this);
30801         
30802         
30803         var prune = [];
30804         
30805         var pos = this.el.getBox(true);
30806         
30807         var minX = pos.x;
30808         
30809         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30810         
30811         var hit_end = false;
30812         
30813         Roo.each(queue, function(box){
30814             
30815             if(hit_end){
30816                 
30817                 Roo.each(box, function(b){
30818                 
30819                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30820                     b.el.hide();
30821
30822                 }, this);
30823
30824                 return;
30825             }
30826             
30827             var mx = 0;
30828             
30829             Roo.each(box, function(b){
30830                 
30831                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30832                 b.el.show();
30833
30834                 mx = Math.max(mx, b.x);
30835                 
30836             }, this);
30837             
30838             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30839             
30840             if(maxX < minX){
30841                 
30842                 Roo.each(box, function(b){
30843                 
30844                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30845                     b.el.hide();
30846                     
30847                 }, this);
30848                 
30849                 hit_end = true;
30850                 
30851                 return;
30852             }
30853             
30854             prune.push(box);
30855             
30856         }, this);
30857         
30858         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30859     },
30860     
30861     /** Sets position of item in DOM
30862     * @param {Element} item
30863     * @param {Number} x - horizontal position
30864     * @param {Number} y - vertical position
30865     * @param {Boolean} isInstant - disables transitions
30866     */
30867     _processVerticalLayoutQueue : function( queue, isInstant )
30868     {
30869         var pos = this.el.getBox(true);
30870         var x = pos.x;
30871         var y = pos.y;
30872         var maxY = [];
30873         
30874         for (var i = 0; i < this.cols; i++){
30875             maxY[i] = pos.y;
30876         }
30877         
30878         Roo.each(queue, function(box, k){
30879             
30880             var col = k % this.cols;
30881             
30882             Roo.each(box, function(b,kk){
30883                 
30884                 b.el.position('absolute');
30885                 
30886                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30887                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30888                 
30889                 if(b.size == 'md-left' || b.size == 'md-right'){
30890                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30891                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30892                 }
30893                 
30894                 b.el.setWidth(width);
30895                 b.el.setHeight(height);
30896                 // iframe?
30897                 b.el.select('iframe',true).setSize(width,height);
30898                 
30899             }, this);
30900             
30901             for (var i = 0; i < this.cols; i++){
30902                 
30903                 if(maxY[i] < maxY[col]){
30904                     col = i;
30905                     continue;
30906                 }
30907                 
30908                 col = Math.min(col, i);
30909                 
30910             }
30911             
30912             x = pos.x + col * (this.colWidth + this.padWidth);
30913             
30914             y = maxY[col];
30915             
30916             var positions = [];
30917             
30918             switch (box.length){
30919                 case 1 :
30920                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30921                     break;
30922                 case 2 :
30923                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30924                     break;
30925                 case 3 :
30926                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30927                     break;
30928                 case 4 :
30929                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30930                     break;
30931                 default :
30932                     break;
30933             }
30934             
30935             Roo.each(box, function(b,kk){
30936                 
30937                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30938                 
30939                 var sz = b.el.getSize();
30940                 
30941                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30942                 
30943             }, this);
30944             
30945         }, this);
30946         
30947         var mY = 0;
30948         
30949         for (var i = 0; i < this.cols; i++){
30950             mY = Math.max(mY, maxY[i]);
30951         }
30952         
30953         this.el.setHeight(mY - pos.y);
30954         
30955     },
30956     
30957 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30958 //    {
30959 //        var pos = this.el.getBox(true);
30960 //        var x = pos.x;
30961 //        var y = pos.y;
30962 //        var maxX = pos.right;
30963 //        
30964 //        var maxHeight = 0;
30965 //        
30966 //        Roo.each(items, function(item, k){
30967 //            
30968 //            var c = k % 2;
30969 //            
30970 //            item.el.position('absolute');
30971 //                
30972 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30973 //
30974 //            item.el.setWidth(width);
30975 //
30976 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30977 //
30978 //            item.el.setHeight(height);
30979 //            
30980 //            if(c == 0){
30981 //                item.el.setXY([x, y], isInstant ? false : true);
30982 //            } else {
30983 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30984 //            }
30985 //            
30986 //            y = y + height + this.alternativePadWidth;
30987 //            
30988 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30989 //            
30990 //        }, this);
30991 //        
30992 //        this.el.setHeight(maxHeight);
30993 //        
30994 //    },
30995     
30996     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30997     {
30998         var pos = this.el.getBox(true);
30999         
31000         var minX = pos.x;
31001         var minY = pos.y;
31002         
31003         var maxX = pos.right;
31004         
31005         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31006         
31007         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31008         
31009         Roo.each(queue, function(box, k){
31010             
31011             Roo.each(box, function(b, kk){
31012                 
31013                 b.el.position('absolute');
31014                 
31015                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31016                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31017                 
31018                 if(b.size == 'md-left' || b.size == 'md-right'){
31019                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31020                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31021                 }
31022                 
31023                 b.el.setWidth(width);
31024                 b.el.setHeight(height);
31025                 
31026             }, this);
31027             
31028             if(!box.length){
31029                 return;
31030             }
31031             
31032             var positions = [];
31033             
31034             switch (box.length){
31035                 case 1 :
31036                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31037                     break;
31038                 case 2 :
31039                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31040                     break;
31041                 case 3 :
31042                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31043                     break;
31044                 case 4 :
31045                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31046                     break;
31047                 default :
31048                     break;
31049             }
31050             
31051             Roo.each(box, function(b,kk){
31052                 
31053                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31054                 
31055                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31056                 
31057             }, this);
31058             
31059         }, this);
31060         
31061     },
31062     
31063     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31064     {
31065         Roo.each(eItems, function(b,k){
31066             
31067             b.size = (k == 0) ? 'sm' : 'xs';
31068             b.x = (k == 0) ? 2 : 1;
31069             b.y = (k == 0) ? 2 : 1;
31070             
31071             b.el.position('absolute');
31072             
31073             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31074                 
31075             b.el.setWidth(width);
31076             
31077             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31078             
31079             b.el.setHeight(height);
31080             
31081         }, this);
31082
31083         var positions = [];
31084         
31085         positions.push({
31086             x : maxX - this.unitWidth * 2 - this.gutter,
31087             y : minY
31088         });
31089         
31090         positions.push({
31091             x : maxX - this.unitWidth,
31092             y : minY + (this.unitWidth + this.gutter) * 2
31093         });
31094         
31095         positions.push({
31096             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31097             y : minY
31098         });
31099         
31100         Roo.each(eItems, function(b,k){
31101             
31102             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31103
31104         }, this);
31105         
31106     },
31107     
31108     getVerticalOneBoxColPositions : function(x, y, box)
31109     {
31110         var pos = [];
31111         
31112         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31113         
31114         if(box[0].size == 'md-left'){
31115             rand = 0;
31116         }
31117         
31118         if(box[0].size == 'md-right'){
31119             rand = 1;
31120         }
31121         
31122         pos.push({
31123             x : x + (this.unitWidth + this.gutter) * rand,
31124             y : y
31125         });
31126         
31127         return pos;
31128     },
31129     
31130     getVerticalTwoBoxColPositions : function(x, y, box)
31131     {
31132         var pos = [];
31133         
31134         if(box[0].size == 'xs'){
31135             
31136             pos.push({
31137                 x : x,
31138                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31139             });
31140
31141             pos.push({
31142                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31143                 y : y
31144             });
31145             
31146             return pos;
31147             
31148         }
31149         
31150         pos.push({
31151             x : x,
31152             y : y
31153         });
31154
31155         pos.push({
31156             x : x + (this.unitWidth + this.gutter) * 2,
31157             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31158         });
31159         
31160         return pos;
31161         
31162     },
31163     
31164     getVerticalThreeBoxColPositions : function(x, y, box)
31165     {
31166         var pos = [];
31167         
31168         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31169             
31170             pos.push({
31171                 x : x,
31172                 y : y
31173             });
31174
31175             pos.push({
31176                 x : x + (this.unitWidth + this.gutter) * 1,
31177                 y : y
31178             });
31179             
31180             pos.push({
31181                 x : x + (this.unitWidth + this.gutter) * 2,
31182                 y : y
31183             });
31184             
31185             return pos;
31186             
31187         }
31188         
31189         if(box[0].size == 'xs' && box[1].size == 'xs'){
31190             
31191             pos.push({
31192                 x : x,
31193                 y : y
31194             });
31195
31196             pos.push({
31197                 x : x,
31198                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31199             });
31200             
31201             pos.push({
31202                 x : x + (this.unitWidth + this.gutter) * 1,
31203                 y : y
31204             });
31205             
31206             return pos;
31207             
31208         }
31209         
31210         pos.push({
31211             x : x,
31212             y : y
31213         });
31214
31215         pos.push({
31216             x : x + (this.unitWidth + this.gutter) * 2,
31217             y : y
31218         });
31219
31220         pos.push({
31221             x : x + (this.unitWidth + this.gutter) * 2,
31222             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31223         });
31224             
31225         return pos;
31226         
31227     },
31228     
31229     getVerticalFourBoxColPositions : function(x, y, box)
31230     {
31231         var pos = [];
31232         
31233         if(box[0].size == 'xs'){
31234             
31235             pos.push({
31236                 x : x,
31237                 y : y
31238             });
31239
31240             pos.push({
31241                 x : x,
31242                 y : y + (this.unitHeight + this.gutter) * 1
31243             });
31244             
31245             pos.push({
31246                 x : x,
31247                 y : y + (this.unitHeight + this.gutter) * 2
31248             });
31249             
31250             pos.push({
31251                 x : x + (this.unitWidth + this.gutter) * 1,
31252                 y : y
31253             });
31254             
31255             return pos;
31256             
31257         }
31258         
31259         pos.push({
31260             x : x,
31261             y : y
31262         });
31263
31264         pos.push({
31265             x : x + (this.unitWidth + this.gutter) * 2,
31266             y : y
31267         });
31268
31269         pos.push({
31270             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31271             y : y + (this.unitHeight + this.gutter) * 1
31272         });
31273
31274         pos.push({
31275             x : x + (this.unitWidth + this.gutter) * 2,
31276             y : y + (this.unitWidth + this.gutter) * 2
31277         });
31278
31279         return pos;
31280         
31281     },
31282     
31283     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31284     {
31285         var pos = [];
31286         
31287         if(box[0].size == 'md-left'){
31288             pos.push({
31289                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31290                 y : minY
31291             });
31292             
31293             return pos;
31294         }
31295         
31296         if(box[0].size == 'md-right'){
31297             pos.push({
31298                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31299                 y : minY + (this.unitWidth + this.gutter) * 1
31300             });
31301             
31302             return pos;
31303         }
31304         
31305         var rand = Math.floor(Math.random() * (4 - box[0].y));
31306         
31307         pos.push({
31308             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31309             y : minY + (this.unitWidth + this.gutter) * rand
31310         });
31311         
31312         return pos;
31313         
31314     },
31315     
31316     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31317     {
31318         var pos = [];
31319         
31320         if(box[0].size == 'xs'){
31321             
31322             pos.push({
31323                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31324                 y : minY
31325             });
31326
31327             pos.push({
31328                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31329                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31330             });
31331             
31332             return pos;
31333             
31334         }
31335         
31336         pos.push({
31337             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31338             y : minY
31339         });
31340
31341         pos.push({
31342             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31343             y : minY + (this.unitWidth + this.gutter) * 2
31344         });
31345         
31346         return pos;
31347         
31348     },
31349     
31350     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31351     {
31352         var pos = [];
31353         
31354         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31355             
31356             pos.push({
31357                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31358                 y : minY
31359             });
31360
31361             pos.push({
31362                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31363                 y : minY + (this.unitWidth + this.gutter) * 1
31364             });
31365             
31366             pos.push({
31367                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31368                 y : minY + (this.unitWidth + this.gutter) * 2
31369             });
31370             
31371             return pos;
31372             
31373         }
31374         
31375         if(box[0].size == 'xs' && box[1].size == 'xs'){
31376             
31377             pos.push({
31378                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31379                 y : minY
31380             });
31381
31382             pos.push({
31383                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31384                 y : minY
31385             });
31386             
31387             pos.push({
31388                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31389                 y : minY + (this.unitWidth + this.gutter) * 1
31390             });
31391             
31392             return pos;
31393             
31394         }
31395         
31396         pos.push({
31397             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31398             y : minY
31399         });
31400
31401         pos.push({
31402             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31403             y : minY + (this.unitWidth + this.gutter) * 2
31404         });
31405
31406         pos.push({
31407             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31408             y : minY + (this.unitWidth + this.gutter) * 2
31409         });
31410             
31411         return pos;
31412         
31413     },
31414     
31415     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31416     {
31417         var pos = [];
31418         
31419         if(box[0].size == 'xs'){
31420             
31421             pos.push({
31422                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31423                 y : minY
31424             });
31425
31426             pos.push({
31427                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31428                 y : minY
31429             });
31430             
31431             pos.push({
31432                 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),
31433                 y : minY
31434             });
31435             
31436             pos.push({
31437                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31438                 y : minY + (this.unitWidth + this.gutter) * 1
31439             });
31440             
31441             return pos;
31442             
31443         }
31444         
31445         pos.push({
31446             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31447             y : minY
31448         });
31449         
31450         pos.push({
31451             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31452             y : minY + (this.unitWidth + this.gutter) * 2
31453         });
31454         
31455         pos.push({
31456             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31457             y : minY + (this.unitWidth + this.gutter) * 2
31458         });
31459         
31460         pos.push({
31461             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),
31462             y : minY + (this.unitWidth + this.gutter) * 2
31463         });
31464
31465         return pos;
31466         
31467     },
31468     
31469     /**
31470     * remove a Masonry Brick
31471     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31472     */
31473     removeBrick : function(brick_id)
31474     {
31475         if (!brick_id) {
31476             return;
31477         }
31478         
31479         for (var i = 0; i<this.bricks.length; i++) {
31480             if (this.bricks[i].id == brick_id) {
31481                 this.bricks.splice(i,1);
31482                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31483                 this.initial();
31484             }
31485         }
31486     },
31487     
31488     /**
31489     * adds a Masonry Brick
31490     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31491     */
31492     addBrick : function(cfg)
31493     {
31494         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31495         //this.register(cn);
31496         cn.parentId = this.id;
31497         cn.onRender(this.el, null);
31498         return cn;
31499     },
31500     
31501     /**
31502     * register a Masonry Brick
31503     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31504     */
31505     
31506     register : function(brick)
31507     {
31508         this.bricks.push(brick);
31509         brick.masonryId = this.id;
31510     },
31511     
31512     /**
31513     * clear all the Masonry Brick
31514     */
31515     clearAll : function()
31516     {
31517         this.bricks = [];
31518         //this.getChildContainer().dom.innerHTML = "";
31519         this.el.dom.innerHTML = '';
31520     },
31521     
31522     getSelected : function()
31523     {
31524         if (!this.selectedBrick) {
31525             return false;
31526         }
31527         
31528         return this.selectedBrick;
31529     }
31530 });
31531
31532 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31533     
31534     groups: {},
31535      /**
31536     * register a Masonry Layout
31537     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31538     */
31539     
31540     register : function(layout)
31541     {
31542         this.groups[layout.id] = layout;
31543     },
31544     /**
31545     * fetch a  Masonry Layout based on the masonry layout ID
31546     * @param {string} the masonry layout to add
31547     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31548     */
31549     
31550     get: function(layout_id) {
31551         if (typeof(this.groups[layout_id]) == 'undefined') {
31552             return false;
31553         }
31554         return this.groups[layout_id] ;
31555     }
31556     
31557     
31558     
31559 });
31560
31561  
31562
31563  /**
31564  *
31565  * This is based on 
31566  * http://masonry.desandro.com
31567  *
31568  * The idea is to render all the bricks based on vertical width...
31569  *
31570  * The original code extends 'outlayer' - we might need to use that....
31571  * 
31572  */
31573
31574
31575 /**
31576  * @class Roo.bootstrap.LayoutMasonryAuto
31577  * @extends Roo.bootstrap.Component
31578  * Bootstrap Layout Masonry class
31579  * 
31580  * @constructor
31581  * Create a new Element
31582  * @param {Object} config The config object
31583  */
31584
31585 Roo.bootstrap.LayoutMasonryAuto = function(config){
31586     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31587 };
31588
31589 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31590     
31591       /**
31592      * @cfg {Boolean} isFitWidth  - resize the width..
31593      */   
31594     isFitWidth : false,  // options..
31595     /**
31596      * @cfg {Boolean} isOriginLeft = left align?
31597      */   
31598     isOriginLeft : true,
31599     /**
31600      * @cfg {Boolean} isOriginTop = top align?
31601      */   
31602     isOriginTop : false,
31603     /**
31604      * @cfg {Boolean} isLayoutInstant = no animation?
31605      */   
31606     isLayoutInstant : false, // needed?
31607     /**
31608      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31609      */   
31610     isResizingContainer : true,
31611     /**
31612      * @cfg {Number} columnWidth  width of the columns 
31613      */   
31614     
31615     columnWidth : 0,
31616     
31617     /**
31618      * @cfg {Number} maxCols maximum number of columns
31619      */   
31620     
31621     maxCols: 0,
31622     /**
31623      * @cfg {Number} padHeight padding below box..
31624      */   
31625     
31626     padHeight : 10, 
31627     
31628     /**
31629      * @cfg {Boolean} isAutoInitial defalut true
31630      */   
31631     
31632     isAutoInitial : true, 
31633     
31634     // private?
31635     gutter : 0,
31636     
31637     containerWidth: 0,
31638     initialColumnWidth : 0,
31639     currentSize : null,
31640     
31641     colYs : null, // array.
31642     maxY : 0,
31643     padWidth: 10,
31644     
31645     
31646     tag: 'div',
31647     cls: '',
31648     bricks: null, //CompositeElement
31649     cols : 0, // array?
31650     // element : null, // wrapped now this.el
31651     _isLayoutInited : null, 
31652     
31653     
31654     getAutoCreate : function(){
31655         
31656         var cfg = {
31657             tag: this.tag,
31658             cls: 'blog-masonary-wrapper ' + this.cls,
31659             cn : {
31660                 cls : 'mas-boxes masonary'
31661             }
31662         };
31663         
31664         return cfg;
31665     },
31666     
31667     getChildContainer: function( )
31668     {
31669         if (this.boxesEl) {
31670             return this.boxesEl;
31671         }
31672         
31673         this.boxesEl = this.el.select('.mas-boxes').first();
31674         
31675         return this.boxesEl;
31676     },
31677     
31678     
31679     initEvents : function()
31680     {
31681         var _this = this;
31682         
31683         if(this.isAutoInitial){
31684             Roo.log('hook children rendered');
31685             this.on('childrenrendered', function() {
31686                 Roo.log('children rendered');
31687                 _this.initial();
31688             } ,this);
31689         }
31690         
31691     },
31692     
31693     initial : function()
31694     {
31695         this.reloadItems();
31696
31697         this.currentSize = this.el.getBox(true);
31698
31699         /// was window resize... - let's see if this works..
31700         Roo.EventManager.onWindowResize(this.resize, this); 
31701
31702         if(!this.isAutoInitial){
31703             this.layout();
31704             return;
31705         }
31706         
31707         this.layout.defer(500,this);
31708     },
31709     
31710     reloadItems: function()
31711     {
31712         this.bricks = this.el.select('.masonry-brick', true);
31713         
31714         this.bricks.each(function(b) {
31715             //Roo.log(b.getSize());
31716             if (!b.attr('originalwidth')) {
31717                 b.attr('originalwidth',  b.getSize().width);
31718             }
31719             
31720         });
31721         
31722         Roo.log(this.bricks.elements.length);
31723     },
31724     
31725     resize : function()
31726     {
31727         Roo.log('resize');
31728         var cs = this.el.getBox(true);
31729         
31730         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31731             Roo.log("no change in with or X");
31732             return;
31733         }
31734         this.currentSize = cs;
31735         this.layout();
31736     },
31737     
31738     layout : function()
31739     {
31740          Roo.log('layout');
31741         this._resetLayout();
31742         //this._manageStamps();
31743       
31744         // don't animate first layout
31745         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31746         this.layoutItems( isInstant );
31747       
31748         // flag for initalized
31749         this._isLayoutInited = true;
31750     },
31751     
31752     layoutItems : function( isInstant )
31753     {
31754         //var items = this._getItemsForLayout( this.items );
31755         // original code supports filtering layout items.. we just ignore it..
31756         
31757         this._layoutItems( this.bricks , isInstant );
31758       
31759         this._postLayout();
31760     },
31761     _layoutItems : function ( items , isInstant)
31762     {
31763        //this.fireEvent( 'layout', this, items );
31764     
31765
31766         if ( !items || !items.elements.length ) {
31767           // no items, emit event with empty array
31768             return;
31769         }
31770
31771         var queue = [];
31772         items.each(function(item) {
31773             Roo.log("layout item");
31774             Roo.log(item);
31775             // get x/y object from method
31776             var position = this._getItemLayoutPosition( item );
31777             // enqueue
31778             position.item = item;
31779             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31780             queue.push( position );
31781         }, this);
31782       
31783         this._processLayoutQueue( queue );
31784     },
31785     /** Sets position of item in DOM
31786     * @param {Element} item
31787     * @param {Number} x - horizontal position
31788     * @param {Number} y - vertical position
31789     * @param {Boolean} isInstant - disables transitions
31790     */
31791     _processLayoutQueue : function( queue )
31792     {
31793         for ( var i=0, len = queue.length; i < len; i++ ) {
31794             var obj = queue[i];
31795             obj.item.position('absolute');
31796             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31797         }
31798     },
31799       
31800     
31801     /**
31802     * Any logic you want to do after each layout,
31803     * i.e. size the container
31804     */
31805     _postLayout : function()
31806     {
31807         this.resizeContainer();
31808     },
31809     
31810     resizeContainer : function()
31811     {
31812         if ( !this.isResizingContainer ) {
31813             return;
31814         }
31815         var size = this._getContainerSize();
31816         if ( size ) {
31817             this.el.setSize(size.width,size.height);
31818             this.boxesEl.setSize(size.width,size.height);
31819         }
31820     },
31821     
31822     
31823     
31824     _resetLayout : function()
31825     {
31826         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31827         this.colWidth = this.el.getWidth();
31828         //this.gutter = this.el.getWidth(); 
31829         
31830         this.measureColumns();
31831
31832         // reset column Y
31833         var i = this.cols;
31834         this.colYs = [];
31835         while (i--) {
31836             this.colYs.push( 0 );
31837         }
31838     
31839         this.maxY = 0;
31840     },
31841
31842     measureColumns : function()
31843     {
31844         this.getContainerWidth();
31845       // if columnWidth is 0, default to outerWidth of first item
31846         if ( !this.columnWidth ) {
31847             var firstItem = this.bricks.first();
31848             Roo.log(firstItem);
31849             this.columnWidth  = this.containerWidth;
31850             if (firstItem && firstItem.attr('originalwidth') ) {
31851                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31852             }
31853             // columnWidth fall back to item of first element
31854             Roo.log("set column width?");
31855                         this.initialColumnWidth = this.columnWidth  ;
31856
31857             // if first elem has no width, default to size of container
31858             
31859         }
31860         
31861         
31862         if (this.initialColumnWidth) {
31863             this.columnWidth = this.initialColumnWidth;
31864         }
31865         
31866         
31867             
31868         // column width is fixed at the top - however if container width get's smaller we should
31869         // reduce it...
31870         
31871         // this bit calcs how man columns..
31872             
31873         var columnWidth = this.columnWidth += this.gutter;
31874       
31875         // calculate columns
31876         var containerWidth = this.containerWidth + this.gutter;
31877         
31878         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31879         // fix rounding errors, typically with gutters
31880         var excess = columnWidth - containerWidth % columnWidth;
31881         
31882         
31883         // if overshoot is less than a pixel, round up, otherwise floor it
31884         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31885         cols = Math[ mathMethod ]( cols );
31886         this.cols = Math.max( cols, 1 );
31887         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31888         
31889          // padding positioning..
31890         var totalColWidth = this.cols * this.columnWidth;
31891         var padavail = this.containerWidth - totalColWidth;
31892         // so for 2 columns - we need 3 'pads'
31893         
31894         var padNeeded = (1+this.cols) * this.padWidth;
31895         
31896         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31897         
31898         this.columnWidth += padExtra
31899         //this.padWidth = Math.floor(padavail /  ( this.cols));
31900         
31901         // adjust colum width so that padding is fixed??
31902         
31903         // we have 3 columns ... total = width * 3
31904         // we have X left over... that should be used by 
31905         
31906         //if (this.expandC) {
31907             
31908         //}
31909         
31910         
31911         
31912     },
31913     
31914     getContainerWidth : function()
31915     {
31916        /* // container is parent if fit width
31917         var container = this.isFitWidth ? this.element.parentNode : this.element;
31918         // check that this.size and size are there
31919         // IE8 triggers resize on body size change, so they might not be
31920         
31921         var size = getSize( container );  //FIXME
31922         this.containerWidth = size && size.innerWidth; //FIXME
31923         */
31924          
31925         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31926         
31927     },
31928     
31929     _getItemLayoutPosition : function( item )  // what is item?
31930     {
31931         // we resize the item to our columnWidth..
31932       
31933         item.setWidth(this.columnWidth);
31934         item.autoBoxAdjust  = false;
31935         
31936         var sz = item.getSize();
31937  
31938         // how many columns does this brick span
31939         var remainder = this.containerWidth % this.columnWidth;
31940         
31941         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31942         // round if off by 1 pixel, otherwise use ceil
31943         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31944         colSpan = Math.min( colSpan, this.cols );
31945         
31946         // normally this should be '1' as we dont' currently allow multi width columns..
31947         
31948         var colGroup = this._getColGroup( colSpan );
31949         // get the minimum Y value from the columns
31950         var minimumY = Math.min.apply( Math, colGroup );
31951         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31952         
31953         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31954          
31955         // position the brick
31956         var position = {
31957             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31958             y: this.currentSize.y + minimumY + this.padHeight
31959         };
31960         
31961         Roo.log(position);
31962         // apply setHeight to necessary columns
31963         var setHeight = minimumY + sz.height + this.padHeight;
31964         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31965         
31966         var setSpan = this.cols + 1 - colGroup.length;
31967         for ( var i = 0; i < setSpan; i++ ) {
31968           this.colYs[ shortColIndex + i ] = setHeight ;
31969         }
31970       
31971         return position;
31972     },
31973     
31974     /**
31975      * @param {Number} colSpan - number of columns the element spans
31976      * @returns {Array} colGroup
31977      */
31978     _getColGroup : function( colSpan )
31979     {
31980         if ( colSpan < 2 ) {
31981           // if brick spans only one column, use all the column Ys
31982           return this.colYs;
31983         }
31984       
31985         var colGroup = [];
31986         // how many different places could this brick fit horizontally
31987         var groupCount = this.cols + 1 - colSpan;
31988         // for each group potential horizontal position
31989         for ( var i = 0; i < groupCount; i++ ) {
31990           // make an array of colY values for that one group
31991           var groupColYs = this.colYs.slice( i, i + colSpan );
31992           // and get the max value of the array
31993           colGroup[i] = Math.max.apply( Math, groupColYs );
31994         }
31995         return colGroup;
31996     },
31997     /*
31998     _manageStamp : function( stamp )
31999     {
32000         var stampSize =  stamp.getSize();
32001         var offset = stamp.getBox();
32002         // get the columns that this stamp affects
32003         var firstX = this.isOriginLeft ? offset.x : offset.right;
32004         var lastX = firstX + stampSize.width;
32005         var firstCol = Math.floor( firstX / this.columnWidth );
32006         firstCol = Math.max( 0, firstCol );
32007         
32008         var lastCol = Math.floor( lastX / this.columnWidth );
32009         // lastCol should not go over if multiple of columnWidth #425
32010         lastCol -= lastX % this.columnWidth ? 0 : 1;
32011         lastCol = Math.min( this.cols - 1, lastCol );
32012         
32013         // set colYs to bottom of the stamp
32014         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32015             stampSize.height;
32016             
32017         for ( var i = firstCol; i <= lastCol; i++ ) {
32018           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32019         }
32020     },
32021     */
32022     
32023     _getContainerSize : function()
32024     {
32025         this.maxY = Math.max.apply( Math, this.colYs );
32026         var size = {
32027             height: this.maxY
32028         };
32029       
32030         if ( this.isFitWidth ) {
32031             size.width = this._getContainerFitWidth();
32032         }
32033       
32034         return size;
32035     },
32036     
32037     _getContainerFitWidth : function()
32038     {
32039         var unusedCols = 0;
32040         // count unused columns
32041         var i = this.cols;
32042         while ( --i ) {
32043           if ( this.colYs[i] !== 0 ) {
32044             break;
32045           }
32046           unusedCols++;
32047         }
32048         // fit container to columns that have been used
32049         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32050     },
32051     
32052     needsResizeLayout : function()
32053     {
32054         var previousWidth = this.containerWidth;
32055         this.getContainerWidth();
32056         return previousWidth !== this.containerWidth;
32057     }
32058  
32059 });
32060
32061  
32062
32063  /*
32064  * - LGPL
32065  *
32066  * element
32067  * 
32068  */
32069
32070 /**
32071  * @class Roo.bootstrap.MasonryBrick
32072  * @extends Roo.bootstrap.Component
32073  * Bootstrap MasonryBrick class
32074  * 
32075  * @constructor
32076  * Create a new MasonryBrick
32077  * @param {Object} config The config object
32078  */
32079
32080 Roo.bootstrap.MasonryBrick = function(config){
32081     
32082     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32083     
32084     Roo.bootstrap.MasonryBrick.register(this);
32085     
32086     this.addEvents({
32087         // raw events
32088         /**
32089          * @event click
32090          * When a MasonryBrick is clcik
32091          * @param {Roo.bootstrap.MasonryBrick} this
32092          * @param {Roo.EventObject} e
32093          */
32094         "click" : true
32095     });
32096 };
32097
32098 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32099     
32100     /**
32101      * @cfg {String} title
32102      */   
32103     title : '',
32104     /**
32105      * @cfg {String} html
32106      */   
32107     html : '',
32108     /**
32109      * @cfg {String} bgimage
32110      */   
32111     bgimage : '',
32112     /**
32113      * @cfg {String} videourl
32114      */   
32115     videourl : '',
32116     /**
32117      * @cfg {String} cls
32118      */   
32119     cls : '',
32120     /**
32121      * @cfg {String} href
32122      */   
32123     href : '',
32124     /**
32125      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32126      */   
32127     size : 'xs',
32128     
32129     /**
32130      * @cfg {String} placetitle (center|bottom)
32131      */   
32132     placetitle : '',
32133     
32134     /**
32135      * @cfg {Boolean} isFitContainer defalut true
32136      */   
32137     isFitContainer : true, 
32138     
32139     /**
32140      * @cfg {Boolean} preventDefault defalut false
32141      */   
32142     preventDefault : false, 
32143     
32144     /**
32145      * @cfg {Boolean} inverse defalut false
32146      */   
32147     maskInverse : false, 
32148     
32149     getAutoCreate : function()
32150     {
32151         if(!this.isFitContainer){
32152             return this.getSplitAutoCreate();
32153         }
32154         
32155         var cls = 'masonry-brick masonry-brick-full';
32156         
32157         if(this.href.length){
32158             cls += ' masonry-brick-link';
32159         }
32160         
32161         if(this.bgimage.length){
32162             cls += ' masonry-brick-image';
32163         }
32164         
32165         if(this.maskInverse){
32166             cls += ' mask-inverse';
32167         }
32168         
32169         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32170             cls += ' enable-mask';
32171         }
32172         
32173         if(this.size){
32174             cls += ' masonry-' + this.size + '-brick';
32175         }
32176         
32177         if(this.placetitle.length){
32178             
32179             switch (this.placetitle) {
32180                 case 'center' :
32181                     cls += ' masonry-center-title';
32182                     break;
32183                 case 'bottom' :
32184                     cls += ' masonry-bottom-title';
32185                     break;
32186                 default:
32187                     break;
32188             }
32189             
32190         } else {
32191             if(!this.html.length && !this.bgimage.length){
32192                 cls += ' masonry-center-title';
32193             }
32194
32195             if(!this.html.length && this.bgimage.length){
32196                 cls += ' masonry-bottom-title';
32197             }
32198         }
32199         
32200         if(this.cls){
32201             cls += ' ' + this.cls;
32202         }
32203         
32204         var cfg = {
32205             tag: (this.href.length) ? 'a' : 'div',
32206             cls: cls,
32207             cn: [
32208                 {
32209                     tag: 'div',
32210                     cls: 'masonry-brick-mask'
32211                 },
32212                 {
32213                     tag: 'div',
32214                     cls: 'masonry-brick-paragraph',
32215                     cn: []
32216                 }
32217             ]
32218         };
32219         
32220         if(this.href.length){
32221             cfg.href = this.href;
32222         }
32223         
32224         var cn = cfg.cn[1].cn;
32225         
32226         if(this.title.length){
32227             cn.push({
32228                 tag: 'h4',
32229                 cls: 'masonry-brick-title',
32230                 html: this.title
32231             });
32232         }
32233         
32234         if(this.html.length){
32235             cn.push({
32236                 tag: 'p',
32237                 cls: 'masonry-brick-text',
32238                 html: this.html
32239             });
32240         }
32241         
32242         if (!this.title.length && !this.html.length) {
32243             cfg.cn[1].cls += ' hide';
32244         }
32245         
32246         if(this.bgimage.length){
32247             cfg.cn.push({
32248                 tag: 'img',
32249                 cls: 'masonry-brick-image-view',
32250                 src: this.bgimage
32251             });
32252         }
32253         
32254         if(this.videourl.length){
32255             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32256             // youtube support only?
32257             cfg.cn.push({
32258                 tag: 'iframe',
32259                 cls: 'masonry-brick-image-view',
32260                 src: vurl,
32261                 frameborder : 0,
32262                 allowfullscreen : true
32263             });
32264         }
32265         
32266         return cfg;
32267         
32268     },
32269     
32270     getSplitAutoCreate : function()
32271     {
32272         var cls = 'masonry-brick masonry-brick-split';
32273         
32274         if(this.href.length){
32275             cls += ' masonry-brick-link';
32276         }
32277         
32278         if(this.bgimage.length){
32279             cls += ' masonry-brick-image';
32280         }
32281         
32282         if(this.size){
32283             cls += ' masonry-' + this.size + '-brick';
32284         }
32285         
32286         switch (this.placetitle) {
32287             case 'center' :
32288                 cls += ' masonry-center-title';
32289                 break;
32290             case 'bottom' :
32291                 cls += ' masonry-bottom-title';
32292                 break;
32293             default:
32294                 if(!this.bgimage.length){
32295                     cls += ' masonry-center-title';
32296                 }
32297
32298                 if(this.bgimage.length){
32299                     cls += ' masonry-bottom-title';
32300                 }
32301                 break;
32302         }
32303         
32304         if(this.cls){
32305             cls += ' ' + this.cls;
32306         }
32307         
32308         var cfg = {
32309             tag: (this.href.length) ? 'a' : 'div',
32310             cls: cls,
32311             cn: [
32312                 {
32313                     tag: 'div',
32314                     cls: 'masonry-brick-split-head',
32315                     cn: [
32316                         {
32317                             tag: 'div',
32318                             cls: 'masonry-brick-paragraph',
32319                             cn: []
32320                         }
32321                     ]
32322                 },
32323                 {
32324                     tag: 'div',
32325                     cls: 'masonry-brick-split-body',
32326                     cn: []
32327                 }
32328             ]
32329         };
32330         
32331         if(this.href.length){
32332             cfg.href = this.href;
32333         }
32334         
32335         if(this.title.length){
32336             cfg.cn[0].cn[0].cn.push({
32337                 tag: 'h4',
32338                 cls: 'masonry-brick-title',
32339                 html: this.title
32340             });
32341         }
32342         
32343         if(this.html.length){
32344             cfg.cn[1].cn.push({
32345                 tag: 'p',
32346                 cls: 'masonry-brick-text',
32347                 html: this.html
32348             });
32349         }
32350
32351         if(this.bgimage.length){
32352             cfg.cn[0].cn.push({
32353                 tag: 'img',
32354                 cls: 'masonry-brick-image-view',
32355                 src: this.bgimage
32356             });
32357         }
32358         
32359         if(this.videourl.length){
32360             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32361             // youtube support only?
32362             cfg.cn[0].cn.cn.push({
32363                 tag: 'iframe',
32364                 cls: 'masonry-brick-image-view',
32365                 src: vurl,
32366                 frameborder : 0,
32367                 allowfullscreen : true
32368             });
32369         }
32370         
32371         return cfg;
32372     },
32373     
32374     initEvents: function() 
32375     {
32376         switch (this.size) {
32377             case 'xs' :
32378                 this.x = 1;
32379                 this.y = 1;
32380                 break;
32381             case 'sm' :
32382                 this.x = 2;
32383                 this.y = 2;
32384                 break;
32385             case 'md' :
32386             case 'md-left' :
32387             case 'md-right' :
32388                 this.x = 3;
32389                 this.y = 3;
32390                 break;
32391             case 'tall' :
32392                 this.x = 2;
32393                 this.y = 3;
32394                 break;
32395             case 'wide' :
32396                 this.x = 3;
32397                 this.y = 2;
32398                 break;
32399             case 'wide-thin' :
32400                 this.x = 3;
32401                 this.y = 1;
32402                 break;
32403                         
32404             default :
32405                 break;
32406         }
32407         
32408         if(Roo.isTouch){
32409             this.el.on('touchstart', this.onTouchStart, this);
32410             this.el.on('touchmove', this.onTouchMove, this);
32411             this.el.on('touchend', this.onTouchEnd, this);
32412             this.el.on('contextmenu', this.onContextMenu, this);
32413         } else {
32414             this.el.on('mouseenter'  ,this.enter, this);
32415             this.el.on('mouseleave', this.leave, this);
32416             this.el.on('click', this.onClick, this);
32417         }
32418         
32419         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32420             this.parent().bricks.push(this);   
32421         }
32422         
32423     },
32424     
32425     onClick: function(e, el)
32426     {
32427         var time = this.endTimer - this.startTimer;
32428         // Roo.log(e.preventDefault());
32429         if(Roo.isTouch){
32430             if(time > 1000){
32431                 e.preventDefault();
32432                 return;
32433             }
32434         }
32435         
32436         if(!this.preventDefault){
32437             return;
32438         }
32439         
32440         e.preventDefault();
32441         
32442         if (this.activcClass != '') {
32443             this.selectBrick();
32444         }
32445         
32446         this.fireEvent('click', this);
32447     },
32448     
32449     enter: function(e, el)
32450     {
32451         e.preventDefault();
32452         
32453         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32454             return;
32455         }
32456         
32457         if(this.bgimage.length && this.html.length){
32458             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32459         }
32460     },
32461     
32462     leave: function(e, el)
32463     {
32464         e.preventDefault();
32465         
32466         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32467             return;
32468         }
32469         
32470         if(this.bgimage.length && this.html.length){
32471             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32472         }
32473     },
32474     
32475     onTouchStart: function(e, el)
32476     {
32477 //        e.preventDefault();
32478         
32479         this.touchmoved = false;
32480         
32481         if(!this.isFitContainer){
32482             return;
32483         }
32484         
32485         if(!this.bgimage.length || !this.html.length){
32486             return;
32487         }
32488         
32489         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32490         
32491         this.timer = new Date().getTime();
32492         
32493     },
32494     
32495     onTouchMove: function(e, el)
32496     {
32497         this.touchmoved = true;
32498     },
32499     
32500     onContextMenu : function(e,el)
32501     {
32502         e.preventDefault();
32503         e.stopPropagation();
32504         return false;
32505     },
32506     
32507     onTouchEnd: function(e, el)
32508     {
32509 //        e.preventDefault();
32510         
32511         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32512         
32513             this.leave(e,el);
32514             
32515             return;
32516         }
32517         
32518         if(!this.bgimage.length || !this.html.length){
32519             
32520             if(this.href.length){
32521                 window.location.href = this.href;
32522             }
32523             
32524             return;
32525         }
32526         
32527         if(!this.isFitContainer){
32528             return;
32529         }
32530         
32531         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32532         
32533         window.location.href = this.href;
32534     },
32535     
32536     //selection on single brick only
32537     selectBrick : function() {
32538         
32539         if (!this.parentId) {
32540             return;
32541         }
32542         
32543         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32544         var index = m.selectedBrick.indexOf(this.id);
32545         
32546         if ( index > -1) {
32547             m.selectedBrick.splice(index,1);
32548             this.el.removeClass(this.activeClass);
32549             return;
32550         }
32551         
32552         for(var i = 0; i < m.selectedBrick.length; i++) {
32553             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32554             b.el.removeClass(b.activeClass);
32555         }
32556         
32557         m.selectedBrick = [];
32558         
32559         m.selectedBrick.push(this.id);
32560         this.el.addClass(this.activeClass);
32561         return;
32562     }
32563     
32564 });
32565
32566 Roo.apply(Roo.bootstrap.MasonryBrick, {
32567     
32568     //groups: {},
32569     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32570      /**
32571     * register a Masonry Brick
32572     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32573     */
32574     
32575     register : function(brick)
32576     {
32577         //this.groups[brick.id] = brick;
32578         this.groups.add(brick.id, brick);
32579     },
32580     /**
32581     * fetch a  masonry brick based on the masonry brick ID
32582     * @param {string} the masonry brick to add
32583     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32584     */
32585     
32586     get: function(brick_id) 
32587     {
32588         // if (typeof(this.groups[brick_id]) == 'undefined') {
32589         //     return false;
32590         // }
32591         // return this.groups[brick_id] ;
32592         
32593         if(this.groups.key(brick_id)) {
32594             return this.groups.key(brick_id);
32595         }
32596         
32597         return false;
32598     }
32599     
32600     
32601     
32602 });
32603
32604  /*
32605  * - LGPL
32606  *
32607  * element
32608  * 
32609  */
32610
32611 /**
32612  * @class Roo.bootstrap.Brick
32613  * @extends Roo.bootstrap.Component
32614  * Bootstrap Brick class
32615  * 
32616  * @constructor
32617  * Create a new Brick
32618  * @param {Object} config The config object
32619  */
32620
32621 Roo.bootstrap.Brick = function(config){
32622     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32623     
32624     this.addEvents({
32625         // raw events
32626         /**
32627          * @event click
32628          * When a Brick is click
32629          * @param {Roo.bootstrap.Brick} this
32630          * @param {Roo.EventObject} e
32631          */
32632         "click" : true
32633     });
32634 };
32635
32636 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32637     
32638     /**
32639      * @cfg {String} title
32640      */   
32641     title : '',
32642     /**
32643      * @cfg {String} html
32644      */   
32645     html : '',
32646     /**
32647      * @cfg {String} bgimage
32648      */   
32649     bgimage : '',
32650     /**
32651      * @cfg {String} cls
32652      */   
32653     cls : '',
32654     /**
32655      * @cfg {String} href
32656      */   
32657     href : '',
32658     /**
32659      * @cfg {String} video
32660      */   
32661     video : '',
32662     /**
32663      * @cfg {Boolean} square
32664      */   
32665     square : true,
32666     
32667     getAutoCreate : function()
32668     {
32669         var cls = 'roo-brick';
32670         
32671         if(this.href.length){
32672             cls += ' roo-brick-link';
32673         }
32674         
32675         if(this.bgimage.length){
32676             cls += ' roo-brick-image';
32677         }
32678         
32679         if(!this.html.length && !this.bgimage.length){
32680             cls += ' roo-brick-center-title';
32681         }
32682         
32683         if(!this.html.length && this.bgimage.length){
32684             cls += ' roo-brick-bottom-title';
32685         }
32686         
32687         if(this.cls){
32688             cls += ' ' + this.cls;
32689         }
32690         
32691         var cfg = {
32692             tag: (this.href.length) ? 'a' : 'div',
32693             cls: cls,
32694             cn: [
32695                 {
32696                     tag: 'div',
32697                     cls: 'roo-brick-paragraph',
32698                     cn: []
32699                 }
32700             ]
32701         };
32702         
32703         if(this.href.length){
32704             cfg.href = this.href;
32705         }
32706         
32707         var cn = cfg.cn[0].cn;
32708         
32709         if(this.title.length){
32710             cn.push({
32711                 tag: 'h4',
32712                 cls: 'roo-brick-title',
32713                 html: this.title
32714             });
32715         }
32716         
32717         if(this.html.length){
32718             cn.push({
32719                 tag: 'p',
32720                 cls: 'roo-brick-text',
32721                 html: this.html
32722             });
32723         } else {
32724             cn.cls += ' hide';
32725         }
32726         
32727         if(this.bgimage.length){
32728             cfg.cn.push({
32729                 tag: 'img',
32730                 cls: 'roo-brick-image-view',
32731                 src: this.bgimage
32732             });
32733         }
32734         
32735         return cfg;
32736     },
32737     
32738     initEvents: function() 
32739     {
32740         if(this.title.length || this.html.length){
32741             this.el.on('mouseenter'  ,this.enter, this);
32742             this.el.on('mouseleave', this.leave, this);
32743         }
32744         
32745         Roo.EventManager.onWindowResize(this.resize, this); 
32746         
32747         if(this.bgimage.length){
32748             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32749             this.imageEl.on('load', this.onImageLoad, this);
32750             return;
32751         }
32752         
32753         this.resize();
32754     },
32755     
32756     onImageLoad : function()
32757     {
32758         this.resize();
32759     },
32760     
32761     resize : function()
32762     {
32763         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32764         
32765         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32766         
32767         if(this.bgimage.length){
32768             var image = this.el.select('.roo-brick-image-view', true).first();
32769             
32770             image.setWidth(paragraph.getWidth());
32771             
32772             if(this.square){
32773                 image.setHeight(paragraph.getWidth());
32774             }
32775             
32776             this.el.setHeight(image.getHeight());
32777             paragraph.setHeight(image.getHeight());
32778             
32779         }
32780         
32781     },
32782     
32783     enter: function(e, el)
32784     {
32785         e.preventDefault();
32786         
32787         if(this.bgimage.length){
32788             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32789             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32790         }
32791     },
32792     
32793     leave: function(e, el)
32794     {
32795         e.preventDefault();
32796         
32797         if(this.bgimage.length){
32798             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32799             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32800         }
32801     }
32802     
32803 });
32804
32805  
32806
32807  /*
32808  * - LGPL
32809  *
32810  * Input
32811  * 
32812  */
32813
32814 /**
32815  * @class Roo.bootstrap.NumberField
32816  * @extends Roo.bootstrap.Input
32817  * Bootstrap NumberField class
32818  * 
32819  * 
32820  * 
32821  * 
32822  * @constructor
32823  * Create a new NumberField
32824  * @param {Object} config The config object
32825  */
32826
32827 Roo.bootstrap.NumberField = function(config){
32828     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32829 };
32830
32831 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32832     
32833     /**
32834      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32835      */
32836     allowDecimals : true,
32837     /**
32838      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32839      */
32840     decimalSeparator : ".",
32841     /**
32842      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32843      */
32844     decimalPrecision : 2,
32845     /**
32846      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32847      */
32848     allowNegative : true,
32849     /**
32850      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32851      */
32852     minValue : Number.NEGATIVE_INFINITY,
32853     /**
32854      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32855      */
32856     maxValue : Number.MAX_VALUE,
32857     /**
32858      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32859      */
32860     minText : "The minimum value for this field is {0}",
32861     /**
32862      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32863      */
32864     maxText : "The maximum value for this field is {0}",
32865     /**
32866      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32867      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32868      */
32869     nanText : "{0} is not a valid number",
32870     /**
32871      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32872      */
32873     castInt : true,
32874
32875     // private
32876     initEvents : function()
32877     {   
32878         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32879         
32880         var allowed = "0123456789";
32881         
32882         if(this.allowDecimals){
32883             allowed += this.decimalSeparator;
32884         }
32885         
32886         if(this.allowNegative){
32887             allowed += "-";
32888         }
32889         
32890         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32891         
32892         var keyPress = function(e){
32893             
32894             var k = e.getKey();
32895             
32896             var c = e.getCharCode();
32897             
32898             if(
32899                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32900                     allowed.indexOf(String.fromCharCode(c)) === -1
32901             ){
32902                 e.stopEvent();
32903                 return;
32904             }
32905             
32906             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32907                 return;
32908             }
32909             
32910             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32911                 e.stopEvent();
32912             }
32913         };
32914         
32915         this.el.on("keypress", keyPress, this);
32916     },
32917     
32918     validateValue : function(value)
32919     {
32920         
32921         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32922             return false;
32923         }
32924         
32925         var num = this.parseValue(value);
32926         
32927         if(isNaN(num)){
32928             this.markInvalid(String.format(this.nanText, value));
32929             return false;
32930         }
32931         
32932         if(num < this.minValue){
32933             this.markInvalid(String.format(this.minText, this.minValue));
32934             return false;
32935         }
32936         
32937         if(num > this.maxValue){
32938             this.markInvalid(String.format(this.maxText, this.maxValue));
32939             return false;
32940         }
32941         
32942         return true;
32943     },
32944
32945     getValue : function()
32946     {
32947         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32948     },
32949
32950     parseValue : function(value)
32951     {
32952         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32953         return isNaN(value) ? '' : value;
32954     },
32955
32956     fixPrecision : function(value)
32957     {
32958         var nan = isNaN(value);
32959         
32960         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32961             return nan ? '' : value;
32962         }
32963         return parseFloat(value).toFixed(this.decimalPrecision);
32964     },
32965
32966     setValue : function(v)
32967     {
32968         v = this.fixPrecision(v);
32969         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32970     },
32971
32972     decimalPrecisionFcn : function(v)
32973     {
32974         return Math.floor(v);
32975     },
32976
32977     beforeBlur : function()
32978     {
32979         if(!this.castInt){
32980             return;
32981         }
32982         
32983         var v = this.parseValue(this.getRawValue());
32984         if(v){
32985             this.setValue(v);
32986         }
32987     }
32988     
32989 });
32990
32991  
32992
32993 /*
32994 * Licence: LGPL
32995 */
32996
32997 /**
32998  * @class Roo.bootstrap.DocumentSlider
32999  * @extends Roo.bootstrap.Component
33000  * Bootstrap DocumentSlider class
33001  * 
33002  * @constructor
33003  * Create a new DocumentViewer
33004  * @param {Object} config The config object
33005  */
33006
33007 Roo.bootstrap.DocumentSlider = function(config){
33008     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33009     
33010     this.files = [];
33011     
33012     this.addEvents({
33013         /**
33014          * @event initial
33015          * Fire after initEvent
33016          * @param {Roo.bootstrap.DocumentSlider} this
33017          */
33018         "initial" : true,
33019         /**
33020          * @event update
33021          * Fire after update
33022          * @param {Roo.bootstrap.DocumentSlider} this
33023          */
33024         "update" : true,
33025         /**
33026          * @event click
33027          * Fire after click
33028          * @param {Roo.bootstrap.DocumentSlider} this
33029          */
33030         "click" : true
33031     });
33032 };
33033
33034 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33035     
33036     files : false,
33037     
33038     indicator : 0,
33039     
33040     getAutoCreate : function()
33041     {
33042         var cfg = {
33043             tag : 'div',
33044             cls : 'roo-document-slider',
33045             cn : [
33046                 {
33047                     tag : 'div',
33048                     cls : 'roo-document-slider-header',
33049                     cn : [
33050                         {
33051                             tag : 'div',
33052                             cls : 'roo-document-slider-header-title'
33053                         }
33054                     ]
33055                 },
33056                 {
33057                     tag : 'div',
33058                     cls : 'roo-document-slider-body',
33059                     cn : [
33060                         {
33061                             tag : 'div',
33062                             cls : 'roo-document-slider-prev',
33063                             cn : [
33064                                 {
33065                                     tag : 'i',
33066                                     cls : 'fa fa-chevron-left'
33067                                 }
33068                             ]
33069                         },
33070                         {
33071                             tag : 'div',
33072                             cls : 'roo-document-slider-thumb',
33073                             cn : [
33074                                 {
33075                                     tag : 'img',
33076                                     cls : 'roo-document-slider-image'
33077                                 }
33078                             ]
33079                         },
33080                         {
33081                             tag : 'div',
33082                             cls : 'roo-document-slider-next',
33083                             cn : [
33084                                 {
33085                                     tag : 'i',
33086                                     cls : 'fa fa-chevron-right'
33087                                 }
33088                             ]
33089                         }
33090                     ]
33091                 }
33092             ]
33093         };
33094         
33095         return cfg;
33096     },
33097     
33098     initEvents : function()
33099     {
33100         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33101         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33102         
33103         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33104         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33105         
33106         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33107         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33108         
33109         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33110         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33111         
33112         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33113         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33114         
33115         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33116         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33117         
33118         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33119         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33120         
33121         this.thumbEl.on('click', this.onClick, this);
33122         
33123         this.prevIndicator.on('click', this.prev, this);
33124         
33125         this.nextIndicator.on('click', this.next, this);
33126         
33127     },
33128     
33129     initial : function()
33130     {
33131         if(this.files.length){
33132             this.indicator = 1;
33133             this.update()
33134         }
33135         
33136         this.fireEvent('initial', this);
33137     },
33138     
33139     update : function()
33140     {
33141         this.imageEl.attr('src', this.files[this.indicator - 1]);
33142         
33143         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33144         
33145         this.prevIndicator.show();
33146         
33147         if(this.indicator == 1){
33148             this.prevIndicator.hide();
33149         }
33150         
33151         this.nextIndicator.show();
33152         
33153         if(this.indicator == this.files.length){
33154             this.nextIndicator.hide();
33155         }
33156         
33157         this.thumbEl.scrollTo('top');
33158         
33159         this.fireEvent('update', this);
33160     },
33161     
33162     onClick : function(e)
33163     {
33164         e.preventDefault();
33165         
33166         this.fireEvent('click', this);
33167     },
33168     
33169     prev : function(e)
33170     {
33171         e.preventDefault();
33172         
33173         this.indicator = Math.max(1, this.indicator - 1);
33174         
33175         this.update();
33176     },
33177     
33178     next : function(e)
33179     {
33180         e.preventDefault();
33181         
33182         this.indicator = Math.min(this.files.length, this.indicator + 1);
33183         
33184         this.update();
33185     }
33186 });
33187 /*
33188  * - LGPL
33189  *
33190  * RadioSet
33191  *
33192  *
33193  */
33194
33195 /**
33196  * @class Roo.bootstrap.RadioSet
33197  * @extends Roo.bootstrap.Input
33198  * Bootstrap RadioSet class
33199  * @cfg {String} indicatorpos (left|right) default left
33200  * @cfg {Boolean} inline (true|false) inline the element (default true)
33201  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33202  * @constructor
33203  * Create a new RadioSet
33204  * @param {Object} config The config object
33205  */
33206
33207 Roo.bootstrap.RadioSet = function(config){
33208     
33209     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33210     
33211     this.radioes = [];
33212     
33213     Roo.bootstrap.RadioSet.register(this);
33214     
33215     this.addEvents({
33216         /**
33217         * @event check
33218         * Fires when the element is checked or unchecked.
33219         * @param {Roo.bootstrap.RadioSet} this This radio
33220         * @param {Roo.bootstrap.Radio} item The checked item
33221         */
33222        check : true
33223     });
33224     
33225 };
33226
33227 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33228
33229     radioes : false,
33230     
33231     inline : true,
33232     
33233     weight : '',
33234     
33235     indicatorpos : 'left',
33236     
33237     getAutoCreate : function()
33238     {
33239         var label = {
33240             tag : 'label',
33241             cls : 'roo-radio-set-label',
33242             cn : [
33243                 {
33244                     tag : 'span',
33245                     html : this.fieldLabel
33246                 }
33247             ]
33248         };
33249         
33250         if(this.indicatorpos == 'left'){
33251             label.cn.unshift({
33252                 tag : 'i',
33253                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33254                 tooltip : 'This field is required'
33255             });
33256         } else {
33257             label.cn.push({
33258                 tag : 'i',
33259                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33260                 tooltip : 'This field is required'
33261             });
33262         }
33263         
33264         var items = {
33265             tag : 'div',
33266             cls : 'roo-radio-set-items'
33267         };
33268         
33269         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33270         
33271         if (align === 'left' && this.fieldLabel.length) {
33272             
33273             items = {
33274                 cls : "roo-radio-set-right", 
33275                 cn: [
33276                     items
33277                 ]
33278             };
33279             
33280             if(this.labelWidth > 12){
33281                 label.style = "width: " + this.labelWidth + 'px';
33282             }
33283             
33284             if(this.labelWidth < 13 && this.labelmd == 0){
33285                 this.labelmd = this.labelWidth;
33286             }
33287             
33288             if(this.labellg > 0){
33289                 label.cls += ' col-lg-' + this.labellg;
33290                 items.cls += ' col-lg-' + (12 - this.labellg);
33291             }
33292             
33293             if(this.labelmd > 0){
33294                 label.cls += ' col-md-' + this.labelmd;
33295                 items.cls += ' col-md-' + (12 - this.labelmd);
33296             }
33297             
33298             if(this.labelsm > 0){
33299                 label.cls += ' col-sm-' + this.labelsm;
33300                 items.cls += ' col-sm-' + (12 - this.labelsm);
33301             }
33302             
33303             if(this.labelxs > 0){
33304                 label.cls += ' col-xs-' + this.labelxs;
33305                 items.cls += ' col-xs-' + (12 - this.labelxs);
33306             }
33307         }
33308         
33309         var cfg = {
33310             tag : 'div',
33311             cls : 'roo-radio-set',
33312             cn : [
33313                 {
33314                     tag : 'input',
33315                     cls : 'roo-radio-set-input',
33316                     type : 'hidden',
33317                     name : this.name,
33318                     value : this.value ? this.value :  ''
33319                 },
33320                 label,
33321                 items
33322             ]
33323         };
33324         
33325         if(this.weight.length){
33326             cfg.cls += ' roo-radio-' + this.weight;
33327         }
33328         
33329         if(this.inline) {
33330             cfg.cls += ' roo-radio-set-inline';
33331         }
33332         
33333         var settings=this;
33334         ['xs','sm','md','lg'].map(function(size){
33335             if (settings[size]) {
33336                 cfg.cls += ' col-' + size + '-' + settings[size];
33337             }
33338         });
33339         
33340         return cfg;
33341         
33342     },
33343
33344     initEvents : function()
33345     {
33346         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33347         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33348         
33349         if(!this.fieldLabel.length){
33350             this.labelEl.hide();
33351         }
33352         
33353         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33354         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33355         
33356         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33357         this.indicatorEl().hide();
33358         
33359         this.originalValue = this.getValue();
33360         
33361     },
33362     
33363     inputEl: function ()
33364     {
33365         return this.el.select('.roo-radio-set-input', true).first();
33366     },
33367     
33368     getChildContainer : function()
33369     {
33370         return this.itemsEl;
33371     },
33372     
33373     register : function(item)
33374     {
33375         this.radioes.push(item);
33376         
33377     },
33378     
33379     validate : function()
33380     {   
33381         var valid = false;
33382         
33383         Roo.each(this.radioes, function(i){
33384             if(!i.checked){
33385                 return;
33386             }
33387             
33388             valid = true;
33389             return false;
33390         });
33391         
33392         if(this.allowBlank) {
33393             return true;
33394         }
33395         
33396         if(this.disabled || valid){
33397             this.markValid();
33398             return true;
33399         }
33400         
33401         this.markInvalid();
33402         return false;
33403         
33404     },
33405     
33406     markValid : function()
33407     {
33408         if(this.labelEl.isVisible(true)){
33409             this.indicatorEl().hide();
33410         }
33411         
33412         this.el.removeClass([this.invalidClass, this.validClass]);
33413         this.el.addClass(this.validClass);
33414         
33415         this.fireEvent('valid', this);
33416     },
33417     
33418     markInvalid : function(msg)
33419     {
33420         if(this.allowBlank || this.disabled){
33421             return;
33422         }
33423         
33424         if(this.labelEl.isVisible(true)){
33425             this.indicatorEl().show();
33426         }
33427         
33428         this.el.removeClass([this.invalidClass, this.validClass]);
33429         this.el.addClass(this.invalidClass);
33430         
33431         this.fireEvent('invalid', this, msg);
33432         
33433     },
33434     
33435     setValue : function(v, suppressEvent)
33436     {   
33437         this.value = v;
33438         if(this.rendered){
33439             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33440         }
33441         
33442         Roo.each(this.radioes, function(i){
33443             
33444             i.checked = false;
33445             i.el.removeClass('checked');
33446             
33447             if(i.value === v || i.value.toString() === v.toString()){
33448                 i.checked = true;
33449                 i.el.addClass('checked');
33450                 
33451                 if(suppressEvent !== true){
33452                     this.fireEvent('check', this, i);
33453                 }
33454             }
33455             
33456         }, this);
33457         
33458         this.validate();
33459     },
33460     
33461     clearInvalid : function(){
33462         
33463         if(!this.el || this.preventMark){
33464             return;
33465         }
33466         
33467         this.el.removeClass([this.invalidClass]);
33468         
33469         this.fireEvent('valid', this);
33470     }
33471     
33472 });
33473
33474 Roo.apply(Roo.bootstrap.RadioSet, {
33475     
33476     groups: {},
33477     
33478     register : function(set)
33479     {
33480         this.groups[set.name] = set;
33481     },
33482     
33483     get: function(name) 
33484     {
33485         if (typeof(this.groups[name]) == 'undefined') {
33486             return false;
33487         }
33488         
33489         return this.groups[name] ;
33490     }
33491     
33492 });
33493 /*
33494  * Based on:
33495  * Ext JS Library 1.1.1
33496  * Copyright(c) 2006-2007, Ext JS, LLC.
33497  *
33498  * Originally Released Under LGPL - original licence link has changed is not relivant.
33499  *
33500  * Fork - LGPL
33501  * <script type="text/javascript">
33502  */
33503
33504
33505 /**
33506  * @class Roo.bootstrap.SplitBar
33507  * @extends Roo.util.Observable
33508  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33509  * <br><br>
33510  * Usage:
33511  * <pre><code>
33512 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33513                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33514 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33515 split.minSize = 100;
33516 split.maxSize = 600;
33517 split.animate = true;
33518 split.on('moved', splitterMoved);
33519 </code></pre>
33520  * @constructor
33521  * Create a new SplitBar
33522  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33523  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33524  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33525  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33526                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33527                         position of the SplitBar).
33528  */
33529 Roo.bootstrap.SplitBar = function(cfg){
33530     
33531     /** @private */
33532     
33533     //{
33534     //  dragElement : elm
33535     //  resizingElement: el,
33536         // optional..
33537     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33538     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33539         // existingProxy ???
33540     //}
33541     
33542     this.el = Roo.get(cfg.dragElement, true);
33543     this.el.dom.unselectable = "on";
33544     /** @private */
33545     this.resizingEl = Roo.get(cfg.resizingElement, true);
33546
33547     /**
33548      * @private
33549      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33550      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33551      * @type Number
33552      */
33553     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33554     
33555     /**
33556      * The minimum size of the resizing element. (Defaults to 0)
33557      * @type Number
33558      */
33559     this.minSize = 0;
33560     
33561     /**
33562      * The maximum size of the resizing element. (Defaults to 2000)
33563      * @type Number
33564      */
33565     this.maxSize = 2000;
33566     
33567     /**
33568      * Whether to animate the transition to the new size
33569      * @type Boolean
33570      */
33571     this.animate = false;
33572     
33573     /**
33574      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33575      * @type Boolean
33576      */
33577     this.useShim = false;
33578     
33579     /** @private */
33580     this.shim = null;
33581     
33582     if(!cfg.existingProxy){
33583         /** @private */
33584         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33585     }else{
33586         this.proxy = Roo.get(cfg.existingProxy).dom;
33587     }
33588     /** @private */
33589     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33590     
33591     /** @private */
33592     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33593     
33594     /** @private */
33595     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33596     
33597     /** @private */
33598     this.dragSpecs = {};
33599     
33600     /**
33601      * @private The adapter to use to positon and resize elements
33602      */
33603     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33604     this.adapter.init(this);
33605     
33606     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33607         /** @private */
33608         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33609         this.el.addClass("roo-splitbar-h");
33610     }else{
33611         /** @private */
33612         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33613         this.el.addClass("roo-splitbar-v");
33614     }
33615     
33616     this.addEvents({
33617         /**
33618          * @event resize
33619          * Fires when the splitter is moved (alias for {@link #event-moved})
33620          * @param {Roo.bootstrap.SplitBar} this
33621          * @param {Number} newSize the new width or height
33622          */
33623         "resize" : true,
33624         /**
33625          * @event moved
33626          * Fires when the splitter is moved
33627          * @param {Roo.bootstrap.SplitBar} this
33628          * @param {Number} newSize the new width or height
33629          */
33630         "moved" : true,
33631         /**
33632          * @event beforeresize
33633          * Fires before the splitter is dragged
33634          * @param {Roo.bootstrap.SplitBar} this
33635          */
33636         "beforeresize" : true,
33637
33638         "beforeapply" : true
33639     });
33640
33641     Roo.util.Observable.call(this);
33642 };
33643
33644 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33645     onStartProxyDrag : function(x, y){
33646         this.fireEvent("beforeresize", this);
33647         if(!this.overlay){
33648             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33649             o.unselectable();
33650             o.enableDisplayMode("block");
33651             // all splitbars share the same overlay
33652             Roo.bootstrap.SplitBar.prototype.overlay = o;
33653         }
33654         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33655         this.overlay.show();
33656         Roo.get(this.proxy).setDisplayed("block");
33657         var size = this.adapter.getElementSize(this);
33658         this.activeMinSize = this.getMinimumSize();;
33659         this.activeMaxSize = this.getMaximumSize();;
33660         var c1 = size - this.activeMinSize;
33661         var c2 = Math.max(this.activeMaxSize - size, 0);
33662         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33663             this.dd.resetConstraints();
33664             this.dd.setXConstraint(
33665                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33666                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33667             );
33668             this.dd.setYConstraint(0, 0);
33669         }else{
33670             this.dd.resetConstraints();
33671             this.dd.setXConstraint(0, 0);
33672             this.dd.setYConstraint(
33673                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33674                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33675             );
33676          }
33677         this.dragSpecs.startSize = size;
33678         this.dragSpecs.startPoint = [x, y];
33679         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33680     },
33681     
33682     /** 
33683      * @private Called after the drag operation by the DDProxy
33684      */
33685     onEndProxyDrag : function(e){
33686         Roo.get(this.proxy).setDisplayed(false);
33687         var endPoint = Roo.lib.Event.getXY(e);
33688         if(this.overlay){
33689             this.overlay.hide();
33690         }
33691         var newSize;
33692         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33693             newSize = this.dragSpecs.startSize + 
33694                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33695                     endPoint[0] - this.dragSpecs.startPoint[0] :
33696                     this.dragSpecs.startPoint[0] - endPoint[0]
33697                 );
33698         }else{
33699             newSize = this.dragSpecs.startSize + 
33700                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33701                     endPoint[1] - this.dragSpecs.startPoint[1] :
33702                     this.dragSpecs.startPoint[1] - endPoint[1]
33703                 );
33704         }
33705         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33706         if(newSize != this.dragSpecs.startSize){
33707             if(this.fireEvent('beforeapply', this, newSize) !== false){
33708                 this.adapter.setElementSize(this, newSize);
33709                 this.fireEvent("moved", this, newSize);
33710                 this.fireEvent("resize", this, newSize);
33711             }
33712         }
33713     },
33714     
33715     /**
33716      * Get the adapter this SplitBar uses
33717      * @return The adapter object
33718      */
33719     getAdapter : function(){
33720         return this.adapter;
33721     },
33722     
33723     /**
33724      * Set the adapter this SplitBar uses
33725      * @param {Object} adapter A SplitBar adapter object
33726      */
33727     setAdapter : function(adapter){
33728         this.adapter = adapter;
33729         this.adapter.init(this);
33730     },
33731     
33732     /**
33733      * Gets the minimum size for the resizing element
33734      * @return {Number} The minimum size
33735      */
33736     getMinimumSize : function(){
33737         return this.minSize;
33738     },
33739     
33740     /**
33741      * Sets the minimum size for the resizing element
33742      * @param {Number} minSize The minimum size
33743      */
33744     setMinimumSize : function(minSize){
33745         this.minSize = minSize;
33746     },
33747     
33748     /**
33749      * Gets the maximum size for the resizing element
33750      * @return {Number} The maximum size
33751      */
33752     getMaximumSize : function(){
33753         return this.maxSize;
33754     },
33755     
33756     /**
33757      * Sets the maximum size for the resizing element
33758      * @param {Number} maxSize The maximum size
33759      */
33760     setMaximumSize : function(maxSize){
33761         this.maxSize = maxSize;
33762     },
33763     
33764     /**
33765      * Sets the initialize size for the resizing element
33766      * @param {Number} size The initial size
33767      */
33768     setCurrentSize : function(size){
33769         var oldAnimate = this.animate;
33770         this.animate = false;
33771         this.adapter.setElementSize(this, size);
33772         this.animate = oldAnimate;
33773     },
33774     
33775     /**
33776      * Destroy this splitbar. 
33777      * @param {Boolean} removeEl True to remove the element
33778      */
33779     destroy : function(removeEl){
33780         if(this.shim){
33781             this.shim.remove();
33782         }
33783         this.dd.unreg();
33784         this.proxy.parentNode.removeChild(this.proxy);
33785         if(removeEl){
33786             this.el.remove();
33787         }
33788     }
33789 });
33790
33791 /**
33792  * @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.
33793  */
33794 Roo.bootstrap.SplitBar.createProxy = function(dir){
33795     var proxy = new Roo.Element(document.createElement("div"));
33796     proxy.unselectable();
33797     var cls = 'roo-splitbar-proxy';
33798     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33799     document.body.appendChild(proxy.dom);
33800     return proxy.dom;
33801 };
33802
33803 /** 
33804  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33805  * Default Adapter. It assumes the splitter and resizing element are not positioned
33806  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33807  */
33808 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33809 };
33810
33811 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33812     // do nothing for now
33813     init : function(s){
33814     
33815     },
33816     /**
33817      * Called before drag operations to get the current size of the resizing element. 
33818      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33819      */
33820      getElementSize : function(s){
33821         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33822             return s.resizingEl.getWidth();
33823         }else{
33824             return s.resizingEl.getHeight();
33825         }
33826     },
33827     
33828     /**
33829      * Called after drag operations to set the size of the resizing element.
33830      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33831      * @param {Number} newSize The new size to set
33832      * @param {Function} onComplete A function to be invoked when resizing is complete
33833      */
33834     setElementSize : function(s, newSize, onComplete){
33835         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33836             if(!s.animate){
33837                 s.resizingEl.setWidth(newSize);
33838                 if(onComplete){
33839                     onComplete(s, newSize);
33840                 }
33841             }else{
33842                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33843             }
33844         }else{
33845             
33846             if(!s.animate){
33847                 s.resizingEl.setHeight(newSize);
33848                 if(onComplete){
33849                     onComplete(s, newSize);
33850                 }
33851             }else{
33852                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33853             }
33854         }
33855     }
33856 };
33857
33858 /** 
33859  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33860  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33861  * Adapter that  moves the splitter element to align with the resized sizing element. 
33862  * Used with an absolute positioned SplitBar.
33863  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33864  * document.body, make sure you assign an id to the body element.
33865  */
33866 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33867     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33868     this.container = Roo.get(container);
33869 };
33870
33871 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33872     init : function(s){
33873         this.basic.init(s);
33874     },
33875     
33876     getElementSize : function(s){
33877         return this.basic.getElementSize(s);
33878     },
33879     
33880     setElementSize : function(s, newSize, onComplete){
33881         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33882     },
33883     
33884     moveSplitter : function(s){
33885         var yes = Roo.bootstrap.SplitBar;
33886         switch(s.placement){
33887             case yes.LEFT:
33888                 s.el.setX(s.resizingEl.getRight());
33889                 break;
33890             case yes.RIGHT:
33891                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33892                 break;
33893             case yes.TOP:
33894                 s.el.setY(s.resizingEl.getBottom());
33895                 break;
33896             case yes.BOTTOM:
33897                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33898                 break;
33899         }
33900     }
33901 };
33902
33903 /**
33904  * Orientation constant - Create a vertical SplitBar
33905  * @static
33906  * @type Number
33907  */
33908 Roo.bootstrap.SplitBar.VERTICAL = 1;
33909
33910 /**
33911  * Orientation constant - Create a horizontal SplitBar
33912  * @static
33913  * @type Number
33914  */
33915 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33916
33917 /**
33918  * Placement constant - The resizing element is to the left of the splitter element
33919  * @static
33920  * @type Number
33921  */
33922 Roo.bootstrap.SplitBar.LEFT = 1;
33923
33924 /**
33925  * Placement constant - The resizing element is to the right of the splitter element
33926  * @static
33927  * @type Number
33928  */
33929 Roo.bootstrap.SplitBar.RIGHT = 2;
33930
33931 /**
33932  * Placement constant - The resizing element is positioned above the splitter element
33933  * @static
33934  * @type Number
33935  */
33936 Roo.bootstrap.SplitBar.TOP = 3;
33937
33938 /**
33939  * Placement constant - The resizing element is positioned under splitter element
33940  * @static
33941  * @type Number
33942  */
33943 Roo.bootstrap.SplitBar.BOTTOM = 4;
33944 Roo.namespace("Roo.bootstrap.layout");/*
33945  * Based on:
33946  * Ext JS Library 1.1.1
33947  * Copyright(c) 2006-2007, Ext JS, LLC.
33948  *
33949  * Originally Released Under LGPL - original licence link has changed is not relivant.
33950  *
33951  * Fork - LGPL
33952  * <script type="text/javascript">
33953  */
33954
33955 /**
33956  * @class Roo.bootstrap.layout.Manager
33957  * @extends Roo.bootstrap.Component
33958  * Base class for layout managers.
33959  */
33960 Roo.bootstrap.layout.Manager = function(config)
33961 {
33962     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33963
33964
33965
33966
33967
33968     /** false to disable window resize monitoring @type Boolean */
33969     this.monitorWindowResize = true;
33970     this.regions = {};
33971     this.addEvents({
33972         /**
33973          * @event layout
33974          * Fires when a layout is performed.
33975          * @param {Roo.LayoutManager} this
33976          */
33977         "layout" : true,
33978         /**
33979          * @event regionresized
33980          * Fires when the user resizes a region.
33981          * @param {Roo.LayoutRegion} region The resized region
33982          * @param {Number} newSize The new size (width for east/west, height for north/south)
33983          */
33984         "regionresized" : true,
33985         /**
33986          * @event regioncollapsed
33987          * Fires when a region is collapsed.
33988          * @param {Roo.LayoutRegion} region The collapsed region
33989          */
33990         "regioncollapsed" : true,
33991         /**
33992          * @event regionexpanded
33993          * Fires when a region is expanded.
33994          * @param {Roo.LayoutRegion} region The expanded region
33995          */
33996         "regionexpanded" : true
33997     });
33998     this.updating = false;
33999
34000     if (config.el) {
34001         this.el = Roo.get(config.el);
34002         this.initEvents();
34003     }
34004
34005 };
34006
34007 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34008
34009
34010     regions : null,
34011
34012     monitorWindowResize : true,
34013
34014
34015     updating : false,
34016
34017
34018     onRender : function(ct, position)
34019     {
34020         if(!this.el){
34021             this.el = Roo.get(ct);
34022             this.initEvents();
34023         }
34024         //this.fireEvent('render',this);
34025     },
34026
34027
34028     initEvents: function()
34029     {
34030
34031
34032         // ie scrollbar fix
34033         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34034             document.body.scroll = "no";
34035         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34036             this.el.position('relative');
34037         }
34038         this.id = this.el.id;
34039         this.el.addClass("roo-layout-container");
34040         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34041         if(this.el.dom != document.body ) {
34042             this.el.on('resize', this.layout,this);
34043             this.el.on('show', this.layout,this);
34044         }
34045
34046     },
34047
34048     /**
34049      * Returns true if this layout is currently being updated
34050      * @return {Boolean}
34051      */
34052     isUpdating : function(){
34053         return this.updating;
34054     },
34055
34056     /**
34057      * Suspend the LayoutManager from doing auto-layouts while
34058      * making multiple add or remove calls
34059      */
34060     beginUpdate : function(){
34061         this.updating = true;
34062     },
34063
34064     /**
34065      * Restore auto-layouts and optionally disable the manager from performing a layout
34066      * @param {Boolean} noLayout true to disable a layout update
34067      */
34068     endUpdate : function(noLayout){
34069         this.updating = false;
34070         if(!noLayout){
34071             this.layout();
34072         }
34073     },
34074
34075     layout: function(){
34076         // abstract...
34077     },
34078
34079     onRegionResized : function(region, newSize){
34080         this.fireEvent("regionresized", region, newSize);
34081         this.layout();
34082     },
34083
34084     onRegionCollapsed : function(region){
34085         this.fireEvent("regioncollapsed", region);
34086     },
34087
34088     onRegionExpanded : function(region){
34089         this.fireEvent("regionexpanded", region);
34090     },
34091
34092     /**
34093      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34094      * performs box-model adjustments.
34095      * @return {Object} The size as an object {width: (the width), height: (the height)}
34096      */
34097     getViewSize : function()
34098     {
34099         var size;
34100         if(this.el.dom != document.body){
34101             size = this.el.getSize();
34102         }else{
34103             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34104         }
34105         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34106         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34107         return size;
34108     },
34109
34110     /**
34111      * Returns the Element this layout is bound to.
34112      * @return {Roo.Element}
34113      */
34114     getEl : function(){
34115         return this.el;
34116     },
34117
34118     /**
34119      * Returns the specified region.
34120      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34121      * @return {Roo.LayoutRegion}
34122      */
34123     getRegion : function(target){
34124         return this.regions[target.toLowerCase()];
34125     },
34126
34127     onWindowResize : function(){
34128         if(this.monitorWindowResize){
34129             this.layout();
34130         }
34131     }
34132 });
34133 /*
34134  * Based on:
34135  * Ext JS Library 1.1.1
34136  * Copyright(c) 2006-2007, Ext JS, LLC.
34137  *
34138  * Originally Released Under LGPL - original licence link has changed is not relivant.
34139  *
34140  * Fork - LGPL
34141  * <script type="text/javascript">
34142  */
34143 /**
34144  * @class Roo.bootstrap.layout.Border
34145  * @extends Roo.bootstrap.layout.Manager
34146  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34147  * please see: examples/bootstrap/nested.html<br><br>
34148  
34149 <b>The container the layout is rendered into can be either the body element or any other element.
34150 If it is not the body element, the container needs to either be an absolute positioned element,
34151 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34152 the container size if it is not the body element.</b>
34153
34154 * @constructor
34155 * Create a new Border
34156 * @param {Object} config Configuration options
34157  */
34158 Roo.bootstrap.layout.Border = function(config){
34159     config = config || {};
34160     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34161     
34162     
34163     
34164     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34165         if(config[region]){
34166             config[region].region = region;
34167             this.addRegion(config[region]);
34168         }
34169     },this);
34170     
34171 };
34172
34173 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34174
34175 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34176     /**
34177      * Creates and adds a new region if it doesn't already exist.
34178      * @param {String} target The target region key (north, south, east, west or center).
34179      * @param {Object} config The regions config object
34180      * @return {BorderLayoutRegion} The new region
34181      */
34182     addRegion : function(config)
34183     {
34184         if(!this.regions[config.region]){
34185             var r = this.factory(config);
34186             this.bindRegion(r);
34187         }
34188         return this.regions[config.region];
34189     },
34190
34191     // private (kinda)
34192     bindRegion : function(r){
34193         this.regions[r.config.region] = r;
34194         
34195         r.on("visibilitychange",    this.layout, this);
34196         r.on("paneladded",          this.layout, this);
34197         r.on("panelremoved",        this.layout, this);
34198         r.on("invalidated",         this.layout, this);
34199         r.on("resized",             this.onRegionResized, this);
34200         r.on("collapsed",           this.onRegionCollapsed, this);
34201         r.on("expanded",            this.onRegionExpanded, this);
34202     },
34203
34204     /**
34205      * Performs a layout update.
34206      */
34207     layout : function()
34208     {
34209         if(this.updating) {
34210             return;
34211         }
34212         
34213         // render all the rebions if they have not been done alreayd?
34214         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34215             if(this.regions[region] && !this.regions[region].bodyEl){
34216                 this.regions[region].onRender(this.el)
34217             }
34218         },this);
34219         
34220         var size = this.getViewSize();
34221         var w = size.width;
34222         var h = size.height;
34223         var centerW = w;
34224         var centerH = h;
34225         var centerY = 0;
34226         var centerX = 0;
34227         //var x = 0, y = 0;
34228
34229         var rs = this.regions;
34230         var north = rs["north"];
34231         var south = rs["south"]; 
34232         var west = rs["west"];
34233         var east = rs["east"];
34234         var center = rs["center"];
34235         //if(this.hideOnLayout){ // not supported anymore
34236             //c.el.setStyle("display", "none");
34237         //}
34238         if(north && north.isVisible()){
34239             var b = north.getBox();
34240             var m = north.getMargins();
34241             b.width = w - (m.left+m.right);
34242             b.x = m.left;
34243             b.y = m.top;
34244             centerY = b.height + b.y + m.bottom;
34245             centerH -= centerY;
34246             north.updateBox(this.safeBox(b));
34247         }
34248         if(south && south.isVisible()){
34249             var b = south.getBox();
34250             var m = south.getMargins();
34251             b.width = w - (m.left+m.right);
34252             b.x = m.left;
34253             var totalHeight = (b.height + m.top + m.bottom);
34254             b.y = h - totalHeight + m.top;
34255             centerH -= totalHeight;
34256             south.updateBox(this.safeBox(b));
34257         }
34258         if(west && west.isVisible()){
34259             var b = west.getBox();
34260             var m = west.getMargins();
34261             b.height = centerH - (m.top+m.bottom);
34262             b.x = m.left;
34263             b.y = centerY + m.top;
34264             var totalWidth = (b.width + m.left + m.right);
34265             centerX += totalWidth;
34266             centerW -= totalWidth;
34267             west.updateBox(this.safeBox(b));
34268         }
34269         if(east && east.isVisible()){
34270             var b = east.getBox();
34271             var m = east.getMargins();
34272             b.height = centerH - (m.top+m.bottom);
34273             var totalWidth = (b.width + m.left + m.right);
34274             b.x = w - totalWidth + m.left;
34275             b.y = centerY + m.top;
34276             centerW -= totalWidth;
34277             east.updateBox(this.safeBox(b));
34278         }
34279         if(center){
34280             var m = center.getMargins();
34281             var centerBox = {
34282                 x: centerX + m.left,
34283                 y: centerY + m.top,
34284                 width: centerW - (m.left+m.right),
34285                 height: centerH - (m.top+m.bottom)
34286             };
34287             //if(this.hideOnLayout){
34288                 //center.el.setStyle("display", "block");
34289             //}
34290             center.updateBox(this.safeBox(centerBox));
34291         }
34292         this.el.repaint();
34293         this.fireEvent("layout", this);
34294     },
34295
34296     // private
34297     safeBox : function(box){
34298         box.width = Math.max(0, box.width);
34299         box.height = Math.max(0, box.height);
34300         return box;
34301     },
34302
34303     /**
34304      * Adds a ContentPanel (or subclass) to this layout.
34305      * @param {String} target The target region key (north, south, east, west or center).
34306      * @param {Roo.ContentPanel} panel The panel to add
34307      * @return {Roo.ContentPanel} The added panel
34308      */
34309     add : function(target, panel){
34310          
34311         target = target.toLowerCase();
34312         return this.regions[target].add(panel);
34313     },
34314
34315     /**
34316      * Remove a ContentPanel (or subclass) to this layout.
34317      * @param {String} target The target region key (north, south, east, west or center).
34318      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34319      * @return {Roo.ContentPanel} The removed panel
34320      */
34321     remove : function(target, panel){
34322         target = target.toLowerCase();
34323         return this.regions[target].remove(panel);
34324     },
34325
34326     /**
34327      * Searches all regions for a panel with the specified id
34328      * @param {String} panelId
34329      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34330      */
34331     findPanel : function(panelId){
34332         var rs = this.regions;
34333         for(var target in rs){
34334             if(typeof rs[target] != "function"){
34335                 var p = rs[target].getPanel(panelId);
34336                 if(p){
34337                     return p;
34338                 }
34339             }
34340         }
34341         return null;
34342     },
34343
34344     /**
34345      * Searches all regions for a panel with the specified id and activates (shows) it.
34346      * @param {String/ContentPanel} panelId The panels id or the panel itself
34347      * @return {Roo.ContentPanel} The shown panel or null
34348      */
34349     showPanel : function(panelId) {
34350       var rs = this.regions;
34351       for(var target in rs){
34352          var r = rs[target];
34353          if(typeof r != "function"){
34354             if(r.hasPanel(panelId)){
34355                return r.showPanel(panelId);
34356             }
34357          }
34358       }
34359       return null;
34360    },
34361
34362    /**
34363      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34364      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34365      */
34366    /*
34367     restoreState : function(provider){
34368         if(!provider){
34369             provider = Roo.state.Manager;
34370         }
34371         var sm = new Roo.LayoutStateManager();
34372         sm.init(this, provider);
34373     },
34374 */
34375  
34376  
34377     /**
34378      * Adds a xtype elements to the layout.
34379      * <pre><code>
34380
34381 layout.addxtype({
34382        xtype : 'ContentPanel',
34383        region: 'west',
34384        items: [ .... ]
34385    }
34386 );
34387
34388 layout.addxtype({
34389         xtype : 'NestedLayoutPanel',
34390         region: 'west',
34391         layout: {
34392            center: { },
34393            west: { }   
34394         },
34395         items : [ ... list of content panels or nested layout panels.. ]
34396    }
34397 );
34398 </code></pre>
34399      * @param {Object} cfg Xtype definition of item to add.
34400      */
34401     addxtype : function(cfg)
34402     {
34403         // basically accepts a pannel...
34404         // can accept a layout region..!?!?
34405         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34406         
34407         
34408         // theory?  children can only be panels??
34409         
34410         //if (!cfg.xtype.match(/Panel$/)) {
34411         //    return false;
34412         //}
34413         var ret = false;
34414         
34415         if (typeof(cfg.region) == 'undefined') {
34416             Roo.log("Failed to add Panel, region was not set");
34417             Roo.log(cfg);
34418             return false;
34419         }
34420         var region = cfg.region;
34421         delete cfg.region;
34422         
34423           
34424         var xitems = [];
34425         if (cfg.items) {
34426             xitems = cfg.items;
34427             delete cfg.items;
34428         }
34429         var nb = false;
34430         
34431         switch(cfg.xtype) 
34432         {
34433             case 'Content':  // ContentPanel (el, cfg)
34434             case 'Scroll':  // ContentPanel (el, cfg)
34435             case 'View': 
34436                 cfg.autoCreate = true;
34437                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34438                 //} else {
34439                 //    var el = this.el.createChild();
34440                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34441                 //}
34442                 
34443                 this.add(region, ret);
34444                 break;
34445             
34446             /*
34447             case 'TreePanel': // our new panel!
34448                 cfg.el = this.el.createChild();
34449                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34450                 this.add(region, ret);
34451                 break;
34452             */
34453             
34454             case 'Nest': 
34455                 // create a new Layout (which is  a Border Layout...
34456                 
34457                 var clayout = cfg.layout;
34458                 clayout.el  = this.el.createChild();
34459                 clayout.items   = clayout.items  || [];
34460                 
34461                 delete cfg.layout;
34462                 
34463                 // replace this exitems with the clayout ones..
34464                 xitems = clayout.items;
34465                  
34466                 // force background off if it's in center...
34467                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34468                     cfg.background = false;
34469                 }
34470                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34471                 
34472                 
34473                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34474                 //console.log('adding nested layout panel '  + cfg.toSource());
34475                 this.add(region, ret);
34476                 nb = {}; /// find first...
34477                 break;
34478             
34479             case 'Grid':
34480                 
34481                 // needs grid and region
34482                 
34483                 //var el = this.getRegion(region).el.createChild();
34484                 /*
34485                  *var el = this.el.createChild();
34486                 // create the grid first...
34487                 cfg.grid.container = el;
34488                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34489                 */
34490                 
34491                 if (region == 'center' && this.active ) {
34492                     cfg.background = false;
34493                 }
34494                 
34495                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34496                 
34497                 this.add(region, ret);
34498                 /*
34499                 if (cfg.background) {
34500                     // render grid on panel activation (if panel background)
34501                     ret.on('activate', function(gp) {
34502                         if (!gp.grid.rendered) {
34503                     //        gp.grid.render(el);
34504                         }
34505                     });
34506                 } else {
34507                   //  cfg.grid.render(el);
34508                 }
34509                 */
34510                 break;
34511            
34512            
34513             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34514                 // it was the old xcomponent building that caused this before.
34515                 // espeically if border is the top element in the tree.
34516                 ret = this;
34517                 break; 
34518                 
34519                     
34520                 
34521                 
34522                 
34523             default:
34524                 /*
34525                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34526                     
34527                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34528                     this.add(region, ret);
34529                 } else {
34530                 */
34531                     Roo.log(cfg);
34532                     throw "Can not add '" + cfg.xtype + "' to Border";
34533                     return null;
34534              
34535                                 
34536              
34537         }
34538         this.beginUpdate();
34539         // add children..
34540         var region = '';
34541         var abn = {};
34542         Roo.each(xitems, function(i)  {
34543             region = nb && i.region ? i.region : false;
34544             
34545             var add = ret.addxtype(i);
34546            
34547             if (region) {
34548                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34549                 if (!i.background) {
34550                     abn[region] = nb[region] ;
34551                 }
34552             }
34553             
34554         });
34555         this.endUpdate();
34556
34557         // make the last non-background panel active..
34558         //if (nb) { Roo.log(abn); }
34559         if (nb) {
34560             
34561             for(var r in abn) {
34562                 region = this.getRegion(r);
34563                 if (region) {
34564                     // tried using nb[r], but it does not work..
34565                      
34566                     region.showPanel(abn[r]);
34567                    
34568                 }
34569             }
34570         }
34571         return ret;
34572         
34573     },
34574     
34575     
34576 // private
34577     factory : function(cfg)
34578     {
34579         
34580         var validRegions = Roo.bootstrap.layout.Border.regions;
34581
34582         var target = cfg.region;
34583         cfg.mgr = this;
34584         
34585         var r = Roo.bootstrap.layout;
34586         Roo.log(target);
34587         switch(target){
34588             case "north":
34589                 return new r.North(cfg);
34590             case "south":
34591                 return new r.South(cfg);
34592             case "east":
34593                 return new r.East(cfg);
34594             case "west":
34595                 return new r.West(cfg);
34596             case "center":
34597                 return new r.Center(cfg);
34598         }
34599         throw 'Layout region "'+target+'" not supported.';
34600     }
34601     
34602     
34603 });
34604  /*
34605  * Based on:
34606  * Ext JS Library 1.1.1
34607  * Copyright(c) 2006-2007, Ext JS, LLC.
34608  *
34609  * Originally Released Under LGPL - original licence link has changed is not relivant.
34610  *
34611  * Fork - LGPL
34612  * <script type="text/javascript">
34613  */
34614  
34615 /**
34616  * @class Roo.bootstrap.layout.Basic
34617  * @extends Roo.util.Observable
34618  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34619  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34620  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34621  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34622  * @cfg {string}   region  the region that it inhabits..
34623  * @cfg {bool}   skipConfig skip config?
34624  * 
34625
34626  */
34627 Roo.bootstrap.layout.Basic = function(config){
34628     
34629     this.mgr = config.mgr;
34630     
34631     this.position = config.region;
34632     
34633     var skipConfig = config.skipConfig;
34634     
34635     this.events = {
34636         /**
34637          * @scope Roo.BasicLayoutRegion
34638          */
34639         
34640         /**
34641          * @event beforeremove
34642          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34643          * @param {Roo.LayoutRegion} this
34644          * @param {Roo.ContentPanel} panel The panel
34645          * @param {Object} e The cancel event object
34646          */
34647         "beforeremove" : true,
34648         /**
34649          * @event invalidated
34650          * Fires when the layout for this region is changed.
34651          * @param {Roo.LayoutRegion} this
34652          */
34653         "invalidated" : true,
34654         /**
34655          * @event visibilitychange
34656          * Fires when this region is shown or hidden 
34657          * @param {Roo.LayoutRegion} this
34658          * @param {Boolean} visibility true or false
34659          */
34660         "visibilitychange" : true,
34661         /**
34662          * @event paneladded
34663          * Fires when a panel is added. 
34664          * @param {Roo.LayoutRegion} this
34665          * @param {Roo.ContentPanel} panel The panel
34666          */
34667         "paneladded" : true,
34668         /**
34669          * @event panelremoved
34670          * Fires when a panel is removed. 
34671          * @param {Roo.LayoutRegion} this
34672          * @param {Roo.ContentPanel} panel The panel
34673          */
34674         "panelremoved" : true,
34675         /**
34676          * @event beforecollapse
34677          * Fires when this region before collapse.
34678          * @param {Roo.LayoutRegion} this
34679          */
34680         "beforecollapse" : true,
34681         /**
34682          * @event collapsed
34683          * Fires when this region is collapsed.
34684          * @param {Roo.LayoutRegion} this
34685          */
34686         "collapsed" : true,
34687         /**
34688          * @event expanded
34689          * Fires when this region is expanded.
34690          * @param {Roo.LayoutRegion} this
34691          */
34692         "expanded" : true,
34693         /**
34694          * @event slideshow
34695          * Fires when this region is slid into view.
34696          * @param {Roo.LayoutRegion} this
34697          */
34698         "slideshow" : true,
34699         /**
34700          * @event slidehide
34701          * Fires when this region slides out of view. 
34702          * @param {Roo.LayoutRegion} this
34703          */
34704         "slidehide" : true,
34705         /**
34706          * @event panelactivated
34707          * Fires when a panel is activated. 
34708          * @param {Roo.LayoutRegion} this
34709          * @param {Roo.ContentPanel} panel The activated panel
34710          */
34711         "panelactivated" : true,
34712         /**
34713          * @event resized
34714          * Fires when the user resizes this region. 
34715          * @param {Roo.LayoutRegion} this
34716          * @param {Number} newSize The new size (width for east/west, height for north/south)
34717          */
34718         "resized" : true
34719     };
34720     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34721     this.panels = new Roo.util.MixedCollection();
34722     this.panels.getKey = this.getPanelId.createDelegate(this);
34723     this.box = null;
34724     this.activePanel = null;
34725     // ensure listeners are added...
34726     
34727     if (config.listeners || config.events) {
34728         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34729             listeners : config.listeners || {},
34730             events : config.events || {}
34731         });
34732     }
34733     
34734     if(skipConfig !== true){
34735         this.applyConfig(config);
34736     }
34737 };
34738
34739 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34740 {
34741     getPanelId : function(p){
34742         return p.getId();
34743     },
34744     
34745     applyConfig : function(config){
34746         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34747         this.config = config;
34748         
34749     },
34750     
34751     /**
34752      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34753      * the width, for horizontal (north, south) the height.
34754      * @param {Number} newSize The new width or height
34755      */
34756     resizeTo : function(newSize){
34757         var el = this.el ? this.el :
34758                  (this.activePanel ? this.activePanel.getEl() : null);
34759         if(el){
34760             switch(this.position){
34761                 case "east":
34762                 case "west":
34763                     el.setWidth(newSize);
34764                     this.fireEvent("resized", this, newSize);
34765                 break;
34766                 case "north":
34767                 case "south":
34768                     el.setHeight(newSize);
34769                     this.fireEvent("resized", this, newSize);
34770                 break;                
34771             }
34772         }
34773     },
34774     
34775     getBox : function(){
34776         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34777     },
34778     
34779     getMargins : function(){
34780         return this.margins;
34781     },
34782     
34783     updateBox : function(box){
34784         this.box = box;
34785         var el = this.activePanel.getEl();
34786         el.dom.style.left = box.x + "px";
34787         el.dom.style.top = box.y + "px";
34788         this.activePanel.setSize(box.width, box.height);
34789     },
34790     
34791     /**
34792      * Returns the container element for this region.
34793      * @return {Roo.Element}
34794      */
34795     getEl : function(){
34796         return this.activePanel;
34797     },
34798     
34799     /**
34800      * Returns true if this region is currently visible.
34801      * @return {Boolean}
34802      */
34803     isVisible : function(){
34804         return this.activePanel ? true : false;
34805     },
34806     
34807     setActivePanel : function(panel){
34808         panel = this.getPanel(panel);
34809         if(this.activePanel && this.activePanel != panel){
34810             this.activePanel.setActiveState(false);
34811             this.activePanel.getEl().setLeftTop(-10000,-10000);
34812         }
34813         this.activePanel = panel;
34814         panel.setActiveState(true);
34815         if(this.box){
34816             panel.setSize(this.box.width, this.box.height);
34817         }
34818         this.fireEvent("panelactivated", this, panel);
34819         this.fireEvent("invalidated");
34820     },
34821     
34822     /**
34823      * Show the specified panel.
34824      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34825      * @return {Roo.ContentPanel} The shown panel or null
34826      */
34827     showPanel : function(panel){
34828         panel = this.getPanel(panel);
34829         if(panel){
34830             this.setActivePanel(panel);
34831         }
34832         return panel;
34833     },
34834     
34835     /**
34836      * Get the active panel for this region.
34837      * @return {Roo.ContentPanel} The active panel or null
34838      */
34839     getActivePanel : function(){
34840         return this.activePanel;
34841     },
34842     
34843     /**
34844      * Add the passed ContentPanel(s)
34845      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34846      * @return {Roo.ContentPanel} The panel added (if only one was added)
34847      */
34848     add : function(panel){
34849         if(arguments.length > 1){
34850             for(var i = 0, len = arguments.length; i < len; i++) {
34851                 this.add(arguments[i]);
34852             }
34853             return null;
34854         }
34855         if(this.hasPanel(panel)){
34856             this.showPanel(panel);
34857             return panel;
34858         }
34859         var el = panel.getEl();
34860         if(el.dom.parentNode != this.mgr.el.dom){
34861             this.mgr.el.dom.appendChild(el.dom);
34862         }
34863         if(panel.setRegion){
34864             panel.setRegion(this);
34865         }
34866         this.panels.add(panel);
34867         el.setStyle("position", "absolute");
34868         if(!panel.background){
34869             this.setActivePanel(panel);
34870             if(this.config.initialSize && this.panels.getCount()==1){
34871                 this.resizeTo(this.config.initialSize);
34872             }
34873         }
34874         this.fireEvent("paneladded", this, panel);
34875         return panel;
34876     },
34877     
34878     /**
34879      * Returns true if the panel is in this region.
34880      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34881      * @return {Boolean}
34882      */
34883     hasPanel : function(panel){
34884         if(typeof panel == "object"){ // must be panel obj
34885             panel = panel.getId();
34886         }
34887         return this.getPanel(panel) ? true : false;
34888     },
34889     
34890     /**
34891      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34892      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34893      * @param {Boolean} preservePanel Overrides the config preservePanel option
34894      * @return {Roo.ContentPanel} The panel that was removed
34895      */
34896     remove : function(panel, preservePanel){
34897         panel = this.getPanel(panel);
34898         if(!panel){
34899             return null;
34900         }
34901         var e = {};
34902         this.fireEvent("beforeremove", this, panel, e);
34903         if(e.cancel === true){
34904             return null;
34905         }
34906         var panelId = panel.getId();
34907         this.panels.removeKey(panelId);
34908         return panel;
34909     },
34910     
34911     /**
34912      * Returns the panel specified or null if it's not in this region.
34913      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34914      * @return {Roo.ContentPanel}
34915      */
34916     getPanel : function(id){
34917         if(typeof id == "object"){ // must be panel obj
34918             return id;
34919         }
34920         return this.panels.get(id);
34921     },
34922     
34923     /**
34924      * Returns this regions position (north/south/east/west/center).
34925      * @return {String} 
34926      */
34927     getPosition: function(){
34928         return this.position;    
34929     }
34930 });/*
34931  * Based on:
34932  * Ext JS Library 1.1.1
34933  * Copyright(c) 2006-2007, Ext JS, LLC.
34934  *
34935  * Originally Released Under LGPL - original licence link has changed is not relivant.
34936  *
34937  * Fork - LGPL
34938  * <script type="text/javascript">
34939  */
34940  
34941 /**
34942  * @class Roo.bootstrap.layout.Region
34943  * @extends Roo.bootstrap.layout.Basic
34944  * This class represents a region in a layout manager.
34945  
34946  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34947  * @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})
34948  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34949  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34950  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34951  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34952  * @cfg {String}    title           The title for the region (overrides panel titles)
34953  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34954  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34955  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34956  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34957  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34958  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34959  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34960  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34961  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34962  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34963
34964  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34965  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34966  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34967  * @cfg {Number}    width           For East/West panels
34968  * @cfg {Number}    height          For North/South panels
34969  * @cfg {Boolean}   split           To show the splitter
34970  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34971  * 
34972  * @cfg {string}   cls             Extra CSS classes to add to region
34973  * 
34974  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34975  * @cfg {string}   region  the region that it inhabits..
34976  *
34977
34978  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34979  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34980
34981  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34982  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34983  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34984  */
34985 Roo.bootstrap.layout.Region = function(config)
34986 {
34987     this.applyConfig(config);
34988
34989     var mgr = config.mgr;
34990     var pos = config.region;
34991     config.skipConfig = true;
34992     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34993     
34994     if (mgr.el) {
34995         this.onRender(mgr.el);   
34996     }
34997      
34998     this.visible = true;
34999     this.collapsed = false;
35000     this.unrendered_panels = [];
35001 };
35002
35003 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35004
35005     position: '', // set by wrapper (eg. north/south etc..)
35006     unrendered_panels : null,  // unrendered panels.
35007     createBody : function(){
35008         /** This region's body element 
35009         * @type Roo.Element */
35010         this.bodyEl = this.el.createChild({
35011                 tag: "div",
35012                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35013         });
35014     },
35015
35016     onRender: function(ctr, pos)
35017     {
35018         var dh = Roo.DomHelper;
35019         /** This region's container element 
35020         * @type Roo.Element */
35021         this.el = dh.append(ctr.dom, {
35022                 tag: "div",
35023                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35024             }, true);
35025         /** This region's title element 
35026         * @type Roo.Element */
35027     
35028         this.titleEl = dh.append(this.el.dom,
35029             {
35030                     tag: "div",
35031                     unselectable: "on",
35032                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35033                     children:[
35034                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35035                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35036                     ]}, true);
35037         
35038         this.titleEl.enableDisplayMode();
35039         /** This region's title text element 
35040         * @type HTMLElement */
35041         this.titleTextEl = this.titleEl.dom.firstChild;
35042         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35043         /*
35044         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35045         this.closeBtn.enableDisplayMode();
35046         this.closeBtn.on("click", this.closeClicked, this);
35047         this.closeBtn.hide();
35048     */
35049         this.createBody(this.config);
35050         if(this.config.hideWhenEmpty){
35051             this.hide();
35052             this.on("paneladded", this.validateVisibility, this);
35053             this.on("panelremoved", this.validateVisibility, this);
35054         }
35055         if(this.autoScroll){
35056             this.bodyEl.setStyle("overflow", "auto");
35057         }else{
35058             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35059         }
35060         //if(c.titlebar !== false){
35061             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35062                 this.titleEl.hide();
35063             }else{
35064                 this.titleEl.show();
35065                 if(this.config.title){
35066                     this.titleTextEl.innerHTML = this.config.title;
35067                 }
35068             }
35069         //}
35070         if(this.config.collapsed){
35071             this.collapse(true);
35072         }
35073         if(this.config.hidden){
35074             this.hide();
35075         }
35076         
35077         if (this.unrendered_panels && this.unrendered_panels.length) {
35078             for (var i =0;i< this.unrendered_panels.length; i++) {
35079                 this.add(this.unrendered_panels[i]);
35080             }
35081             this.unrendered_panels = null;
35082             
35083         }
35084         
35085     },
35086     
35087     applyConfig : function(c)
35088     {
35089         /*
35090          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35091             var dh = Roo.DomHelper;
35092             if(c.titlebar !== false){
35093                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35094                 this.collapseBtn.on("click", this.collapse, this);
35095                 this.collapseBtn.enableDisplayMode();
35096                 /*
35097                 if(c.showPin === true || this.showPin){
35098                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35099                     this.stickBtn.enableDisplayMode();
35100                     this.stickBtn.on("click", this.expand, this);
35101                     this.stickBtn.hide();
35102                 }
35103                 
35104             }
35105             */
35106             /** This region's collapsed element
35107             * @type Roo.Element */
35108             /*
35109              *
35110             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35111                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35112             ]}, true);
35113             
35114             if(c.floatable !== false){
35115                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35116                this.collapsedEl.on("click", this.collapseClick, this);
35117             }
35118
35119             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35120                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35121                    id: "message", unselectable: "on", style:{"float":"left"}});
35122                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35123              }
35124             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35125             this.expandBtn.on("click", this.expand, this);
35126             
35127         }
35128         
35129         if(this.collapseBtn){
35130             this.collapseBtn.setVisible(c.collapsible == true);
35131         }
35132         
35133         this.cmargins = c.cmargins || this.cmargins ||
35134                          (this.position == "west" || this.position == "east" ?
35135                              {top: 0, left: 2, right:2, bottom: 0} :
35136                              {top: 2, left: 0, right:0, bottom: 2});
35137         */
35138         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35139         
35140         
35141         this.bottomTabs = c.tabPosition != "top";
35142         
35143         this.autoScroll = c.autoScroll || false;
35144         
35145         
35146        
35147         
35148         this.duration = c.duration || .30;
35149         this.slideDuration = c.slideDuration || .45;
35150         this.config = c;
35151        
35152     },
35153     /**
35154      * Returns true if this region is currently visible.
35155      * @return {Boolean}
35156      */
35157     isVisible : function(){
35158         return this.visible;
35159     },
35160
35161     /**
35162      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35163      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35164      */
35165     //setCollapsedTitle : function(title){
35166     //    title = title || "&#160;";
35167      //   if(this.collapsedTitleTextEl){
35168       //      this.collapsedTitleTextEl.innerHTML = title;
35169        // }
35170     //},
35171
35172     getBox : function(){
35173         var b;
35174       //  if(!this.collapsed){
35175             b = this.el.getBox(false, true);
35176        // }else{
35177           //  b = this.collapsedEl.getBox(false, true);
35178         //}
35179         return b;
35180     },
35181
35182     getMargins : function(){
35183         return this.margins;
35184         //return this.collapsed ? this.cmargins : this.margins;
35185     },
35186 /*
35187     highlight : function(){
35188         this.el.addClass("x-layout-panel-dragover");
35189     },
35190
35191     unhighlight : function(){
35192         this.el.removeClass("x-layout-panel-dragover");
35193     },
35194 */
35195     updateBox : function(box)
35196     {
35197         if (!this.bodyEl) {
35198             return; // not rendered yet..
35199         }
35200         
35201         this.box = box;
35202         if(!this.collapsed){
35203             this.el.dom.style.left = box.x + "px";
35204             this.el.dom.style.top = box.y + "px";
35205             this.updateBody(box.width, box.height);
35206         }else{
35207             this.collapsedEl.dom.style.left = box.x + "px";
35208             this.collapsedEl.dom.style.top = box.y + "px";
35209             this.collapsedEl.setSize(box.width, box.height);
35210         }
35211         if(this.tabs){
35212             this.tabs.autoSizeTabs();
35213         }
35214     },
35215
35216     updateBody : function(w, h)
35217     {
35218         if(w !== null){
35219             this.el.setWidth(w);
35220             w -= this.el.getBorderWidth("rl");
35221             if(this.config.adjustments){
35222                 w += this.config.adjustments[0];
35223             }
35224         }
35225         if(h !== null && h > 0){
35226             this.el.setHeight(h);
35227             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35228             h -= this.el.getBorderWidth("tb");
35229             if(this.config.adjustments){
35230                 h += this.config.adjustments[1];
35231             }
35232             this.bodyEl.setHeight(h);
35233             if(this.tabs){
35234                 h = this.tabs.syncHeight(h);
35235             }
35236         }
35237         if(this.panelSize){
35238             w = w !== null ? w : this.panelSize.width;
35239             h = h !== null ? h : this.panelSize.height;
35240         }
35241         if(this.activePanel){
35242             var el = this.activePanel.getEl();
35243             w = w !== null ? w : el.getWidth();
35244             h = h !== null ? h : el.getHeight();
35245             this.panelSize = {width: w, height: h};
35246             this.activePanel.setSize(w, h);
35247         }
35248         if(Roo.isIE && this.tabs){
35249             this.tabs.el.repaint();
35250         }
35251     },
35252
35253     /**
35254      * Returns the container element for this region.
35255      * @return {Roo.Element}
35256      */
35257     getEl : function(){
35258         return this.el;
35259     },
35260
35261     /**
35262      * Hides this region.
35263      */
35264     hide : function(){
35265         //if(!this.collapsed){
35266             this.el.dom.style.left = "-2000px";
35267             this.el.hide();
35268         //}else{
35269          //   this.collapsedEl.dom.style.left = "-2000px";
35270          //   this.collapsedEl.hide();
35271        // }
35272         this.visible = false;
35273         this.fireEvent("visibilitychange", this, false);
35274     },
35275
35276     /**
35277      * Shows this region if it was previously hidden.
35278      */
35279     show : function(){
35280         //if(!this.collapsed){
35281             this.el.show();
35282         //}else{
35283         //    this.collapsedEl.show();
35284        // }
35285         this.visible = true;
35286         this.fireEvent("visibilitychange", this, true);
35287     },
35288 /*
35289     closeClicked : function(){
35290         if(this.activePanel){
35291             this.remove(this.activePanel);
35292         }
35293     },
35294
35295     collapseClick : function(e){
35296         if(this.isSlid){
35297            e.stopPropagation();
35298            this.slideIn();
35299         }else{
35300            e.stopPropagation();
35301            this.slideOut();
35302         }
35303     },
35304 */
35305     /**
35306      * Collapses this region.
35307      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35308      */
35309     /*
35310     collapse : function(skipAnim, skipCheck = false){
35311         if(this.collapsed) {
35312             return;
35313         }
35314         
35315         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35316             
35317             this.collapsed = true;
35318             if(this.split){
35319                 this.split.el.hide();
35320             }
35321             if(this.config.animate && skipAnim !== true){
35322                 this.fireEvent("invalidated", this);
35323                 this.animateCollapse();
35324             }else{
35325                 this.el.setLocation(-20000,-20000);
35326                 this.el.hide();
35327                 this.collapsedEl.show();
35328                 this.fireEvent("collapsed", this);
35329                 this.fireEvent("invalidated", this);
35330             }
35331         }
35332         
35333     },
35334 */
35335     animateCollapse : function(){
35336         // overridden
35337     },
35338
35339     /**
35340      * Expands this region if it was previously collapsed.
35341      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35342      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35343      */
35344     /*
35345     expand : function(e, skipAnim){
35346         if(e) {
35347             e.stopPropagation();
35348         }
35349         if(!this.collapsed || this.el.hasActiveFx()) {
35350             return;
35351         }
35352         if(this.isSlid){
35353             this.afterSlideIn();
35354             skipAnim = true;
35355         }
35356         this.collapsed = false;
35357         if(this.config.animate && skipAnim !== true){
35358             this.animateExpand();
35359         }else{
35360             this.el.show();
35361             if(this.split){
35362                 this.split.el.show();
35363             }
35364             this.collapsedEl.setLocation(-2000,-2000);
35365             this.collapsedEl.hide();
35366             this.fireEvent("invalidated", this);
35367             this.fireEvent("expanded", this);
35368         }
35369     },
35370 */
35371     animateExpand : function(){
35372         // overridden
35373     },
35374
35375     initTabs : function()
35376     {
35377         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35378         
35379         var ts = new Roo.bootstrap.panel.Tabs({
35380                 el: this.bodyEl.dom,
35381                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35382                 disableTooltips: this.config.disableTabTips,
35383                 toolbar : this.config.toolbar
35384             });
35385         
35386         if(this.config.hideTabs){
35387             ts.stripWrap.setDisplayed(false);
35388         }
35389         this.tabs = ts;
35390         ts.resizeTabs = this.config.resizeTabs === true;
35391         ts.minTabWidth = this.config.minTabWidth || 40;
35392         ts.maxTabWidth = this.config.maxTabWidth || 250;
35393         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35394         ts.monitorResize = false;
35395         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35396         ts.bodyEl.addClass('roo-layout-tabs-body');
35397         this.panels.each(this.initPanelAsTab, this);
35398     },
35399
35400     initPanelAsTab : function(panel){
35401         var ti = this.tabs.addTab(
35402             panel.getEl().id,
35403             panel.getTitle(),
35404             null,
35405             this.config.closeOnTab && panel.isClosable(),
35406             panel.tpl
35407         );
35408         if(panel.tabTip !== undefined){
35409             ti.setTooltip(panel.tabTip);
35410         }
35411         ti.on("activate", function(){
35412               this.setActivePanel(panel);
35413         }, this);
35414         
35415         if(this.config.closeOnTab){
35416             ti.on("beforeclose", function(t, e){
35417                 e.cancel = true;
35418                 this.remove(panel);
35419             }, this);
35420         }
35421         
35422         panel.tabItem = ti;
35423         
35424         return ti;
35425     },
35426
35427     updatePanelTitle : function(panel, title)
35428     {
35429         if(this.activePanel == panel){
35430             this.updateTitle(title);
35431         }
35432         if(this.tabs){
35433             var ti = this.tabs.getTab(panel.getEl().id);
35434             ti.setText(title);
35435             if(panel.tabTip !== undefined){
35436                 ti.setTooltip(panel.tabTip);
35437             }
35438         }
35439     },
35440
35441     updateTitle : function(title){
35442         if(this.titleTextEl && !this.config.title){
35443             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35444         }
35445     },
35446
35447     setActivePanel : function(panel)
35448     {
35449         panel = this.getPanel(panel);
35450         if(this.activePanel && this.activePanel != panel){
35451             this.activePanel.setActiveState(false);
35452         }
35453         this.activePanel = panel;
35454         panel.setActiveState(true);
35455         if(this.panelSize){
35456             panel.setSize(this.panelSize.width, this.panelSize.height);
35457         }
35458         if(this.closeBtn){
35459             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35460         }
35461         this.updateTitle(panel.getTitle());
35462         if(this.tabs){
35463             this.fireEvent("invalidated", this);
35464         }
35465         this.fireEvent("panelactivated", this, panel);
35466     },
35467
35468     /**
35469      * Shows the specified panel.
35470      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35471      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35472      */
35473     showPanel : function(panel)
35474     {
35475         panel = this.getPanel(panel);
35476         if(panel){
35477             if(this.tabs){
35478                 var tab = this.tabs.getTab(panel.getEl().id);
35479                 if(tab.isHidden()){
35480                     this.tabs.unhideTab(tab.id);
35481                 }
35482                 tab.activate();
35483             }else{
35484                 this.setActivePanel(panel);
35485             }
35486         }
35487         return panel;
35488     },
35489
35490     /**
35491      * Get the active panel for this region.
35492      * @return {Roo.ContentPanel} The active panel or null
35493      */
35494     getActivePanel : function(){
35495         return this.activePanel;
35496     },
35497
35498     validateVisibility : function(){
35499         if(this.panels.getCount() < 1){
35500             this.updateTitle("&#160;");
35501             this.closeBtn.hide();
35502             this.hide();
35503         }else{
35504             if(!this.isVisible()){
35505                 this.show();
35506             }
35507         }
35508     },
35509
35510     /**
35511      * Adds the passed ContentPanel(s) to this region.
35512      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35513      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35514      */
35515     add : function(panel)
35516     {
35517         if(arguments.length > 1){
35518             for(var i = 0, len = arguments.length; i < len; i++) {
35519                 this.add(arguments[i]);
35520             }
35521             return null;
35522         }
35523         
35524         // if we have not been rendered yet, then we can not really do much of this..
35525         if (!this.bodyEl) {
35526             this.unrendered_panels.push(panel);
35527             return panel;
35528         }
35529         
35530         
35531         
35532         
35533         if(this.hasPanel(panel)){
35534             this.showPanel(panel);
35535             return panel;
35536         }
35537         panel.setRegion(this);
35538         this.panels.add(panel);
35539        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35540             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35541             // and hide them... ???
35542             this.bodyEl.dom.appendChild(panel.getEl().dom);
35543             if(panel.background !== true){
35544                 this.setActivePanel(panel);
35545             }
35546             this.fireEvent("paneladded", this, panel);
35547             return panel;
35548         }
35549         */
35550         if(!this.tabs){
35551             this.initTabs();
35552         }else{
35553             this.initPanelAsTab(panel);
35554         }
35555         
35556         
35557         if(panel.background !== true){
35558             this.tabs.activate(panel.getEl().id);
35559         }
35560         this.fireEvent("paneladded", this, panel);
35561         return panel;
35562     },
35563
35564     /**
35565      * Hides the tab for the specified panel.
35566      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35567      */
35568     hidePanel : function(panel){
35569         if(this.tabs && (panel = this.getPanel(panel))){
35570             this.tabs.hideTab(panel.getEl().id);
35571         }
35572     },
35573
35574     /**
35575      * Unhides the tab for a previously hidden panel.
35576      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35577      */
35578     unhidePanel : function(panel){
35579         if(this.tabs && (panel = this.getPanel(panel))){
35580             this.tabs.unhideTab(panel.getEl().id);
35581         }
35582     },
35583
35584     clearPanels : function(){
35585         while(this.panels.getCount() > 0){
35586              this.remove(this.panels.first());
35587         }
35588     },
35589
35590     /**
35591      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35593      * @param {Boolean} preservePanel Overrides the config preservePanel option
35594      * @return {Roo.ContentPanel} The panel that was removed
35595      */
35596     remove : function(panel, preservePanel)
35597     {
35598         panel = this.getPanel(panel);
35599         if(!panel){
35600             return null;
35601         }
35602         var e = {};
35603         this.fireEvent("beforeremove", this, panel, e);
35604         if(e.cancel === true){
35605             return null;
35606         }
35607         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35608         var panelId = panel.getId();
35609         this.panels.removeKey(panelId);
35610         if(preservePanel){
35611             document.body.appendChild(panel.getEl().dom);
35612         }
35613         if(this.tabs){
35614             this.tabs.removeTab(panel.getEl().id);
35615         }else if (!preservePanel){
35616             this.bodyEl.dom.removeChild(panel.getEl().dom);
35617         }
35618         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35619             var p = this.panels.first();
35620             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35621             tempEl.appendChild(p.getEl().dom);
35622             this.bodyEl.update("");
35623             this.bodyEl.dom.appendChild(p.getEl().dom);
35624             tempEl = null;
35625             this.updateTitle(p.getTitle());
35626             this.tabs = null;
35627             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35628             this.setActivePanel(p);
35629         }
35630         panel.setRegion(null);
35631         if(this.activePanel == panel){
35632             this.activePanel = null;
35633         }
35634         if(this.config.autoDestroy !== false && preservePanel !== true){
35635             try{panel.destroy();}catch(e){}
35636         }
35637         this.fireEvent("panelremoved", this, panel);
35638         return panel;
35639     },
35640
35641     /**
35642      * Returns the TabPanel component used by this region
35643      * @return {Roo.TabPanel}
35644      */
35645     getTabs : function(){
35646         return this.tabs;
35647     },
35648
35649     createTool : function(parentEl, className){
35650         var btn = Roo.DomHelper.append(parentEl, {
35651             tag: "div",
35652             cls: "x-layout-tools-button",
35653             children: [ {
35654                 tag: "div",
35655                 cls: "roo-layout-tools-button-inner " + className,
35656                 html: "&#160;"
35657             }]
35658         }, true);
35659         btn.addClassOnOver("roo-layout-tools-button-over");
35660         return btn;
35661     }
35662 });/*
35663  * Based on:
35664  * Ext JS Library 1.1.1
35665  * Copyright(c) 2006-2007, Ext JS, LLC.
35666  *
35667  * Originally Released Under LGPL - original licence link has changed is not relivant.
35668  *
35669  * Fork - LGPL
35670  * <script type="text/javascript">
35671  */
35672  
35673
35674
35675 /**
35676  * @class Roo.SplitLayoutRegion
35677  * @extends Roo.LayoutRegion
35678  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35679  */
35680 Roo.bootstrap.layout.Split = function(config){
35681     this.cursor = config.cursor;
35682     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35683 };
35684
35685 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35686 {
35687     splitTip : "Drag to resize.",
35688     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35689     useSplitTips : false,
35690
35691     applyConfig : function(config){
35692         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35693     },
35694     
35695     onRender : function(ctr,pos) {
35696         
35697         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35698         if(!this.config.split){
35699             return;
35700         }
35701         if(!this.split){
35702             
35703             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35704                             tag: "div",
35705                             id: this.el.id + "-split",
35706                             cls: "roo-layout-split roo-layout-split-"+this.position,
35707                             html: "&#160;"
35708             });
35709             /** The SplitBar for this region 
35710             * @type Roo.SplitBar */
35711             // does not exist yet...
35712             Roo.log([this.position, this.orientation]);
35713             
35714             this.split = new Roo.bootstrap.SplitBar({
35715                 dragElement : splitEl,
35716                 resizingElement: this.el,
35717                 orientation : this.orientation
35718             });
35719             
35720             this.split.on("moved", this.onSplitMove, this);
35721             this.split.useShim = this.config.useShim === true;
35722             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35723             if(this.useSplitTips){
35724                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35725             }
35726             //if(config.collapsible){
35727             //    this.split.el.on("dblclick", this.collapse,  this);
35728             //}
35729         }
35730         if(typeof this.config.minSize != "undefined"){
35731             this.split.minSize = this.config.minSize;
35732         }
35733         if(typeof this.config.maxSize != "undefined"){
35734             this.split.maxSize = this.config.maxSize;
35735         }
35736         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35737             this.hideSplitter();
35738         }
35739         
35740     },
35741
35742     getHMaxSize : function(){
35743          var cmax = this.config.maxSize || 10000;
35744          var center = this.mgr.getRegion("center");
35745          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35746     },
35747
35748     getVMaxSize : function(){
35749          var cmax = this.config.maxSize || 10000;
35750          var center = this.mgr.getRegion("center");
35751          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35752     },
35753
35754     onSplitMove : function(split, newSize){
35755         this.fireEvent("resized", this, newSize);
35756     },
35757     
35758     /** 
35759      * Returns the {@link Roo.SplitBar} for this region.
35760      * @return {Roo.SplitBar}
35761      */
35762     getSplitBar : function(){
35763         return this.split;
35764     },
35765     
35766     hide : function(){
35767         this.hideSplitter();
35768         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35769     },
35770
35771     hideSplitter : function(){
35772         if(this.split){
35773             this.split.el.setLocation(-2000,-2000);
35774             this.split.el.hide();
35775         }
35776     },
35777
35778     show : function(){
35779         if(this.split){
35780             this.split.el.show();
35781         }
35782         Roo.bootstrap.layout.Split.superclass.show.call(this);
35783     },
35784     
35785     beforeSlide: function(){
35786         if(Roo.isGecko){// firefox overflow auto bug workaround
35787             this.bodyEl.clip();
35788             if(this.tabs) {
35789                 this.tabs.bodyEl.clip();
35790             }
35791             if(this.activePanel){
35792                 this.activePanel.getEl().clip();
35793                 
35794                 if(this.activePanel.beforeSlide){
35795                     this.activePanel.beforeSlide();
35796                 }
35797             }
35798         }
35799     },
35800     
35801     afterSlide : function(){
35802         if(Roo.isGecko){// firefox overflow auto bug workaround
35803             this.bodyEl.unclip();
35804             if(this.tabs) {
35805                 this.tabs.bodyEl.unclip();
35806             }
35807             if(this.activePanel){
35808                 this.activePanel.getEl().unclip();
35809                 if(this.activePanel.afterSlide){
35810                     this.activePanel.afterSlide();
35811                 }
35812             }
35813         }
35814     },
35815
35816     initAutoHide : function(){
35817         if(this.autoHide !== false){
35818             if(!this.autoHideHd){
35819                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35820                 this.autoHideHd = {
35821                     "mouseout": function(e){
35822                         if(!e.within(this.el, true)){
35823                             st.delay(500);
35824                         }
35825                     },
35826                     "mouseover" : function(e){
35827                         st.cancel();
35828                     },
35829                     scope : this
35830                 };
35831             }
35832             this.el.on(this.autoHideHd);
35833         }
35834     },
35835
35836     clearAutoHide : function(){
35837         if(this.autoHide !== false){
35838             this.el.un("mouseout", this.autoHideHd.mouseout);
35839             this.el.un("mouseover", this.autoHideHd.mouseover);
35840         }
35841     },
35842
35843     clearMonitor : function(){
35844         Roo.get(document).un("click", this.slideInIf, this);
35845     },
35846
35847     // these names are backwards but not changed for compat
35848     slideOut : function(){
35849         if(this.isSlid || this.el.hasActiveFx()){
35850             return;
35851         }
35852         this.isSlid = true;
35853         if(this.collapseBtn){
35854             this.collapseBtn.hide();
35855         }
35856         this.closeBtnState = this.closeBtn.getStyle('display');
35857         this.closeBtn.hide();
35858         if(this.stickBtn){
35859             this.stickBtn.show();
35860         }
35861         this.el.show();
35862         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35863         this.beforeSlide();
35864         this.el.setStyle("z-index", 10001);
35865         this.el.slideIn(this.getSlideAnchor(), {
35866             callback: function(){
35867                 this.afterSlide();
35868                 this.initAutoHide();
35869                 Roo.get(document).on("click", this.slideInIf, this);
35870                 this.fireEvent("slideshow", this);
35871             },
35872             scope: this,
35873             block: true
35874         });
35875     },
35876
35877     afterSlideIn : function(){
35878         this.clearAutoHide();
35879         this.isSlid = false;
35880         this.clearMonitor();
35881         this.el.setStyle("z-index", "");
35882         if(this.collapseBtn){
35883             this.collapseBtn.show();
35884         }
35885         this.closeBtn.setStyle('display', this.closeBtnState);
35886         if(this.stickBtn){
35887             this.stickBtn.hide();
35888         }
35889         this.fireEvent("slidehide", this);
35890     },
35891
35892     slideIn : function(cb){
35893         if(!this.isSlid || this.el.hasActiveFx()){
35894             Roo.callback(cb);
35895             return;
35896         }
35897         this.isSlid = false;
35898         this.beforeSlide();
35899         this.el.slideOut(this.getSlideAnchor(), {
35900             callback: function(){
35901                 this.el.setLeftTop(-10000, -10000);
35902                 this.afterSlide();
35903                 this.afterSlideIn();
35904                 Roo.callback(cb);
35905             },
35906             scope: this,
35907             block: true
35908         });
35909     },
35910     
35911     slideInIf : function(e){
35912         if(!e.within(this.el)){
35913             this.slideIn();
35914         }
35915     },
35916
35917     animateCollapse : function(){
35918         this.beforeSlide();
35919         this.el.setStyle("z-index", 20000);
35920         var anchor = this.getSlideAnchor();
35921         this.el.slideOut(anchor, {
35922             callback : function(){
35923                 this.el.setStyle("z-index", "");
35924                 this.collapsedEl.slideIn(anchor, {duration:.3});
35925                 this.afterSlide();
35926                 this.el.setLocation(-10000,-10000);
35927                 this.el.hide();
35928                 this.fireEvent("collapsed", this);
35929             },
35930             scope: this,
35931             block: true
35932         });
35933     },
35934
35935     animateExpand : function(){
35936         this.beforeSlide();
35937         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35938         this.el.setStyle("z-index", 20000);
35939         this.collapsedEl.hide({
35940             duration:.1
35941         });
35942         this.el.slideIn(this.getSlideAnchor(), {
35943             callback : function(){
35944                 this.el.setStyle("z-index", "");
35945                 this.afterSlide();
35946                 if(this.split){
35947                     this.split.el.show();
35948                 }
35949                 this.fireEvent("invalidated", this);
35950                 this.fireEvent("expanded", this);
35951             },
35952             scope: this,
35953             block: true
35954         });
35955     },
35956
35957     anchors : {
35958         "west" : "left",
35959         "east" : "right",
35960         "north" : "top",
35961         "south" : "bottom"
35962     },
35963
35964     sanchors : {
35965         "west" : "l",
35966         "east" : "r",
35967         "north" : "t",
35968         "south" : "b"
35969     },
35970
35971     canchors : {
35972         "west" : "tl-tr",
35973         "east" : "tr-tl",
35974         "north" : "tl-bl",
35975         "south" : "bl-tl"
35976     },
35977
35978     getAnchor : function(){
35979         return this.anchors[this.position];
35980     },
35981
35982     getCollapseAnchor : function(){
35983         return this.canchors[this.position];
35984     },
35985
35986     getSlideAnchor : function(){
35987         return this.sanchors[this.position];
35988     },
35989
35990     getAlignAdj : function(){
35991         var cm = this.cmargins;
35992         switch(this.position){
35993             case "west":
35994                 return [0, 0];
35995             break;
35996             case "east":
35997                 return [0, 0];
35998             break;
35999             case "north":
36000                 return [0, 0];
36001             break;
36002             case "south":
36003                 return [0, 0];
36004             break;
36005         }
36006     },
36007
36008     getExpandAdj : function(){
36009         var c = this.collapsedEl, cm = this.cmargins;
36010         switch(this.position){
36011             case "west":
36012                 return [-(cm.right+c.getWidth()+cm.left), 0];
36013             break;
36014             case "east":
36015                 return [cm.right+c.getWidth()+cm.left, 0];
36016             break;
36017             case "north":
36018                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36019             break;
36020             case "south":
36021                 return [0, cm.top+cm.bottom+c.getHeight()];
36022             break;
36023         }
36024     }
36025 });/*
36026  * Based on:
36027  * Ext JS Library 1.1.1
36028  * Copyright(c) 2006-2007, Ext JS, LLC.
36029  *
36030  * Originally Released Under LGPL - original licence link has changed is not relivant.
36031  *
36032  * Fork - LGPL
36033  * <script type="text/javascript">
36034  */
36035 /*
36036  * These classes are private internal classes
36037  */
36038 Roo.bootstrap.layout.Center = function(config){
36039     config.region = "center";
36040     Roo.bootstrap.layout.Region.call(this, config);
36041     this.visible = true;
36042     this.minWidth = config.minWidth || 20;
36043     this.minHeight = config.minHeight || 20;
36044 };
36045
36046 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36047     hide : function(){
36048         // center panel can't be hidden
36049     },
36050     
36051     show : function(){
36052         // center panel can't be hidden
36053     },
36054     
36055     getMinWidth: function(){
36056         return this.minWidth;
36057     },
36058     
36059     getMinHeight: function(){
36060         return this.minHeight;
36061     }
36062 });
36063
36064
36065
36066
36067  
36068
36069
36070
36071
36072
36073 Roo.bootstrap.layout.North = function(config)
36074 {
36075     config.region = 'north';
36076     config.cursor = 'n-resize';
36077     
36078     Roo.bootstrap.layout.Split.call(this, config);
36079     
36080     
36081     if(this.split){
36082         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36083         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36084         this.split.el.addClass("roo-layout-split-v");
36085     }
36086     var size = config.initialSize || config.height;
36087     if(typeof size != "undefined"){
36088         this.el.setHeight(size);
36089     }
36090 };
36091 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36092 {
36093     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36094     
36095     
36096     
36097     getBox : function(){
36098         if(this.collapsed){
36099             return this.collapsedEl.getBox();
36100         }
36101         var box = this.el.getBox();
36102         if(this.split){
36103             box.height += this.split.el.getHeight();
36104         }
36105         return box;
36106     },
36107     
36108     updateBox : function(box){
36109         if(this.split && !this.collapsed){
36110             box.height -= this.split.el.getHeight();
36111             this.split.el.setLeft(box.x);
36112             this.split.el.setTop(box.y+box.height);
36113             this.split.el.setWidth(box.width);
36114         }
36115         if(this.collapsed){
36116             this.updateBody(box.width, null);
36117         }
36118         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36119     }
36120 });
36121
36122
36123
36124
36125
36126 Roo.bootstrap.layout.South = function(config){
36127     config.region = 'south';
36128     config.cursor = 's-resize';
36129     Roo.bootstrap.layout.Split.call(this, config);
36130     if(this.split){
36131         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36132         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36133         this.split.el.addClass("roo-layout-split-v");
36134     }
36135     var size = config.initialSize || config.height;
36136     if(typeof size != "undefined"){
36137         this.el.setHeight(size);
36138     }
36139 };
36140
36141 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36142     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36143     getBox : function(){
36144         if(this.collapsed){
36145             return this.collapsedEl.getBox();
36146         }
36147         var box = this.el.getBox();
36148         if(this.split){
36149             var sh = this.split.el.getHeight();
36150             box.height += sh;
36151             box.y -= sh;
36152         }
36153         return box;
36154     },
36155     
36156     updateBox : function(box){
36157         if(this.split && !this.collapsed){
36158             var sh = this.split.el.getHeight();
36159             box.height -= sh;
36160             box.y += sh;
36161             this.split.el.setLeft(box.x);
36162             this.split.el.setTop(box.y-sh);
36163             this.split.el.setWidth(box.width);
36164         }
36165         if(this.collapsed){
36166             this.updateBody(box.width, null);
36167         }
36168         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36169     }
36170 });
36171
36172 Roo.bootstrap.layout.East = function(config){
36173     config.region = "east";
36174     config.cursor = "e-resize";
36175     Roo.bootstrap.layout.Split.call(this, config);
36176     if(this.split){
36177         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36178         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36179         this.split.el.addClass("roo-layout-split-h");
36180     }
36181     var size = config.initialSize || config.width;
36182     if(typeof size != "undefined"){
36183         this.el.setWidth(size);
36184     }
36185 };
36186 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36187     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36188     getBox : function(){
36189         if(this.collapsed){
36190             return this.collapsedEl.getBox();
36191         }
36192         var box = this.el.getBox();
36193         if(this.split){
36194             var sw = this.split.el.getWidth();
36195             box.width += sw;
36196             box.x -= sw;
36197         }
36198         return box;
36199     },
36200
36201     updateBox : function(box){
36202         if(this.split && !this.collapsed){
36203             var sw = this.split.el.getWidth();
36204             box.width -= sw;
36205             this.split.el.setLeft(box.x);
36206             this.split.el.setTop(box.y);
36207             this.split.el.setHeight(box.height);
36208             box.x += sw;
36209         }
36210         if(this.collapsed){
36211             this.updateBody(null, box.height);
36212         }
36213         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36214     }
36215 });
36216
36217 Roo.bootstrap.layout.West = function(config){
36218     config.region = "west";
36219     config.cursor = "w-resize";
36220     
36221     Roo.bootstrap.layout.Split.call(this, config);
36222     if(this.split){
36223         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36224         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36225         this.split.el.addClass("roo-layout-split-h");
36226     }
36227     
36228 };
36229 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36230     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36231     
36232     onRender: function(ctr, pos)
36233     {
36234         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36235         var size = this.config.initialSize || this.config.width;
36236         if(typeof size != "undefined"){
36237             this.el.setWidth(size);
36238         }
36239     },
36240     
36241     getBox : function(){
36242         if(this.collapsed){
36243             return this.collapsedEl.getBox();
36244         }
36245         var box = this.el.getBox();
36246         if(this.split){
36247             box.width += this.split.el.getWidth();
36248         }
36249         return box;
36250     },
36251     
36252     updateBox : function(box){
36253         if(this.split && !this.collapsed){
36254             var sw = this.split.el.getWidth();
36255             box.width -= sw;
36256             this.split.el.setLeft(box.x+box.width);
36257             this.split.el.setTop(box.y);
36258             this.split.el.setHeight(box.height);
36259         }
36260         if(this.collapsed){
36261             this.updateBody(null, box.height);
36262         }
36263         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36264     }
36265 });
36266 Roo.namespace("Roo.bootstrap.panel");/*
36267  * Based on:
36268  * Ext JS Library 1.1.1
36269  * Copyright(c) 2006-2007, Ext JS, LLC.
36270  *
36271  * Originally Released Under LGPL - original licence link has changed is not relivant.
36272  *
36273  * Fork - LGPL
36274  * <script type="text/javascript">
36275  */
36276 /**
36277  * @class Roo.ContentPanel
36278  * @extends Roo.util.Observable
36279  * A basic ContentPanel element.
36280  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36281  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36282  * @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
36283  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36284  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36285  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36286  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36287  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36288  * @cfg {String} title          The title for this panel
36289  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36290  * @cfg {String} url            Calls {@link #setUrl} with this value
36291  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36292  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36293  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36294  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36295  * @cfg {Boolean} badges render the badges
36296
36297  * @constructor
36298  * Create a new ContentPanel.
36299  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36300  * @param {String/Object} config A string to set only the title or a config object
36301  * @param {String} content (optional) Set the HTML content for this panel
36302  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36303  */
36304 Roo.bootstrap.panel.Content = function( config){
36305     
36306     this.tpl = config.tpl || false;
36307     
36308     var el = config.el;
36309     var content = config.content;
36310
36311     if(config.autoCreate){ // xtype is available if this is called from factory
36312         el = Roo.id();
36313     }
36314     this.el = Roo.get(el);
36315     if(!this.el && config && config.autoCreate){
36316         if(typeof config.autoCreate == "object"){
36317             if(!config.autoCreate.id){
36318                 config.autoCreate.id = config.id||el;
36319             }
36320             this.el = Roo.DomHelper.append(document.body,
36321                         config.autoCreate, true);
36322         }else{
36323             var elcfg =  {   tag: "div",
36324                             cls: "roo-layout-inactive-content",
36325                             id: config.id||el
36326                             };
36327             if (config.html) {
36328                 elcfg.html = config.html;
36329                 
36330             }
36331                         
36332             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36333         }
36334     } 
36335     this.closable = false;
36336     this.loaded = false;
36337     this.active = false;
36338    
36339       
36340     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36341         
36342         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36343         
36344         this.wrapEl = this.el; //this.el.wrap();
36345         var ti = [];
36346         if (config.toolbar.items) {
36347             ti = config.toolbar.items ;
36348             delete config.toolbar.items ;
36349         }
36350         
36351         var nitems = [];
36352         this.toolbar.render(this.wrapEl, 'before');
36353         for(var i =0;i < ti.length;i++) {
36354           //  Roo.log(['add child', items[i]]);
36355             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36356         }
36357         this.toolbar.items = nitems;
36358         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36359         delete config.toolbar;
36360         
36361     }
36362     /*
36363     // xtype created footer. - not sure if will work as we normally have to render first..
36364     if (this.footer && !this.footer.el && this.footer.xtype) {
36365         if (!this.wrapEl) {
36366             this.wrapEl = this.el.wrap();
36367         }
36368     
36369         this.footer.container = this.wrapEl.createChild();
36370          
36371         this.footer = Roo.factory(this.footer, Roo);
36372         
36373     }
36374     */
36375     
36376      if(typeof config == "string"){
36377         this.title = config;
36378     }else{
36379         Roo.apply(this, config);
36380     }
36381     
36382     if(this.resizeEl){
36383         this.resizeEl = Roo.get(this.resizeEl, true);
36384     }else{
36385         this.resizeEl = this.el;
36386     }
36387     // handle view.xtype
36388     
36389  
36390     
36391     
36392     this.addEvents({
36393         /**
36394          * @event activate
36395          * Fires when this panel is activated. 
36396          * @param {Roo.ContentPanel} this
36397          */
36398         "activate" : true,
36399         /**
36400          * @event deactivate
36401          * Fires when this panel is activated. 
36402          * @param {Roo.ContentPanel} this
36403          */
36404         "deactivate" : true,
36405
36406         /**
36407          * @event resize
36408          * Fires when this panel is resized if fitToFrame is true.
36409          * @param {Roo.ContentPanel} this
36410          * @param {Number} width The width after any component adjustments
36411          * @param {Number} height The height after any component adjustments
36412          */
36413         "resize" : true,
36414         
36415          /**
36416          * @event render
36417          * Fires when this tab is created
36418          * @param {Roo.ContentPanel} this
36419          */
36420         "render" : true
36421         
36422         
36423         
36424     });
36425     
36426
36427     
36428     
36429     if(this.autoScroll){
36430         this.resizeEl.setStyle("overflow", "auto");
36431     } else {
36432         // fix randome scrolling
36433         //this.el.on('scroll', function() {
36434         //    Roo.log('fix random scolling');
36435         //    this.scrollTo('top',0); 
36436         //});
36437     }
36438     content = content || this.content;
36439     if(content){
36440         this.setContent(content);
36441     }
36442     if(config && config.url){
36443         this.setUrl(this.url, this.params, this.loadOnce);
36444     }
36445     
36446     
36447     
36448     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36449     
36450     if (this.view && typeof(this.view.xtype) != 'undefined') {
36451         this.view.el = this.el.appendChild(document.createElement("div"));
36452         this.view = Roo.factory(this.view); 
36453         this.view.render  &&  this.view.render(false, '');  
36454     }
36455     
36456     
36457     this.fireEvent('render', this);
36458 };
36459
36460 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36461     
36462     tabTip : '',
36463     
36464     setRegion : function(region){
36465         this.region = region;
36466         this.setActiveClass(region && !this.background);
36467     },
36468     
36469     
36470     setActiveClass: function(state)
36471     {
36472         if(state){
36473            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36474            this.el.setStyle('position','relative');
36475         }else{
36476            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36477            this.el.setStyle('position', 'absolute');
36478         } 
36479     },
36480     
36481     /**
36482      * Returns the toolbar for this Panel if one was configured. 
36483      * @return {Roo.Toolbar} 
36484      */
36485     getToolbar : function(){
36486         return this.toolbar;
36487     },
36488     
36489     setActiveState : function(active)
36490     {
36491         this.active = active;
36492         this.setActiveClass(active);
36493         if(!active){
36494             this.fireEvent("deactivate", this);
36495         }else{
36496             this.fireEvent("activate", this);
36497         }
36498     },
36499     /**
36500      * Updates this panel's element
36501      * @param {String} content The new content
36502      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36503     */
36504     setContent : function(content, loadScripts){
36505         this.el.update(content, loadScripts);
36506     },
36507
36508     ignoreResize : function(w, h){
36509         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36510             return true;
36511         }else{
36512             this.lastSize = {width: w, height: h};
36513             return false;
36514         }
36515     },
36516     /**
36517      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36518      * @return {Roo.UpdateManager} The UpdateManager
36519      */
36520     getUpdateManager : function(){
36521         return this.el.getUpdateManager();
36522     },
36523      /**
36524      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36525      * @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:
36526 <pre><code>
36527 panel.load({
36528     url: "your-url.php",
36529     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36530     callback: yourFunction,
36531     scope: yourObject, //(optional scope)
36532     discardUrl: false,
36533     nocache: false,
36534     text: "Loading...",
36535     timeout: 30,
36536     scripts: false
36537 });
36538 </code></pre>
36539      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36540      * 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.
36541      * @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}
36542      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36543      * @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.
36544      * @return {Roo.ContentPanel} this
36545      */
36546     load : function(){
36547         var um = this.el.getUpdateManager();
36548         um.update.apply(um, arguments);
36549         return this;
36550     },
36551
36552
36553     /**
36554      * 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.
36555      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36556      * @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)
36557      * @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)
36558      * @return {Roo.UpdateManager} The UpdateManager
36559      */
36560     setUrl : function(url, params, loadOnce){
36561         if(this.refreshDelegate){
36562             this.removeListener("activate", this.refreshDelegate);
36563         }
36564         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36565         this.on("activate", this.refreshDelegate);
36566         return this.el.getUpdateManager();
36567     },
36568     
36569     _handleRefresh : function(url, params, loadOnce){
36570         if(!loadOnce || !this.loaded){
36571             var updater = this.el.getUpdateManager();
36572             updater.update(url, params, this._setLoaded.createDelegate(this));
36573         }
36574     },
36575     
36576     _setLoaded : function(){
36577         this.loaded = true;
36578     }, 
36579     
36580     /**
36581      * Returns this panel's id
36582      * @return {String} 
36583      */
36584     getId : function(){
36585         return this.el.id;
36586     },
36587     
36588     /** 
36589      * Returns this panel's element - used by regiosn to add.
36590      * @return {Roo.Element} 
36591      */
36592     getEl : function(){
36593         return this.wrapEl || this.el;
36594     },
36595     
36596    
36597     
36598     adjustForComponents : function(width, height)
36599     {
36600         //Roo.log('adjustForComponents ');
36601         if(this.resizeEl != this.el){
36602             width -= this.el.getFrameWidth('lr');
36603             height -= this.el.getFrameWidth('tb');
36604         }
36605         if(this.toolbar){
36606             var te = this.toolbar.getEl();
36607             te.setWidth(width);
36608             height -= te.getHeight();
36609         }
36610         if(this.footer){
36611             var te = this.footer.getEl();
36612             te.setWidth(width);
36613             height -= te.getHeight();
36614         }
36615         
36616         
36617         if(this.adjustments){
36618             width += this.adjustments[0];
36619             height += this.adjustments[1];
36620         }
36621         return {"width": width, "height": height};
36622     },
36623     
36624     setSize : function(width, height){
36625         if(this.fitToFrame && !this.ignoreResize(width, height)){
36626             if(this.fitContainer && this.resizeEl != this.el){
36627                 this.el.setSize(width, height);
36628             }
36629             var size = this.adjustForComponents(width, height);
36630             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36631             this.fireEvent('resize', this, size.width, size.height);
36632         }
36633     },
36634     
36635     /**
36636      * Returns this panel's title
36637      * @return {String} 
36638      */
36639     getTitle : function(){
36640         
36641         if (typeof(this.title) != 'object') {
36642             return this.title;
36643         }
36644         
36645         var t = '';
36646         for (var k in this.title) {
36647             if (!this.title.hasOwnProperty(k)) {
36648                 continue;
36649             }
36650             
36651             if (k.indexOf('-') >= 0) {
36652                 var s = k.split('-');
36653                 for (var i = 0; i<s.length; i++) {
36654                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36655                 }
36656             } else {
36657                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36658             }
36659         }
36660         return t;
36661     },
36662     
36663     /**
36664      * Set this panel's title
36665      * @param {String} title
36666      */
36667     setTitle : function(title){
36668         this.title = title;
36669         if(this.region){
36670             this.region.updatePanelTitle(this, title);
36671         }
36672     },
36673     
36674     /**
36675      * Returns true is this panel was configured to be closable
36676      * @return {Boolean} 
36677      */
36678     isClosable : function(){
36679         return this.closable;
36680     },
36681     
36682     beforeSlide : function(){
36683         this.el.clip();
36684         this.resizeEl.clip();
36685     },
36686     
36687     afterSlide : function(){
36688         this.el.unclip();
36689         this.resizeEl.unclip();
36690     },
36691     
36692     /**
36693      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36694      *   Will fail silently if the {@link #setUrl} method has not been called.
36695      *   This does not activate the panel, just updates its content.
36696      */
36697     refresh : function(){
36698         if(this.refreshDelegate){
36699            this.loaded = false;
36700            this.refreshDelegate();
36701         }
36702     },
36703     
36704     /**
36705      * Destroys this panel
36706      */
36707     destroy : function(){
36708         this.el.removeAllListeners();
36709         var tempEl = document.createElement("span");
36710         tempEl.appendChild(this.el.dom);
36711         tempEl.innerHTML = "";
36712         this.el.remove();
36713         this.el = null;
36714     },
36715     
36716     /**
36717      * form - if the content panel contains a form - this is a reference to it.
36718      * @type {Roo.form.Form}
36719      */
36720     form : false,
36721     /**
36722      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36723      *    This contains a reference to it.
36724      * @type {Roo.View}
36725      */
36726     view : false,
36727     
36728       /**
36729      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36730      * <pre><code>
36731
36732 layout.addxtype({
36733        xtype : 'Form',
36734        items: [ .... ]
36735    }
36736 );
36737
36738 </code></pre>
36739      * @param {Object} cfg Xtype definition of item to add.
36740      */
36741     
36742     
36743     getChildContainer: function () {
36744         return this.getEl();
36745     }
36746     
36747     
36748     /*
36749         var  ret = new Roo.factory(cfg);
36750         return ret;
36751         
36752         
36753         // add form..
36754         if (cfg.xtype.match(/^Form$/)) {
36755             
36756             var el;
36757             //if (this.footer) {
36758             //    el = this.footer.container.insertSibling(false, 'before');
36759             //} else {
36760                 el = this.el.createChild();
36761             //}
36762
36763             this.form = new  Roo.form.Form(cfg);
36764             
36765             
36766             if ( this.form.allItems.length) {
36767                 this.form.render(el.dom);
36768             }
36769             return this.form;
36770         }
36771         // should only have one of theses..
36772         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36773             // views.. should not be just added - used named prop 'view''
36774             
36775             cfg.el = this.el.appendChild(document.createElement("div"));
36776             // factory?
36777             
36778             var ret = new Roo.factory(cfg);
36779              
36780              ret.render && ret.render(false, ''); // render blank..
36781             this.view = ret;
36782             return ret;
36783         }
36784         return false;
36785     }
36786     \*/
36787 });
36788  
36789 /**
36790  * @class Roo.bootstrap.panel.Grid
36791  * @extends Roo.bootstrap.panel.Content
36792  * @constructor
36793  * Create a new GridPanel.
36794  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36795  * @param {Object} config A the config object
36796   
36797  */
36798
36799
36800
36801 Roo.bootstrap.panel.Grid = function(config)
36802 {
36803     
36804       
36805     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36806         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36807
36808     config.el = this.wrapper;
36809     //this.el = this.wrapper;
36810     
36811       if (config.container) {
36812         // ctor'ed from a Border/panel.grid
36813         
36814         
36815         this.wrapper.setStyle("overflow", "hidden");
36816         this.wrapper.addClass('roo-grid-container');
36817
36818     }
36819     
36820     
36821     if(config.toolbar){
36822         var tool_el = this.wrapper.createChild();    
36823         this.toolbar = Roo.factory(config.toolbar);
36824         var ti = [];
36825         if (config.toolbar.items) {
36826             ti = config.toolbar.items ;
36827             delete config.toolbar.items ;
36828         }
36829         
36830         var nitems = [];
36831         this.toolbar.render(tool_el);
36832         for(var i =0;i < ti.length;i++) {
36833           //  Roo.log(['add child', items[i]]);
36834             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36835         }
36836         this.toolbar.items = nitems;
36837         
36838         delete config.toolbar;
36839     }
36840     
36841     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36842     config.grid.scrollBody = true;;
36843     config.grid.monitorWindowResize = false; // turn off autosizing
36844     config.grid.autoHeight = false;
36845     config.grid.autoWidth = false;
36846     
36847     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36848     
36849     if (config.background) {
36850         // render grid on panel activation (if panel background)
36851         this.on('activate', function(gp) {
36852             if (!gp.grid.rendered) {
36853                 gp.grid.render(this.wrapper);
36854                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36855             }
36856         });
36857             
36858     } else {
36859         this.grid.render(this.wrapper);
36860         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36861
36862     }
36863     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36864     // ??? needed ??? config.el = this.wrapper;
36865     
36866     
36867     
36868   
36869     // xtype created footer. - not sure if will work as we normally have to render first..
36870     if (this.footer && !this.footer.el && this.footer.xtype) {
36871         
36872         var ctr = this.grid.getView().getFooterPanel(true);
36873         this.footer.dataSource = this.grid.dataSource;
36874         this.footer = Roo.factory(this.footer, Roo);
36875         this.footer.render(ctr);
36876         
36877     }
36878     
36879     
36880     
36881     
36882      
36883 };
36884
36885 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36886     getId : function(){
36887         return this.grid.id;
36888     },
36889     
36890     /**
36891      * Returns the grid for this panel
36892      * @return {Roo.bootstrap.Table} 
36893      */
36894     getGrid : function(){
36895         return this.grid;    
36896     },
36897     
36898     setSize : function(width, height){
36899         if(!this.ignoreResize(width, height)){
36900             var grid = this.grid;
36901             var size = this.adjustForComponents(width, height);
36902             var gridel = grid.getGridEl();
36903             gridel.setSize(size.width, size.height);
36904             /*
36905             var thd = grid.getGridEl().select('thead',true).first();
36906             var tbd = grid.getGridEl().select('tbody', true).first();
36907             if (tbd) {
36908                 tbd.setSize(width, height - thd.getHeight());
36909             }
36910             */
36911             grid.autoSize();
36912         }
36913     },
36914      
36915     
36916     
36917     beforeSlide : function(){
36918         this.grid.getView().scroller.clip();
36919     },
36920     
36921     afterSlide : function(){
36922         this.grid.getView().scroller.unclip();
36923     },
36924     
36925     destroy : function(){
36926         this.grid.destroy();
36927         delete this.grid;
36928         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36929     }
36930 });
36931
36932 /**
36933  * @class Roo.bootstrap.panel.Nest
36934  * @extends Roo.bootstrap.panel.Content
36935  * @constructor
36936  * Create a new Panel, that can contain a layout.Border.
36937  * 
36938  * 
36939  * @param {Roo.BorderLayout} layout The layout for this panel
36940  * @param {String/Object} config A string to set only the title or a config object
36941  */
36942 Roo.bootstrap.panel.Nest = function(config)
36943 {
36944     // construct with only one argument..
36945     /* FIXME - implement nicer consturctors
36946     if (layout.layout) {
36947         config = layout;
36948         layout = config.layout;
36949         delete config.layout;
36950     }
36951     if (layout.xtype && !layout.getEl) {
36952         // then layout needs constructing..
36953         layout = Roo.factory(layout, Roo);
36954     }
36955     */
36956     
36957     config.el =  config.layout.getEl();
36958     
36959     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36960     
36961     config.layout.monitorWindowResize = false; // turn off autosizing
36962     this.layout = config.layout;
36963     this.layout.getEl().addClass("roo-layout-nested-layout");
36964     
36965     
36966     
36967     
36968 };
36969
36970 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36971
36972     setSize : function(width, height){
36973         if(!this.ignoreResize(width, height)){
36974             var size = this.adjustForComponents(width, height);
36975             var el = this.layout.getEl();
36976             if (size.height < 1) {
36977                 el.setWidth(size.width);   
36978             } else {
36979                 el.setSize(size.width, size.height);
36980             }
36981             var touch = el.dom.offsetWidth;
36982             this.layout.layout();
36983             // ie requires a double layout on the first pass
36984             if(Roo.isIE && !this.initialized){
36985                 this.initialized = true;
36986                 this.layout.layout();
36987             }
36988         }
36989     },
36990     
36991     // activate all subpanels if not currently active..
36992     
36993     setActiveState : function(active){
36994         this.active = active;
36995         this.setActiveClass(active);
36996         
36997         if(!active){
36998             this.fireEvent("deactivate", this);
36999             return;
37000         }
37001         
37002         this.fireEvent("activate", this);
37003         // not sure if this should happen before or after..
37004         if (!this.layout) {
37005             return; // should not happen..
37006         }
37007         var reg = false;
37008         for (var r in this.layout.regions) {
37009             reg = this.layout.getRegion(r);
37010             if (reg.getActivePanel()) {
37011                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37012                 reg.setActivePanel(reg.getActivePanel());
37013                 continue;
37014             }
37015             if (!reg.panels.length) {
37016                 continue;
37017             }
37018             reg.showPanel(reg.getPanel(0));
37019         }
37020         
37021         
37022         
37023         
37024     },
37025     
37026     /**
37027      * Returns the nested BorderLayout for this panel
37028      * @return {Roo.BorderLayout} 
37029      */
37030     getLayout : function(){
37031         return this.layout;
37032     },
37033     
37034      /**
37035      * Adds a xtype elements to the layout of the nested panel
37036      * <pre><code>
37037
37038 panel.addxtype({
37039        xtype : 'ContentPanel',
37040        region: 'west',
37041        items: [ .... ]
37042    }
37043 );
37044
37045 panel.addxtype({
37046         xtype : 'NestedLayoutPanel',
37047         region: 'west',
37048         layout: {
37049            center: { },
37050            west: { }   
37051         },
37052         items : [ ... list of content panels or nested layout panels.. ]
37053    }
37054 );
37055 </code></pre>
37056      * @param {Object} cfg Xtype definition of item to add.
37057      */
37058     addxtype : function(cfg) {
37059         return this.layout.addxtype(cfg);
37060     
37061     }
37062 });        /*
37063  * Based on:
37064  * Ext JS Library 1.1.1
37065  * Copyright(c) 2006-2007, Ext JS, LLC.
37066  *
37067  * Originally Released Under LGPL - original licence link has changed is not relivant.
37068  *
37069  * Fork - LGPL
37070  * <script type="text/javascript">
37071  */
37072 /**
37073  * @class Roo.TabPanel
37074  * @extends Roo.util.Observable
37075  * A lightweight tab container.
37076  * <br><br>
37077  * Usage:
37078  * <pre><code>
37079 // basic tabs 1, built from existing content
37080 var tabs = new Roo.TabPanel("tabs1");
37081 tabs.addTab("script", "View Script");
37082 tabs.addTab("markup", "View Markup");
37083 tabs.activate("script");
37084
37085 // more advanced tabs, built from javascript
37086 var jtabs = new Roo.TabPanel("jtabs");
37087 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37088
37089 // set up the UpdateManager
37090 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37091 var updater = tab2.getUpdateManager();
37092 updater.setDefaultUrl("ajax1.htm");
37093 tab2.on('activate', updater.refresh, updater, true);
37094
37095 // Use setUrl for Ajax loading
37096 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37097 tab3.setUrl("ajax2.htm", null, true);
37098
37099 // Disabled tab
37100 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37101 tab4.disable();
37102
37103 jtabs.activate("jtabs-1");
37104  * </code></pre>
37105  * @constructor
37106  * Create a new TabPanel.
37107  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37108  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37109  */
37110 Roo.bootstrap.panel.Tabs = function(config){
37111     /**
37112     * The container element for this TabPanel.
37113     * @type Roo.Element
37114     */
37115     this.el = Roo.get(config.el);
37116     delete config.el;
37117     if(config){
37118         if(typeof config == "boolean"){
37119             this.tabPosition = config ? "bottom" : "top";
37120         }else{
37121             Roo.apply(this, config);
37122         }
37123     }
37124     
37125     if(this.tabPosition == "bottom"){
37126         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37127         this.el.addClass("roo-tabs-bottom");
37128     }
37129     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37130     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37131     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37132     if(Roo.isIE){
37133         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37134     }
37135     if(this.tabPosition != "bottom"){
37136         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37137          * @type Roo.Element
37138          */
37139         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37140         this.el.addClass("roo-tabs-top");
37141     }
37142     this.items = [];
37143
37144     this.bodyEl.setStyle("position", "relative");
37145
37146     this.active = null;
37147     this.activateDelegate = this.activate.createDelegate(this);
37148
37149     this.addEvents({
37150         /**
37151          * @event tabchange
37152          * Fires when the active tab changes
37153          * @param {Roo.TabPanel} this
37154          * @param {Roo.TabPanelItem} activePanel The new active tab
37155          */
37156         "tabchange": true,
37157         /**
37158          * @event beforetabchange
37159          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37160          * @param {Roo.TabPanel} this
37161          * @param {Object} e Set cancel to true on this object to cancel the tab change
37162          * @param {Roo.TabPanelItem} tab The tab being changed to
37163          */
37164         "beforetabchange" : true
37165     });
37166
37167     Roo.EventManager.onWindowResize(this.onResize, this);
37168     this.cpad = this.el.getPadding("lr");
37169     this.hiddenCount = 0;
37170
37171
37172     // toolbar on the tabbar support...
37173     if (this.toolbar) {
37174         alert("no toolbar support yet");
37175         this.toolbar  = false;
37176         /*
37177         var tcfg = this.toolbar;
37178         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37179         this.toolbar = new Roo.Toolbar(tcfg);
37180         if (Roo.isSafari) {
37181             var tbl = tcfg.container.child('table', true);
37182             tbl.setAttribute('width', '100%');
37183         }
37184         */
37185         
37186     }
37187    
37188
37189
37190     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37191 };
37192
37193 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37194     /*
37195      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37196      */
37197     tabPosition : "top",
37198     /*
37199      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37200      */
37201     currentTabWidth : 0,
37202     /*
37203      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37204      */
37205     minTabWidth : 40,
37206     /*
37207      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37208      */
37209     maxTabWidth : 250,
37210     /*
37211      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37212      */
37213     preferredTabWidth : 175,
37214     /*
37215      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37216      */
37217     resizeTabs : false,
37218     /*
37219      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37220      */
37221     monitorResize : true,
37222     /*
37223      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37224      */
37225     toolbar : false,
37226
37227     /**
37228      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37229      * @param {String} id The id of the div to use <b>or create</b>
37230      * @param {String} text The text for the tab
37231      * @param {String} content (optional) Content to put in the TabPanelItem body
37232      * @param {Boolean} closable (optional) True to create a close icon on the tab
37233      * @return {Roo.TabPanelItem} The created TabPanelItem
37234      */
37235     addTab : function(id, text, content, closable, tpl)
37236     {
37237         var item = new Roo.bootstrap.panel.TabItem({
37238             panel: this,
37239             id : id,
37240             text : text,
37241             closable : closable,
37242             tpl : tpl
37243         });
37244         this.addTabItem(item);
37245         if(content){
37246             item.setContent(content);
37247         }
37248         return item;
37249     },
37250
37251     /**
37252      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37253      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37254      * @return {Roo.TabPanelItem}
37255      */
37256     getTab : function(id){
37257         return this.items[id];
37258     },
37259
37260     /**
37261      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37262      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37263      */
37264     hideTab : function(id){
37265         var t = this.items[id];
37266         if(!t.isHidden()){
37267            t.setHidden(true);
37268            this.hiddenCount++;
37269            this.autoSizeTabs();
37270         }
37271     },
37272
37273     /**
37274      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37275      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37276      */
37277     unhideTab : function(id){
37278         var t = this.items[id];
37279         if(t.isHidden()){
37280            t.setHidden(false);
37281            this.hiddenCount--;
37282            this.autoSizeTabs();
37283         }
37284     },
37285
37286     /**
37287      * Adds an existing {@link Roo.TabPanelItem}.
37288      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37289      */
37290     addTabItem : function(item){
37291         this.items[item.id] = item;
37292         this.items.push(item);
37293       //  if(this.resizeTabs){
37294     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37295   //         this.autoSizeTabs();
37296 //        }else{
37297 //            item.autoSize();
37298        // }
37299     },
37300
37301     /**
37302      * Removes a {@link Roo.TabPanelItem}.
37303      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37304      */
37305     removeTab : function(id){
37306         var items = this.items;
37307         var tab = items[id];
37308         if(!tab) { return; }
37309         var index = items.indexOf(tab);
37310         if(this.active == tab && items.length > 1){
37311             var newTab = this.getNextAvailable(index);
37312             if(newTab) {
37313                 newTab.activate();
37314             }
37315         }
37316         this.stripEl.dom.removeChild(tab.pnode.dom);
37317         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37318             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37319         }
37320         items.splice(index, 1);
37321         delete this.items[tab.id];
37322         tab.fireEvent("close", tab);
37323         tab.purgeListeners();
37324         this.autoSizeTabs();
37325     },
37326
37327     getNextAvailable : function(start){
37328         var items = this.items;
37329         var index = start;
37330         // look for a next tab that will slide over to
37331         // replace the one being removed
37332         while(index < items.length){
37333             var item = items[++index];
37334             if(item && !item.isHidden()){
37335                 return item;
37336             }
37337         }
37338         // if one isn't found select the previous tab (on the left)
37339         index = start;
37340         while(index >= 0){
37341             var item = items[--index];
37342             if(item && !item.isHidden()){
37343                 return item;
37344             }
37345         }
37346         return null;
37347     },
37348
37349     /**
37350      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37351      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37352      */
37353     disableTab : function(id){
37354         var tab = this.items[id];
37355         if(tab && this.active != tab){
37356             tab.disable();
37357         }
37358     },
37359
37360     /**
37361      * Enables a {@link Roo.TabPanelItem} that is disabled.
37362      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37363      */
37364     enableTab : function(id){
37365         var tab = this.items[id];
37366         tab.enable();
37367     },
37368
37369     /**
37370      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37371      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37372      * @return {Roo.TabPanelItem} The TabPanelItem.
37373      */
37374     activate : function(id){
37375         var tab = this.items[id];
37376         if(!tab){
37377             return null;
37378         }
37379         if(tab == this.active || tab.disabled){
37380             return tab;
37381         }
37382         var e = {};
37383         this.fireEvent("beforetabchange", this, e, tab);
37384         if(e.cancel !== true && !tab.disabled){
37385             if(this.active){
37386                 this.active.hide();
37387             }
37388             this.active = this.items[id];
37389             this.active.show();
37390             this.fireEvent("tabchange", this, this.active);
37391         }
37392         return tab;
37393     },
37394
37395     /**
37396      * Gets the active {@link Roo.TabPanelItem}.
37397      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37398      */
37399     getActiveTab : function(){
37400         return this.active;
37401     },
37402
37403     /**
37404      * Updates the tab body element to fit the height of the container element
37405      * for overflow scrolling
37406      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37407      */
37408     syncHeight : function(targetHeight){
37409         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37410         var bm = this.bodyEl.getMargins();
37411         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37412         this.bodyEl.setHeight(newHeight);
37413         return newHeight;
37414     },
37415
37416     onResize : function(){
37417         if(this.monitorResize){
37418             this.autoSizeTabs();
37419         }
37420     },
37421
37422     /**
37423      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37424      */
37425     beginUpdate : function(){
37426         this.updating = true;
37427     },
37428
37429     /**
37430      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37431      */
37432     endUpdate : function(){
37433         this.updating = false;
37434         this.autoSizeTabs();
37435     },
37436
37437     /**
37438      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37439      */
37440     autoSizeTabs : function(){
37441         var count = this.items.length;
37442         var vcount = count - this.hiddenCount;
37443         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37444             return;
37445         }
37446         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37447         var availWidth = Math.floor(w / vcount);
37448         var b = this.stripBody;
37449         if(b.getWidth() > w){
37450             var tabs = this.items;
37451             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37452             if(availWidth < this.minTabWidth){
37453                 /*if(!this.sleft){    // incomplete scrolling code
37454                     this.createScrollButtons();
37455                 }
37456                 this.showScroll();
37457                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37458             }
37459         }else{
37460             if(this.currentTabWidth < this.preferredTabWidth){
37461                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37462             }
37463         }
37464     },
37465
37466     /**
37467      * Returns the number of tabs in this TabPanel.
37468      * @return {Number}
37469      */
37470      getCount : function(){
37471          return this.items.length;
37472      },
37473
37474     /**
37475      * Resizes all the tabs to the passed width
37476      * @param {Number} The new width
37477      */
37478     setTabWidth : function(width){
37479         this.currentTabWidth = width;
37480         for(var i = 0, len = this.items.length; i < len; i++) {
37481                 if(!this.items[i].isHidden()) {
37482                 this.items[i].setWidth(width);
37483             }
37484         }
37485     },
37486
37487     /**
37488      * Destroys this TabPanel
37489      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37490      */
37491     destroy : function(removeEl){
37492         Roo.EventManager.removeResizeListener(this.onResize, this);
37493         for(var i = 0, len = this.items.length; i < len; i++){
37494             this.items[i].purgeListeners();
37495         }
37496         if(removeEl === true){
37497             this.el.update("");
37498             this.el.remove();
37499         }
37500     },
37501     
37502     createStrip : function(container)
37503     {
37504         var strip = document.createElement("nav");
37505         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37506         container.appendChild(strip);
37507         return strip;
37508     },
37509     
37510     createStripList : function(strip)
37511     {
37512         // div wrapper for retard IE
37513         // returns the "tr" element.
37514         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37515         //'<div class="x-tabs-strip-wrap">'+
37516           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37517           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37518         return strip.firstChild; //.firstChild.firstChild.firstChild;
37519     },
37520     createBody : function(container)
37521     {
37522         var body = document.createElement("div");
37523         Roo.id(body, "tab-body");
37524         //Roo.fly(body).addClass("x-tabs-body");
37525         Roo.fly(body).addClass("tab-content");
37526         container.appendChild(body);
37527         return body;
37528     },
37529     createItemBody :function(bodyEl, id){
37530         var body = Roo.getDom(id);
37531         if(!body){
37532             body = document.createElement("div");
37533             body.id = id;
37534         }
37535         //Roo.fly(body).addClass("x-tabs-item-body");
37536         Roo.fly(body).addClass("tab-pane");
37537          bodyEl.insertBefore(body, bodyEl.firstChild);
37538         return body;
37539     },
37540     /** @private */
37541     createStripElements :  function(stripEl, text, closable, tpl)
37542     {
37543         var td = document.createElement("li"); // was td..
37544         
37545         
37546         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37547         
37548         
37549         stripEl.appendChild(td);
37550         /*if(closable){
37551             td.className = "x-tabs-closable";
37552             if(!this.closeTpl){
37553                 this.closeTpl = new Roo.Template(
37554                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37555                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37556                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37557                 );
37558             }
37559             var el = this.closeTpl.overwrite(td, {"text": text});
37560             var close = el.getElementsByTagName("div")[0];
37561             var inner = el.getElementsByTagName("em")[0];
37562             return {"el": el, "close": close, "inner": inner};
37563         } else {
37564         */
37565         // not sure what this is..
37566 //            if(!this.tabTpl){
37567                 //this.tabTpl = new Roo.Template(
37568                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37569                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37570                 //);
37571 //                this.tabTpl = new Roo.Template(
37572 //                   '<a href="#">' +
37573 //                   '<span unselectable="on"' +
37574 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37575 //                            ' >{text}</span></a>'
37576 //                );
37577 //                
37578 //            }
37579
37580
37581             var template = tpl || this.tabTpl || false;
37582             
37583             if(!template){
37584                 
37585                 template = new Roo.Template(
37586                    '<a href="#">' +
37587                    '<span unselectable="on"' +
37588                             (this.disableTooltips ? '' : ' title="{text}"') +
37589                             ' >{text}</span></a>'
37590                 );
37591             }
37592             
37593             switch (typeof(template)) {
37594                 case 'object' :
37595                     break;
37596                 case 'string' :
37597                     template = new Roo.Template(template);
37598                     break;
37599                 default :
37600                     break;
37601             }
37602             
37603             var el = template.overwrite(td, {"text": text});
37604             
37605             var inner = el.getElementsByTagName("span")[0];
37606             
37607             return {"el": el, "inner": inner};
37608             
37609     }
37610         
37611     
37612 });
37613
37614 /**
37615  * @class Roo.TabPanelItem
37616  * @extends Roo.util.Observable
37617  * Represents an individual item (tab plus body) in a TabPanel.
37618  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37619  * @param {String} id The id of this TabPanelItem
37620  * @param {String} text The text for the tab of this TabPanelItem
37621  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37622  */
37623 Roo.bootstrap.panel.TabItem = function(config){
37624     /**
37625      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37626      * @type Roo.TabPanel
37627      */
37628     this.tabPanel = config.panel;
37629     /**
37630      * The id for this TabPanelItem
37631      * @type String
37632      */
37633     this.id = config.id;
37634     /** @private */
37635     this.disabled = false;
37636     /** @private */
37637     this.text = config.text;
37638     /** @private */
37639     this.loaded = false;
37640     this.closable = config.closable;
37641
37642     /**
37643      * The body element for this TabPanelItem.
37644      * @type Roo.Element
37645      */
37646     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37647     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37648     this.bodyEl.setStyle("display", "block");
37649     this.bodyEl.setStyle("zoom", "1");
37650     //this.hideAction();
37651
37652     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37653     /** @private */
37654     this.el = Roo.get(els.el);
37655     this.inner = Roo.get(els.inner, true);
37656     this.textEl = Roo.get(this.el.dom.firstChild, true);
37657     this.pnode = Roo.get(els.el.parentNode, true);
37658     this.el.on("mousedown", this.onTabMouseDown, this);
37659     this.el.on("click", this.onTabClick, this);
37660     /** @private */
37661     if(config.closable){
37662         var c = Roo.get(els.close, true);
37663         c.dom.title = this.closeText;
37664         c.addClassOnOver("close-over");
37665         c.on("click", this.closeClick, this);
37666      }
37667
37668     this.addEvents({
37669          /**
37670          * @event activate
37671          * Fires when this tab becomes the active tab.
37672          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37673          * @param {Roo.TabPanelItem} this
37674          */
37675         "activate": true,
37676         /**
37677          * @event beforeclose
37678          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37679          * @param {Roo.TabPanelItem} this
37680          * @param {Object} e Set cancel to true on this object to cancel the close.
37681          */
37682         "beforeclose": true,
37683         /**
37684          * @event close
37685          * Fires when this tab is closed.
37686          * @param {Roo.TabPanelItem} this
37687          */
37688          "close": true,
37689         /**
37690          * @event deactivate
37691          * Fires when this tab is no longer the active tab.
37692          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37693          * @param {Roo.TabPanelItem} this
37694          */
37695          "deactivate" : true
37696     });
37697     this.hidden = false;
37698
37699     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37700 };
37701
37702 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37703            {
37704     purgeListeners : function(){
37705        Roo.util.Observable.prototype.purgeListeners.call(this);
37706        this.el.removeAllListeners();
37707     },
37708     /**
37709      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37710      */
37711     show : function(){
37712         this.pnode.addClass("active");
37713         this.showAction();
37714         if(Roo.isOpera){
37715             this.tabPanel.stripWrap.repaint();
37716         }
37717         this.fireEvent("activate", this.tabPanel, this);
37718     },
37719
37720     /**
37721      * Returns true if this tab is the active tab.
37722      * @return {Boolean}
37723      */
37724     isActive : function(){
37725         return this.tabPanel.getActiveTab() == this;
37726     },
37727
37728     /**
37729      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37730      */
37731     hide : function(){
37732         this.pnode.removeClass("active");
37733         this.hideAction();
37734         this.fireEvent("deactivate", this.tabPanel, this);
37735     },
37736
37737     hideAction : function(){
37738         this.bodyEl.hide();
37739         this.bodyEl.setStyle("position", "absolute");
37740         this.bodyEl.setLeft("-20000px");
37741         this.bodyEl.setTop("-20000px");
37742     },
37743
37744     showAction : function(){
37745         this.bodyEl.setStyle("position", "relative");
37746         this.bodyEl.setTop("");
37747         this.bodyEl.setLeft("");
37748         this.bodyEl.show();
37749     },
37750
37751     /**
37752      * Set the tooltip for the tab.
37753      * @param {String} tooltip The tab's tooltip
37754      */
37755     setTooltip : function(text){
37756         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37757             this.textEl.dom.qtip = text;
37758             this.textEl.dom.removeAttribute('title');
37759         }else{
37760             this.textEl.dom.title = text;
37761         }
37762     },
37763
37764     onTabClick : function(e){
37765         e.preventDefault();
37766         this.tabPanel.activate(this.id);
37767     },
37768
37769     onTabMouseDown : function(e){
37770         e.preventDefault();
37771         this.tabPanel.activate(this.id);
37772     },
37773 /*
37774     getWidth : function(){
37775         return this.inner.getWidth();
37776     },
37777
37778     setWidth : function(width){
37779         var iwidth = width - this.pnode.getPadding("lr");
37780         this.inner.setWidth(iwidth);
37781         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37782         this.pnode.setWidth(width);
37783     },
37784 */
37785     /**
37786      * Show or hide the tab
37787      * @param {Boolean} hidden True to hide or false to show.
37788      */
37789     setHidden : function(hidden){
37790         this.hidden = hidden;
37791         this.pnode.setStyle("display", hidden ? "none" : "");
37792     },
37793
37794     /**
37795      * Returns true if this tab is "hidden"
37796      * @return {Boolean}
37797      */
37798     isHidden : function(){
37799         return this.hidden;
37800     },
37801
37802     /**
37803      * Returns the text for this tab
37804      * @return {String}
37805      */
37806     getText : function(){
37807         return this.text;
37808     },
37809     /*
37810     autoSize : function(){
37811         //this.el.beginMeasure();
37812         this.textEl.setWidth(1);
37813         /*
37814          *  #2804 [new] Tabs in Roojs
37815          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37816          */
37817         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37818         //this.el.endMeasure();
37819     //},
37820
37821     /**
37822      * Sets the text for the tab (Note: this also sets the tooltip text)
37823      * @param {String} text The tab's text and tooltip
37824      */
37825     setText : function(text){
37826         this.text = text;
37827         this.textEl.update(text);
37828         this.setTooltip(text);
37829         //if(!this.tabPanel.resizeTabs){
37830         //    this.autoSize();
37831         //}
37832     },
37833     /**
37834      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37835      */
37836     activate : function(){
37837         this.tabPanel.activate(this.id);
37838     },
37839
37840     /**
37841      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37842      */
37843     disable : function(){
37844         if(this.tabPanel.active != this){
37845             this.disabled = true;
37846             this.pnode.addClass("disabled");
37847         }
37848     },
37849
37850     /**
37851      * Enables this TabPanelItem if it was previously disabled.
37852      */
37853     enable : function(){
37854         this.disabled = false;
37855         this.pnode.removeClass("disabled");
37856     },
37857
37858     /**
37859      * Sets the content for this TabPanelItem.
37860      * @param {String} content The content
37861      * @param {Boolean} loadScripts true to look for and load scripts
37862      */
37863     setContent : function(content, loadScripts){
37864         this.bodyEl.update(content, loadScripts);
37865     },
37866
37867     /**
37868      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37869      * @return {Roo.UpdateManager} The UpdateManager
37870      */
37871     getUpdateManager : function(){
37872         return this.bodyEl.getUpdateManager();
37873     },
37874
37875     /**
37876      * Set a URL to be used to load the content for this TabPanelItem.
37877      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37878      * @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)
37879      * @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)
37880      * @return {Roo.UpdateManager} The UpdateManager
37881      */
37882     setUrl : function(url, params, loadOnce){
37883         if(this.refreshDelegate){
37884             this.un('activate', this.refreshDelegate);
37885         }
37886         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37887         this.on("activate", this.refreshDelegate);
37888         return this.bodyEl.getUpdateManager();
37889     },
37890
37891     /** @private */
37892     _handleRefresh : function(url, params, loadOnce){
37893         if(!loadOnce || !this.loaded){
37894             var updater = this.bodyEl.getUpdateManager();
37895             updater.update(url, params, this._setLoaded.createDelegate(this));
37896         }
37897     },
37898
37899     /**
37900      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37901      *   Will fail silently if the setUrl method has not been called.
37902      *   This does not activate the panel, just updates its content.
37903      */
37904     refresh : function(){
37905         if(this.refreshDelegate){
37906            this.loaded = false;
37907            this.refreshDelegate();
37908         }
37909     },
37910
37911     /** @private */
37912     _setLoaded : function(){
37913         this.loaded = true;
37914     },
37915
37916     /** @private */
37917     closeClick : function(e){
37918         var o = {};
37919         e.stopEvent();
37920         this.fireEvent("beforeclose", this, o);
37921         if(o.cancel !== true){
37922             this.tabPanel.removeTab(this.id);
37923         }
37924     },
37925     /**
37926      * The text displayed in the tooltip for the close icon.
37927      * @type String
37928      */
37929     closeText : "Close this tab"
37930 });
37931 Roo.bootstrap.PhoneInput = function(config) {
37932     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37933 };
37934
37935 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField,  {
37936         
37937         triggerList : true,
37938         
37939         listWidth: undefined,
37940         
37941         getAutoCreate : function()
37942         {
37943             var align = this.labelAlign || this.parentLabelAlign();
37944             var id = Roo.id();
37945             
37946             var cfg = {
37947                 cls: 'form-group', //input-group
37948                 cn: []
37949             };
37950             
37951             var input =  {
37952                 tag: 'input',
37953                 id : id,
37954                 type : 'tel',
37955                 cls : 'form-control tel-input',
37956                 autocomplete: 'new-password'
37957                 //placeholder : this.placeholder || '' 
37958             };
37959             
37960             if (this.name) {
37961                 input.name = this.name;
37962             }
37963             
37964             if (this.disabled) {
37965                 input.disabled=true;
37966             }
37967             
37968             var flag_container = {
37969                 tag: 'div',
37970                 cls: 'flag-container',
37971                 cn: [
37972                     {
37973                         tag: 'div',
37974                         cls: 'flag'
37975                     },
37976                     {
37977                         tag: 'div',
37978                         cls: 'caret'
37979                     }
37980                 ]
37981             };
37982             
37983             var box = {
37984                 tag: 'div',
37985                 cls: this.hasFeedback ? 'has-feedback' : '',
37986                 cn: [
37987                     input
37988                 ]
37989             };
37990             
37991             var container = {
37992                 cls: 'roo-select2-container input-group',
37993                 cn: [
37994                     flag_container,
37995                     box
37996                 ]
37997             };
37998             
37999             if (this.fieldLabel.length) {
38000                 var indicator = {
38001                     tag: 'i',
38002                     tooltip: 'This field is required'
38003                 };
38004                 
38005                 var label = {
38006                     tag: 'label',
38007                     'for':  id,
38008                     cls: 'control-label',
38009                     cn: []
38010                 };
38011                 
38012                 var label_text = {
38013                     tag: 'span',
38014                     html: this.fieldLabel
38015                 };
38016                 
38017                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
38018                 label.cn = [
38019                     indicator,
38020                     label_text
38021                 ];
38022                 
38023                 if(this.indicatorpos == 'right') {
38024                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
38025                     label.cn = [
38026                         label_text,
38027                         indicator
38028                     ];
38029                 }
38030                 
38031                 if(align == 'left') {
38032                     container = {
38033                         tag: 'div',
38034                         cn: [
38035                             container
38036                         ]
38037                     };
38038                     
38039                     if(this.labelWidth > 12){
38040                         label.style = "width: " + this.labelWidth + 'px';
38041                     }
38042                     if(this.labelWidth < 13 && this.labelmd == 0){
38043                         this.labelmd = this.labelWidth;
38044                     }
38045                     if(this.labellg > 0){
38046                         label.cls += ' col-lg-' + this.labellg;
38047                         input.cls += ' col-lg-' + (12 - this.labellg);
38048                     }
38049                     if(this.labelmd > 0){
38050                         label.cls += ' col-md-' + this.labelmd;
38051                         container.cls += ' col-md-' + (12 - this.labelmd);
38052                     }
38053                     if(this.labelsm > 0){
38054                         label.cls += ' col-sm-' + this.labelsm;
38055                         container.cls += ' col-sm-' + (12 - this.labelsm);
38056                     }
38057                     if(this.labelxs > 0){
38058                         label.cls += ' col-xs-' + this.labelxs;
38059                         container.cls += ' col-xs-' + (12 - this.labelxs);
38060                     }
38061                 }
38062             }
38063             
38064             cfg.cn = [
38065                 label,
38066                 container
38067             ];
38068             
38069             
38070             var settings = this;
38071             
38072             ['xs','sm','md','lg'].map(function(size){
38073                 if (settings[size]) {
38074                     cfg.cls += ' col-' + size + '-' + settings[size];
38075                 }
38076             });
38077             
38078             return cfg;
38079         },
38080         
38081         initEvents : function()
38082         {
38083             this.createList();
38084             
38085             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38086             
38087             this.trigger = this.el.select('div.flag-container',true).first();
38088             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38089         },
38090         
38091         onTriggerClick : function(e)
38092         {
38093             Roo.log('trigger click');
38094             
38095             if(this.disabled || !this.triggerList){
38096                 return;
38097             }
38098             
38099             this.page = 0;
38100             this.loadNext = false;
38101             
38102             if(this.isExpanded()){
38103                 this.collapse();
38104             }else {
38105                 this.hasFocus = true;
38106                 if(this.triggerAction == 'all') {
38107                     //Original data flow: doQuery() -> store.load() -> proxy.load() -> store.loadRecords()
38108                 } else {
38109                     
38110                 }
38111                 if (!this.blockFocus) {
38112                     this.inputEl().focus();
38113                 }
38114             }
38115         },
38116         
38117         isExpanded : function(){
38118             return this.list.isVisible();
38119         },
38120         
38121         collapse : function(){
38122             if(!this.isExpanded()){
38123                 return;
38124             }
38125             this.list.hide();
38126             this.hasFocus = false;
38127             Roo.get(document).un('mousedown', this.collapseIf, this);
38128             Roo.get(document).un('mousewheel', this.collapseIf, this);
38129             this.fireEvent('collapse', this);
38130             
38131             this.validate();
38132         },
38133         
38134         expand : function(){
38135            
38136             if(this.isExpanded() || !this.hasFocus){
38137                 return;
38138             }
38139             
38140             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
38141             this.list.setWidth(lw);
38142             
38143             Roo.log('expand');
38144             
38145             this.list.show();
38146             
38147             this.restrictHeight();
38148             
38149             Roo.get(document).on('mousedown', this.collapseIf, this);
38150             Roo.get(document).on('mousewheel', this.collapseIf, this);
38151             
38152             this.fireEvent('expand', this);
38153         },
38154         
38155         restrictHeight : function(){
38156             //this.innerList.dom.style.height = '';
38157             //var inner = this.innerList.dom;
38158             //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38159             //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38160             //this.list.beginUpdate();
38161             //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38162             this.list.alignTo(this.inputEl(), this.listAlign);
38163             this.list.alignTo(this.inputEl(), this.listAlign);
38164             //this.list.endUpdate();
38165         }
38166 });