85309aff02ce4b80d7d5d24608f2433b66602a8a
[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         if(this.multiple){
13734             this.addItem(o);
13735             return;
13736         }
13737             
13738         var dv = ''; // display value
13739         var vv = ''; // value value..
13740         this.lastData = o;
13741         if (this.displayField) {
13742             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13743         } else {
13744             // this is an error condition!!!
13745             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13746         }
13747         
13748         if(this.valueField){
13749             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13750         }
13751         
13752         var close = this.closeTriggerEl();
13753         
13754         if(close){
13755             if(dv.length || vv * 1 > 0){
13756                 close.show() ;
13757                 this.blockFocus=true;
13758             } else {
13759                 close.hide();
13760             }             
13761         }
13762         
13763         if(this.hiddenField){
13764             this.hiddenField.dom.value = vv;
13765             
13766             this.lastSelectionText = dv;
13767             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13768             this.value = vv;
13769             return;
13770         }
13771         // no hidden field.. - we store the value in 'value', but still display
13772         // display field!!!!
13773         this.lastSelectionText = dv;
13774         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13775         this.value = vv;
13776         
13777         
13778         
13779     },
13780     // private
13781     reset : function(){
13782         // overridden so that last data is reset..
13783         
13784         if(this.multiple){
13785             this.clearItem();
13786             return;
13787         }
13788         
13789         this.setValue(this.originalValue);
13790         //this.clearInvalid();
13791         this.lastData = false;
13792         if (this.view) {
13793             this.view.clearSelections();
13794         }
13795         
13796         this.validate();
13797     },
13798     // private
13799     findRecord : function(prop, value){
13800         var record;
13801         if(this.store.getCount() > 0){
13802             this.store.each(function(r){
13803                 if(r.data[prop] == value){
13804                     record = r;
13805                     return false;
13806                 }
13807                 return true;
13808             });
13809         }
13810         return record;
13811     },
13812     
13813     getName: function()
13814     {
13815         // returns hidden if it's set..
13816         if (!this.rendered) {return ''};
13817         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13818         
13819     },
13820     // private
13821     onViewMove : function(e, t){
13822         this.inKeyMode = false;
13823     },
13824
13825     // private
13826     onViewOver : function(e, t){
13827         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13828             return;
13829         }
13830         var item = this.view.findItemFromChild(t);
13831         
13832         if(item){
13833             var index = this.view.indexOf(item);
13834             this.select(index, false);
13835         }
13836     },
13837
13838     // private
13839     onViewClick : function(view, doFocus, el, e)
13840     {
13841         var index = this.view.getSelectedIndexes()[0];
13842         
13843         var r = this.store.getAt(index);
13844         
13845         if(this.tickable){
13846             
13847             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13848                 return;
13849             }
13850             
13851             var rm = false;
13852             var _this = this;
13853             
13854             Roo.each(this.tickItems, function(v,k){
13855                 
13856                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13857                     Roo.log(v);
13858                     _this.tickItems.splice(k, 1);
13859                     
13860                     if(typeof(e) == 'undefined' && view == false){
13861                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13862                     }
13863                     
13864                     rm = true;
13865                     return;
13866                 }
13867             });
13868             
13869             if(rm){
13870                 return;
13871             }
13872             
13873             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13874                 this.tickItems.push(r.data);
13875             }
13876             
13877             if(typeof(e) == 'undefined' && view == false){
13878                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13879             }
13880                     
13881             return;
13882         }
13883         
13884         if(r){
13885             this.onSelect(r, index);
13886         }
13887         if(doFocus !== false && !this.blockFocus){
13888             this.inputEl().focus();
13889         }
13890     },
13891
13892     // private
13893     restrictHeight : function(){
13894         //this.innerList.dom.style.height = '';
13895         //var inner = this.innerList.dom;
13896         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13897         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13898         //this.list.beginUpdate();
13899         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13900         this.list.alignTo(this.inputEl(), this.listAlign);
13901         this.list.alignTo(this.inputEl(), this.listAlign);
13902         //this.list.endUpdate();
13903     },
13904
13905     // private
13906     onEmptyResults : function(){
13907         
13908         if(this.tickable && this.editable){
13909             this.restrictHeight();
13910             return;
13911         }
13912         
13913         this.collapse();
13914     },
13915
13916     /**
13917      * Returns true if the dropdown list is expanded, else false.
13918      */
13919     isExpanded : function(){
13920         return this.list.isVisible();
13921     },
13922
13923     /**
13924      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13925      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13926      * @param {String} value The data value of the item to select
13927      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13928      * selected item if it is not currently in view (defaults to true)
13929      * @return {Boolean} True if the value matched an item in the list, else false
13930      */
13931     selectByValue : function(v, scrollIntoView){
13932         if(v !== undefined && v !== null){
13933             var r = this.findRecord(this.valueField || this.displayField, v);
13934             if(r){
13935                 this.select(this.store.indexOf(r), scrollIntoView);
13936                 return true;
13937             }
13938         }
13939         return false;
13940     },
13941
13942     /**
13943      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13944      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13945      * @param {Number} index The zero-based index of the list item to select
13946      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13947      * selected item if it is not currently in view (defaults to true)
13948      */
13949     select : function(index, scrollIntoView){
13950         this.selectedIndex = index;
13951         this.view.select(index);
13952         if(scrollIntoView !== false){
13953             var el = this.view.getNode(index);
13954             /*
13955              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13956              */
13957             if(el){
13958                 this.list.scrollChildIntoView(el, false);
13959             }
13960         }
13961     },
13962
13963     // private
13964     selectNext : function(){
13965         var ct = this.store.getCount();
13966         if(ct > 0){
13967             if(this.selectedIndex == -1){
13968                 this.select(0);
13969             }else if(this.selectedIndex < ct-1){
13970                 this.select(this.selectedIndex+1);
13971             }
13972         }
13973     },
13974
13975     // private
13976     selectPrev : function(){
13977         var ct = this.store.getCount();
13978         if(ct > 0){
13979             if(this.selectedIndex == -1){
13980                 this.select(0);
13981             }else if(this.selectedIndex != 0){
13982                 this.select(this.selectedIndex-1);
13983             }
13984         }
13985     },
13986
13987     // private
13988     onKeyUp : function(e){
13989         if(this.editable !== false && !e.isSpecialKey()){
13990             this.lastKey = e.getKey();
13991             this.dqTask.delay(this.queryDelay);
13992         }
13993     },
13994
13995     // private
13996     validateBlur : function(){
13997         return !this.list || !this.list.isVisible();   
13998     },
13999
14000     // private
14001     initQuery : function(){
14002         
14003         var v = this.getRawValue();
14004         
14005         if(this.tickable && this.editable){
14006             v = this.tickableInputEl().getValue();
14007         }
14008         
14009         this.doQuery(v);
14010     },
14011
14012     // private
14013     doForce : function(){
14014         if(this.inputEl().dom.value.length > 0){
14015             this.inputEl().dom.value =
14016                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14017              
14018         }
14019     },
14020
14021     /**
14022      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14023      * query allowing the query action to be canceled if needed.
14024      * @param {String} query The SQL query to execute
14025      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14026      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14027      * saved in the current store (defaults to false)
14028      */
14029     doQuery : function(q, forceAll){
14030         
14031         if(q === undefined || q === null){
14032             q = '';
14033         }
14034         var qe = {
14035             query: q,
14036             forceAll: forceAll,
14037             combo: this,
14038             cancel:false
14039         };
14040         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14041             return false;
14042         }
14043         q = qe.query;
14044         
14045         forceAll = qe.forceAll;
14046         if(forceAll === true || (q.length >= this.minChars)){
14047             
14048             this.hasQuery = true;
14049             
14050             if(this.lastQuery != q || this.alwaysQuery){
14051                 this.lastQuery = q;
14052                 if(this.mode == 'local'){
14053                     this.selectedIndex = -1;
14054                     if(forceAll){
14055                         this.store.clearFilter();
14056                     }else{
14057                         
14058                         if(this.specialFilter){
14059                             this.fireEvent('specialfilter', this);
14060                             this.onLoad();
14061                             return;
14062                         }
14063                         
14064                         this.store.filter(this.displayField, q);
14065                     }
14066                     
14067                     this.store.fireEvent("datachanged", this.store);
14068                     
14069                     this.onLoad();
14070                     
14071                     
14072                 }else{
14073                     
14074                     this.store.baseParams[this.queryParam] = q;
14075                     
14076                     var options = {params : this.getParams(q)};
14077                     
14078                     if(this.loadNext){
14079                         options.add = true;
14080                         options.params.start = this.page * this.pageSize;
14081                     }
14082                     
14083                     this.store.load(options);
14084                     
14085                     /*
14086                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14087                      *  we should expand the list on onLoad
14088                      *  so command out it
14089                      */
14090 //                    this.expand();
14091                 }
14092             }else{
14093                 this.selectedIndex = -1;
14094                 this.onLoad();   
14095             }
14096         }
14097         
14098         this.loadNext = false;
14099     },
14100     
14101     // private
14102     getParams : function(q){
14103         var p = {};
14104         //p[this.queryParam] = q;
14105         
14106         if(this.pageSize){
14107             p.start = 0;
14108             p.limit = this.pageSize;
14109         }
14110         return p;
14111     },
14112
14113     /**
14114      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14115      */
14116     collapse : function(){
14117         if(!this.isExpanded()){
14118             return;
14119         }
14120         
14121         this.list.hide();
14122         
14123         this.hasFocus = false;
14124         
14125         if(this.tickable){
14126             this.okBtn.hide();
14127             this.cancelBtn.hide();
14128             this.trigger.show();
14129             
14130             if(this.editable){
14131                 this.tickableInputEl().dom.value = '';
14132                 this.tickableInputEl().blur();
14133             }
14134             
14135         }
14136         
14137         Roo.get(document).un('mousedown', this.collapseIf, this);
14138         Roo.get(document).un('mousewheel', this.collapseIf, this);
14139         if (!this.editable) {
14140             Roo.get(document).un('keydown', this.listKeyPress, this);
14141         }
14142         this.fireEvent('collapse', this);
14143         
14144         this.validate();
14145     },
14146
14147     // private
14148     collapseIf : function(e){
14149         var in_combo  = e.within(this.el);
14150         var in_list =  e.within(this.list);
14151         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14152         
14153         if (in_combo || in_list || is_list) {
14154             //e.stopPropagation();
14155             return;
14156         }
14157         
14158         if(this.tickable){
14159             this.onTickableFooterButtonClick(e, false, false);
14160         }
14161
14162         this.collapse();
14163         
14164     },
14165
14166     /**
14167      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14168      */
14169     expand : function(){
14170        
14171         if(this.isExpanded() || !this.hasFocus){
14172             return;
14173         }
14174         
14175         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14176         this.list.setWidth(lw);
14177         
14178         Roo.log('expand');
14179         
14180         this.list.show();
14181         
14182         this.restrictHeight();
14183         
14184         if(this.tickable){
14185             
14186             this.tickItems = Roo.apply([], this.item);
14187             
14188             this.okBtn.show();
14189             this.cancelBtn.show();
14190             this.trigger.hide();
14191             
14192             if(this.editable){
14193                 this.tickableInputEl().focus();
14194             }
14195             
14196         }
14197         
14198         Roo.get(document).on('mousedown', this.collapseIf, this);
14199         Roo.get(document).on('mousewheel', this.collapseIf, this);
14200         if (!this.editable) {
14201             Roo.get(document).on('keydown', this.listKeyPress, this);
14202         }
14203         
14204         this.fireEvent('expand', this);
14205     },
14206
14207     // private
14208     // Implements the default empty TriggerField.onTriggerClick function
14209     onTriggerClick : function(e)
14210     {
14211         Roo.log('trigger click');
14212         
14213         if(this.disabled || !this.triggerList){
14214             return;
14215         }
14216         
14217         this.page = 0;
14218         this.loadNext = false;
14219         
14220         if(this.isExpanded()){
14221             this.collapse();
14222             if (!this.blockFocus) {
14223                 this.inputEl().focus();
14224             }
14225             
14226         }else {
14227             this.hasFocus = true;
14228             if(this.triggerAction == 'all') {
14229                 this.doQuery(this.allQuery, true);
14230             } else {
14231                 this.doQuery(this.getRawValue());
14232             }
14233             if (!this.blockFocus) {
14234                 this.inputEl().focus();
14235             }
14236         }
14237     },
14238     
14239     onTickableTriggerClick : function(e)
14240     {
14241         if(this.disabled){
14242             return;
14243         }
14244         
14245         this.page = 0;
14246         this.loadNext = false;
14247         this.hasFocus = true;
14248         
14249         if(this.triggerAction == 'all') {
14250             this.doQuery(this.allQuery, true);
14251         } else {
14252             this.doQuery(this.getRawValue());
14253         }
14254     },
14255     
14256     onSearchFieldClick : function(e)
14257     {
14258         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14259             this.onTickableFooterButtonClick(e, false, false);
14260             return;
14261         }
14262         
14263         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14264             return;
14265         }
14266         
14267         this.page = 0;
14268         this.loadNext = false;
14269         this.hasFocus = true;
14270         
14271         if(this.triggerAction == 'all') {
14272             this.doQuery(this.allQuery, true);
14273         } else {
14274             this.doQuery(this.getRawValue());
14275         }
14276     },
14277     
14278     listKeyPress : function(e)
14279     {
14280         //Roo.log('listkeypress');
14281         // scroll to first matching element based on key pres..
14282         if (e.isSpecialKey()) {
14283             return false;
14284         }
14285         var k = String.fromCharCode(e.getKey()).toUpperCase();
14286         //Roo.log(k);
14287         var match  = false;
14288         var csel = this.view.getSelectedNodes();
14289         var cselitem = false;
14290         if (csel.length) {
14291             var ix = this.view.indexOf(csel[0]);
14292             cselitem  = this.store.getAt(ix);
14293             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14294                 cselitem = false;
14295             }
14296             
14297         }
14298         
14299         this.store.each(function(v) { 
14300             if (cselitem) {
14301                 // start at existing selection.
14302                 if (cselitem.id == v.id) {
14303                     cselitem = false;
14304                 }
14305                 return true;
14306             }
14307                 
14308             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14309                 match = this.store.indexOf(v);
14310                 return false;
14311             }
14312             return true;
14313         }, this);
14314         
14315         if (match === false) {
14316             return true; // no more action?
14317         }
14318         // scroll to?
14319         this.view.select(match);
14320         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14321         sn.scrollIntoView(sn.dom.parentNode, false);
14322     },
14323     
14324     onViewScroll : function(e, t){
14325         
14326         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){
14327             return;
14328         }
14329         
14330         this.hasQuery = true;
14331         
14332         this.loading = this.list.select('.loading', true).first();
14333         
14334         if(this.loading === null){
14335             this.list.createChild({
14336                 tag: 'div',
14337                 cls: 'loading roo-select2-more-results roo-select2-active',
14338                 html: 'Loading more results...'
14339             });
14340             
14341             this.loading = this.list.select('.loading', true).first();
14342             
14343             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14344             
14345             this.loading.hide();
14346         }
14347         
14348         this.loading.show();
14349         
14350         var _combo = this;
14351         
14352         this.page++;
14353         this.loadNext = true;
14354         
14355         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14356         
14357         return;
14358     },
14359     
14360     addItem : function(o)
14361     {   
14362         var dv = ''; // display value
14363         
14364         if (this.displayField) {
14365             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14366         } else {
14367             // this is an error condition!!!
14368             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14369         }
14370         
14371         if(!dv.length){
14372             return;
14373         }
14374         
14375         var choice = this.choices.createChild({
14376             tag: 'li',
14377             cls: 'roo-select2-search-choice',
14378             cn: [
14379                 {
14380                     tag: 'div',
14381                     html: dv
14382                 },
14383                 {
14384                     tag: 'a',
14385                     href: '#',
14386                     cls: 'roo-select2-search-choice-close fa fa-times',
14387                     tabindex: '-1'
14388                 }
14389             ]
14390             
14391         }, this.searchField);
14392         
14393         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14394         
14395         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14396         
14397         this.item.push(o);
14398         
14399         this.lastData = o;
14400         
14401         this.syncValue();
14402         
14403         this.inputEl().dom.value = '';
14404         
14405         this.validate();
14406     },
14407     
14408     onRemoveItem : function(e, _self, o)
14409     {
14410         e.preventDefault();
14411         
14412         this.lastItem = Roo.apply([], this.item);
14413         
14414         var index = this.item.indexOf(o.data) * 1;
14415         
14416         if( index < 0){
14417             Roo.log('not this item?!');
14418             return;
14419         }
14420         
14421         this.item.splice(index, 1);
14422         o.item.remove();
14423         
14424         this.syncValue();
14425         
14426         this.fireEvent('remove', this, e);
14427         
14428         this.validate();
14429         
14430     },
14431     
14432     syncValue : function()
14433     {
14434         if(!this.item.length){
14435             this.clearValue();
14436             return;
14437         }
14438             
14439         var value = [];
14440         var _this = this;
14441         Roo.each(this.item, function(i){
14442             if(_this.valueField){
14443                 value.push(i[_this.valueField]);
14444                 return;
14445             }
14446
14447             value.push(i);
14448         });
14449
14450         this.value = value.join(',');
14451
14452         if(this.hiddenField){
14453             this.hiddenField.dom.value = this.value;
14454         }
14455         
14456         this.store.fireEvent("datachanged", this.store);
14457         
14458         this.validate();
14459     },
14460     
14461     clearItem : function()
14462     {
14463         if(!this.multiple){
14464             return;
14465         }
14466         
14467         this.item = [];
14468         
14469         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14470            c.remove();
14471         });
14472         
14473         this.syncValue();
14474         
14475         this.validate();
14476         
14477         if(this.tickable && !Roo.isTouch){
14478             this.view.refresh();
14479         }
14480     },
14481     
14482     inputEl: function ()
14483     {
14484         if(Roo.isIOS && this.useNativeIOS){
14485             return this.el.select('select.roo-ios-select', true).first();
14486         }
14487         
14488         if(Roo.isTouch && this.mobileTouchView){
14489             return this.el.select('input.form-control',true).first();
14490         }
14491         
14492         if(this.tickable){
14493             return this.searchField;
14494         }
14495         
14496         return this.el.select('input.form-control',true).first();
14497     },
14498     
14499     onTickableFooterButtonClick : function(e, btn, el)
14500     {
14501         e.preventDefault();
14502         
14503         this.lastItem = Roo.apply([], this.item);
14504         
14505         if(btn && btn.name == 'cancel'){
14506             this.tickItems = Roo.apply([], this.item);
14507             this.collapse();
14508             return;
14509         }
14510         
14511         this.clearItem();
14512         
14513         var _this = this;
14514         
14515         Roo.each(this.tickItems, function(o){
14516             _this.addItem(o);
14517         });
14518         
14519         this.collapse();
14520         
14521     },
14522     
14523     validate : function()
14524     {
14525         var v = this.getRawValue();
14526         
14527         if(this.multiple){
14528             v = this.getValue();
14529         }
14530         
14531         if(this.disabled || this.allowBlank || v.length){
14532             this.markValid();
14533             return true;
14534         }
14535         
14536         this.markInvalid();
14537         return false;
14538     },
14539     
14540     tickableInputEl : function()
14541     {
14542         if(!this.tickable || !this.editable){
14543             return this.inputEl();
14544         }
14545         
14546         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14547     },
14548     
14549     
14550     getAutoCreateTouchView : function()
14551     {
14552         var id = Roo.id();
14553         
14554         var cfg = {
14555             cls: 'form-group' //input-group
14556         };
14557         
14558         var input =  {
14559             tag: 'input',
14560             id : id,
14561             type : this.inputType,
14562             cls : 'form-control x-combo-noedit',
14563             autocomplete: 'new-password',
14564             placeholder : this.placeholder || '',
14565             readonly : true
14566         };
14567         
14568         if (this.name) {
14569             input.name = this.name;
14570         }
14571         
14572         if (this.size) {
14573             input.cls += ' input-' + this.size;
14574         }
14575         
14576         if (this.disabled) {
14577             input.disabled = true;
14578         }
14579         
14580         var inputblock = {
14581             cls : '',
14582             cn : [
14583                 input
14584             ]
14585         };
14586         
14587         if(this.before){
14588             inputblock.cls += ' input-group';
14589             
14590             inputblock.cn.unshift({
14591                 tag :'span',
14592                 cls : 'input-group-addon',
14593                 html : this.before
14594             });
14595         }
14596         
14597         if(this.removable && !this.multiple){
14598             inputblock.cls += ' roo-removable';
14599             
14600             inputblock.cn.push({
14601                 tag: 'button',
14602                 html : 'x',
14603                 cls : 'roo-combo-removable-btn close'
14604             });
14605         }
14606
14607         if(this.hasFeedback && !this.allowBlank){
14608             
14609             inputblock.cls += ' has-feedback';
14610             
14611             inputblock.cn.push({
14612                 tag: 'span',
14613                 cls: 'glyphicon form-control-feedback'
14614             });
14615             
14616         }
14617         
14618         if (this.after) {
14619             
14620             inputblock.cls += (this.before) ? '' : ' input-group';
14621             
14622             inputblock.cn.push({
14623                 tag :'span',
14624                 cls : 'input-group-addon',
14625                 html : this.after
14626             });
14627         }
14628
14629         var box = {
14630             tag: 'div',
14631             cn: [
14632                 {
14633                     tag: 'input',
14634                     type : 'hidden',
14635                     cls: 'form-hidden-field'
14636                 },
14637                 inputblock
14638             ]
14639             
14640         };
14641         
14642         if(this.multiple){
14643             box = {
14644                 tag: 'div',
14645                 cn: [
14646                     {
14647                         tag: 'input',
14648                         type : 'hidden',
14649                         cls: 'form-hidden-field'
14650                     },
14651                     {
14652                         tag: 'ul',
14653                         cls: 'roo-select2-choices',
14654                         cn:[
14655                             {
14656                                 tag: 'li',
14657                                 cls: 'roo-select2-search-field',
14658                                 cn: [
14659
14660                                     inputblock
14661                                 ]
14662                             }
14663                         ]
14664                     }
14665                 ]
14666             }
14667         };
14668         
14669         var combobox = {
14670             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14671             cn: [
14672                 box
14673             ]
14674         };
14675         
14676         if(!this.multiple && this.showToggleBtn){
14677             
14678             var caret = {
14679                         tag: 'span',
14680                         cls: 'caret'
14681             };
14682             
14683             if (this.caret != false) {
14684                 caret = {
14685                      tag: 'i',
14686                      cls: 'fa fa-' + this.caret
14687                 };
14688                 
14689             }
14690             
14691             combobox.cn.push({
14692                 tag :'span',
14693                 cls : 'input-group-addon btn dropdown-toggle',
14694                 cn : [
14695                     caret,
14696                     {
14697                         tag: 'span',
14698                         cls: 'combobox-clear',
14699                         cn  : [
14700                             {
14701                                 tag : 'i',
14702                                 cls: 'icon-remove'
14703                             }
14704                         ]
14705                     }
14706                 ]
14707
14708             })
14709         }
14710         
14711         if(this.multiple){
14712             combobox.cls += ' roo-select2-container-multi';
14713         }
14714         
14715         var align = this.labelAlign || this.parentLabelAlign();
14716         
14717         if (align ==='left' && this.fieldLabel.length) {
14718
14719             cfg.cn = [
14720                 {
14721                    tag : 'i',
14722                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14723                    tooltip : 'This field is required'
14724                 },
14725                 {
14726                     tag: 'label',
14727                     cls : 'control-label',
14728                     html : this.fieldLabel
14729
14730                 },
14731                 {
14732                     cls : '', 
14733                     cn: [
14734                         combobox
14735                     ]
14736                 }
14737             ];
14738             
14739             var labelCfg = cfg.cn[1];
14740             var contentCfg = cfg.cn[2];
14741             
14742
14743             if(this.indicatorpos == 'right'){
14744                 cfg.cn = [
14745                     {
14746                         tag: 'label',
14747                         'for' :  id,
14748                         cls : 'control-label',
14749                         cn : [
14750                             {
14751                                 tag : 'span',
14752                                 html : this.fieldLabel
14753                             },
14754                             {
14755                                 tag : 'i',
14756                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14757                                 tooltip : 'This field is required'
14758                             }
14759                         ]
14760                     },
14761                     {
14762                         cls : "",
14763                         cn: [
14764                             combobox
14765                         ]
14766                     }
14767
14768                 ];
14769                 
14770                 labelCfg = cfg.cn[0];
14771                 contentCfg = cfg.cn[1];
14772             }
14773             
14774            
14775             
14776             if(this.labelWidth > 12){
14777                 labelCfg.style = "width: " + this.labelWidth + 'px';
14778             }
14779             
14780             if(this.labelWidth < 13 && this.labelmd == 0){
14781                 this.labelmd = this.labelWidth;
14782             }
14783             
14784             if(this.labellg > 0){
14785                 labelCfg.cls += ' col-lg-' + this.labellg;
14786                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14787             }
14788             
14789             if(this.labelmd > 0){
14790                 labelCfg.cls += ' col-md-' + this.labelmd;
14791                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14792             }
14793             
14794             if(this.labelsm > 0){
14795                 labelCfg.cls += ' col-sm-' + this.labelsm;
14796                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14797             }
14798             
14799             if(this.labelxs > 0){
14800                 labelCfg.cls += ' col-xs-' + this.labelxs;
14801                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14802             }
14803                 
14804                 
14805         } else if ( this.fieldLabel.length) {
14806             cfg.cn = [
14807                 {
14808                    tag : 'i',
14809                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14810                    tooltip : 'This field is required'
14811                 },
14812                 {
14813                     tag: 'label',
14814                     cls : 'control-label',
14815                     html : this.fieldLabel
14816
14817                 },
14818                 {
14819                     cls : '', 
14820                     cn: [
14821                         combobox
14822                     ]
14823                 }
14824             ];
14825             
14826             if(this.indicatorpos == 'right'){
14827                 cfg.cn = [
14828                     {
14829                         tag: 'label',
14830                         cls : 'control-label',
14831                         html : this.fieldLabel,
14832                         cn : [
14833                             {
14834                                tag : 'i',
14835                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14836                                tooltip : 'This field is required'
14837                             }
14838                         ]
14839                     },
14840                     {
14841                         cls : '', 
14842                         cn: [
14843                             combobox
14844                         ]
14845                     }
14846                 ];
14847             }
14848         } else {
14849             cfg.cn = combobox;    
14850         }
14851         
14852         
14853         var settings = this;
14854         
14855         ['xs','sm','md','lg'].map(function(size){
14856             if (settings[size]) {
14857                 cfg.cls += ' col-' + size + '-' + settings[size];
14858             }
14859         });
14860         
14861         return cfg;
14862     },
14863     
14864     initTouchView : function()
14865     {
14866         this.renderTouchView();
14867         
14868         this.touchViewEl.on('scroll', function(){
14869             this.el.dom.scrollTop = 0;
14870         }, this);
14871         
14872         this.originalValue = this.getValue();
14873         
14874         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14875         
14876         this.inputEl().on("click", this.showTouchView, this);
14877         if (this.triggerEl) {
14878             this.triggerEl.on("click", this.showTouchView, this);
14879         }
14880         
14881         
14882         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14883         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14884         
14885         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14886         
14887         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14888         this.store.on('load', this.onTouchViewLoad, this);
14889         this.store.on('loadexception', this.onTouchViewLoadException, this);
14890         
14891         if(this.hiddenName){
14892             
14893             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14894             
14895             this.hiddenField.dom.value =
14896                 this.hiddenValue !== undefined ? this.hiddenValue :
14897                 this.value !== undefined ? this.value : '';
14898         
14899             this.el.dom.removeAttribute('name');
14900             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14901         }
14902         
14903         if(this.multiple){
14904             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14905             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14906         }
14907         
14908         if(this.removable && !this.multiple){
14909             var close = this.closeTriggerEl();
14910             if(close){
14911                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14912                 close.on('click', this.removeBtnClick, this, close);
14913             }
14914         }
14915         /*
14916          * fix the bug in Safari iOS8
14917          */
14918         this.inputEl().on("focus", function(e){
14919             document.activeElement.blur();
14920         }, this);
14921         
14922         return;
14923         
14924         
14925     },
14926     
14927     renderTouchView : function()
14928     {
14929         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14930         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14931         
14932         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14933         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14934         
14935         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14936         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14937         this.touchViewBodyEl.setStyle('overflow', 'auto');
14938         
14939         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14940         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14941         
14942         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14943         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14944         
14945     },
14946     
14947     showTouchView : function()
14948     {
14949         if(this.disabled){
14950             return;
14951         }
14952         
14953         this.touchViewHeaderEl.hide();
14954
14955         if(this.modalTitle.length){
14956             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14957             this.touchViewHeaderEl.show();
14958         }
14959
14960         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14961         this.touchViewEl.show();
14962
14963         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14964         
14965         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14966         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14967
14968         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14969
14970         if(this.modalTitle.length){
14971             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14972         }
14973         
14974         this.touchViewBodyEl.setHeight(bodyHeight);
14975
14976         if(this.animate){
14977             var _this = this;
14978             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14979         }else{
14980             this.touchViewEl.addClass('in');
14981         }
14982
14983         this.doTouchViewQuery();
14984         
14985     },
14986     
14987     hideTouchView : function()
14988     {
14989         this.touchViewEl.removeClass('in');
14990
14991         if(this.animate){
14992             var _this = this;
14993             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14994         }else{
14995             this.touchViewEl.setStyle('display', 'none');
14996         }
14997         
14998     },
14999     
15000     setTouchViewValue : function()
15001     {
15002         if(this.multiple){
15003             this.clearItem();
15004         
15005             var _this = this;
15006
15007             Roo.each(this.tickItems, function(o){
15008                 this.addItem(o);
15009             }, this);
15010         }
15011         
15012         this.hideTouchView();
15013     },
15014     
15015     doTouchViewQuery : function()
15016     {
15017         var qe = {
15018             query: '',
15019             forceAll: true,
15020             combo: this,
15021             cancel:false
15022         };
15023         
15024         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15025             return false;
15026         }
15027         
15028         if(!this.alwaysQuery || this.mode == 'local'){
15029             this.onTouchViewLoad();
15030             return;
15031         }
15032         
15033         this.store.load();
15034     },
15035     
15036     onTouchViewBeforeLoad : function(combo,opts)
15037     {
15038         return;
15039     },
15040
15041     // private
15042     onTouchViewLoad : function()
15043     {
15044         if(this.store.getCount() < 1){
15045             this.onTouchViewEmptyResults();
15046             return;
15047         }
15048         
15049         this.clearTouchView();
15050         
15051         var rawValue = this.getRawValue();
15052         
15053         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15054         
15055         this.tickItems = [];
15056         
15057         this.store.data.each(function(d, rowIndex){
15058             var row = this.touchViewListGroup.createChild(template);
15059             
15060             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15061                 row.addClass(d.data.cls);
15062             }
15063             
15064             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15065                 var cfg = {
15066                     data : d.data,
15067                     html : d.data[this.displayField]
15068                 };
15069                 
15070                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15071                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15072                 }
15073             }
15074             row.removeClass('selected');
15075             if(!this.multiple && this.valueField &&
15076                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15077             {
15078                 // radio buttons..
15079                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15080                 row.addClass('selected');
15081             }
15082             
15083             if(this.multiple && this.valueField &&
15084                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15085             {
15086                 
15087                 // checkboxes...
15088                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15089                 this.tickItems.push(d.data);
15090             }
15091             
15092             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15093             
15094         }, this);
15095         
15096         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15097         
15098         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15099
15100         if(this.modalTitle.length){
15101             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15102         }
15103
15104         var listHeight = this.touchViewListGroup.getHeight();
15105         
15106         var _this = this;
15107         
15108         if(firstChecked && listHeight > bodyHeight){
15109             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15110         }
15111         
15112     },
15113     
15114     onTouchViewLoadException : function()
15115     {
15116         this.hideTouchView();
15117     },
15118     
15119     onTouchViewEmptyResults : function()
15120     {
15121         this.clearTouchView();
15122         
15123         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15124         
15125         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15126         
15127     },
15128     
15129     clearTouchView : function()
15130     {
15131         this.touchViewListGroup.dom.innerHTML = '';
15132     },
15133     
15134     onTouchViewClick : function(e, el, o)
15135     {
15136         e.preventDefault();
15137         
15138         var row = o.row;
15139         var rowIndex = o.rowIndex;
15140         
15141         var r = this.store.getAt(rowIndex);
15142         
15143         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15144             
15145             if(!this.multiple){
15146                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15147                     c.dom.removeAttribute('checked');
15148                 }, this);
15149
15150                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15151
15152                 this.setFromData(r.data);
15153
15154                 var close = this.closeTriggerEl();
15155
15156                 if(close){
15157                     close.show();
15158                 }
15159
15160                 this.hideTouchView();
15161
15162                 this.fireEvent('select', this, r, rowIndex);
15163
15164                 return;
15165             }
15166
15167             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15168                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15169                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15170                 return;
15171             }
15172
15173             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15174             this.addItem(r.data);
15175             this.tickItems.push(r.data);
15176         }
15177     },
15178     
15179     getAutoCreateNativeIOS : function()
15180     {
15181         var cfg = {
15182             cls: 'form-group' //input-group,
15183         };
15184         
15185         var combobox =  {
15186             tag: 'select',
15187             cls : 'roo-ios-select'
15188         };
15189         
15190         if (this.name) {
15191             combobox.name = this.name;
15192         }
15193         
15194         if (this.disabled) {
15195             combobox.disabled = true;
15196         }
15197         
15198         var settings = this;
15199         
15200         ['xs','sm','md','lg'].map(function(size){
15201             if (settings[size]) {
15202                 cfg.cls += ' col-' + size + '-' + settings[size];
15203             }
15204         });
15205         
15206         cfg.cn = combobox;
15207         
15208         return cfg;
15209         
15210     },
15211     
15212     initIOSView : function()
15213     {
15214         this.store.on('load', this.onIOSViewLoad, this);
15215         
15216         return;
15217     },
15218     
15219     onIOSViewLoad : function()
15220     {
15221         if(this.store.getCount() < 1){
15222             return;
15223         }
15224         
15225         this.clearIOSView();
15226         
15227         if(this.allowBlank) {
15228             
15229             var default_text = '-- SELECT --';
15230             
15231             var opt = this.inputEl().createChild({
15232                 tag: 'option',
15233                 value : 0,
15234                 html : default_text
15235             });
15236             
15237             var o = {};
15238             o[this.valueField] = 0;
15239             o[this.displayField] = default_text;
15240             
15241             this.ios_options.push({
15242                 data : o,
15243                 el : opt
15244             });
15245             
15246         }
15247         
15248         this.store.data.each(function(d, rowIndex){
15249             
15250             var html = '';
15251             
15252             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15253                 html = d.data[this.displayField];
15254             }
15255             
15256             var value = '';
15257             
15258             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15259                 value = d.data[this.valueField];
15260             }
15261             
15262             var option = {
15263                 tag: 'option',
15264                 value : value,
15265                 html : html
15266             };
15267             
15268             if(this.value == d.data[this.valueField]){
15269                 option['selected'] = true;
15270             }
15271             
15272             var opt = this.inputEl().createChild(option);
15273             
15274             this.ios_options.push({
15275                 data : d.data,
15276                 el : opt
15277             });
15278             
15279         }, this);
15280         
15281         this.inputEl().on('change', function(){
15282            this.fireEvent('select', this);
15283         }, this);
15284         
15285     },
15286     
15287     clearIOSView: function()
15288     {
15289         this.inputEl().dom.innerHTML = '';
15290         
15291         this.ios_options = [];
15292     },
15293     
15294     setIOSValue: function(v)
15295     {
15296         this.value = v;
15297         
15298         if(!this.ios_options){
15299             return;
15300         }
15301         
15302         Roo.each(this.ios_options, function(opts){
15303            
15304            opts.el.dom.removeAttribute('selected');
15305            
15306            if(opts.data[this.valueField] != v){
15307                return;
15308            }
15309            
15310            opts.el.dom.setAttribute('selected', true);
15311            
15312         }, this);
15313     }
15314
15315     /** 
15316     * @cfg {Boolean} grow 
15317     * @hide 
15318     */
15319     /** 
15320     * @cfg {Number} growMin 
15321     * @hide 
15322     */
15323     /** 
15324     * @cfg {Number} growMax 
15325     * @hide 
15326     */
15327     /**
15328      * @hide
15329      * @method autoSize
15330      */
15331 });
15332
15333 Roo.apply(Roo.bootstrap.ComboBox,  {
15334     
15335     header : {
15336         tag: 'div',
15337         cls: 'modal-header',
15338         cn: [
15339             {
15340                 tag: 'h4',
15341                 cls: 'modal-title'
15342             }
15343         ]
15344     },
15345     
15346     body : {
15347         tag: 'div',
15348         cls: 'modal-body',
15349         cn: [
15350             {
15351                 tag: 'ul',
15352                 cls: 'list-group'
15353             }
15354         ]
15355     },
15356     
15357     listItemRadio : {
15358         tag: 'li',
15359         cls: 'list-group-item',
15360         cn: [
15361             {
15362                 tag: 'span',
15363                 cls: 'roo-combobox-list-group-item-value'
15364             },
15365             {
15366                 tag: 'div',
15367                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15368                 cn: [
15369                     {
15370                         tag: 'input',
15371                         type: 'radio'
15372                     },
15373                     {
15374                         tag: 'label'
15375                     }
15376                 ]
15377             }
15378         ]
15379     },
15380     
15381     listItemCheckbox : {
15382         tag: 'li',
15383         cls: 'list-group-item',
15384         cn: [
15385             {
15386                 tag: 'span',
15387                 cls: 'roo-combobox-list-group-item-value'
15388             },
15389             {
15390                 tag: 'div',
15391                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15392                 cn: [
15393                     {
15394                         tag: 'input',
15395                         type: 'checkbox'
15396                     },
15397                     {
15398                         tag: 'label'
15399                     }
15400                 ]
15401             }
15402         ]
15403     },
15404     
15405     emptyResult : {
15406         tag: 'div',
15407         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15408     },
15409     
15410     footer : {
15411         tag: 'div',
15412         cls: 'modal-footer',
15413         cn: [
15414             {
15415                 tag: 'div',
15416                 cls: 'row',
15417                 cn: [
15418                     {
15419                         tag: 'div',
15420                         cls: 'col-xs-6 text-left',
15421                         cn: {
15422                             tag: 'button',
15423                             cls: 'btn btn-danger roo-touch-view-cancel',
15424                             html: 'Cancel'
15425                         }
15426                     },
15427                     {
15428                         tag: 'div',
15429                         cls: 'col-xs-6 text-right',
15430                         cn: {
15431                             tag: 'button',
15432                             cls: 'btn btn-success roo-touch-view-ok',
15433                             html: 'OK'
15434                         }
15435                     }
15436                 ]
15437             }
15438         ]
15439         
15440     }
15441 });
15442
15443 Roo.apply(Roo.bootstrap.ComboBox,  {
15444     
15445     touchViewTemplate : {
15446         tag: 'div',
15447         cls: 'modal fade roo-combobox-touch-view',
15448         cn: [
15449             {
15450                 tag: 'div',
15451                 cls: 'modal-dialog',
15452                 style : 'position:fixed', // we have to fix position....
15453                 cn: [
15454                     {
15455                         tag: 'div',
15456                         cls: 'modal-content',
15457                         cn: [
15458                             Roo.bootstrap.ComboBox.header,
15459                             Roo.bootstrap.ComboBox.body,
15460                             Roo.bootstrap.ComboBox.footer
15461                         ]
15462                     }
15463                 ]
15464             }
15465         ]
15466     }
15467 });/*
15468  * Based on:
15469  * Ext JS Library 1.1.1
15470  * Copyright(c) 2006-2007, Ext JS, LLC.
15471  *
15472  * Originally Released Under LGPL - original licence link has changed is not relivant.
15473  *
15474  * Fork - LGPL
15475  * <script type="text/javascript">
15476  */
15477
15478 /**
15479  * @class Roo.View
15480  * @extends Roo.util.Observable
15481  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15482  * This class also supports single and multi selection modes. <br>
15483  * Create a data model bound view:
15484  <pre><code>
15485  var store = new Roo.data.Store(...);
15486
15487  var view = new Roo.View({
15488     el : "my-element",
15489     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15490  
15491     singleSelect: true,
15492     selectedClass: "ydataview-selected",
15493     store: store
15494  });
15495
15496  // listen for node click?
15497  view.on("click", function(vw, index, node, e){
15498  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15499  });
15500
15501  // load XML data
15502  dataModel.load("foobar.xml");
15503  </code></pre>
15504  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15505  * <br><br>
15506  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15507  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15508  * 
15509  * Note: old style constructor is still suported (container, template, config)
15510  * 
15511  * @constructor
15512  * Create a new View
15513  * @param {Object} config The config object
15514  * 
15515  */
15516 Roo.View = function(config, depreciated_tpl, depreciated_config){
15517     
15518     this.parent = false;
15519     
15520     if (typeof(depreciated_tpl) == 'undefined') {
15521         // new way.. - universal constructor.
15522         Roo.apply(this, config);
15523         this.el  = Roo.get(this.el);
15524     } else {
15525         // old format..
15526         this.el  = Roo.get(config);
15527         this.tpl = depreciated_tpl;
15528         Roo.apply(this, depreciated_config);
15529     }
15530     this.wrapEl  = this.el.wrap().wrap();
15531     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15532     
15533     
15534     if(typeof(this.tpl) == "string"){
15535         this.tpl = new Roo.Template(this.tpl);
15536     } else {
15537         // support xtype ctors..
15538         this.tpl = new Roo.factory(this.tpl, Roo);
15539     }
15540     
15541     
15542     this.tpl.compile();
15543     
15544     /** @private */
15545     this.addEvents({
15546         /**
15547          * @event beforeclick
15548          * Fires before a click is processed. Returns false to cancel the default action.
15549          * @param {Roo.View} this
15550          * @param {Number} index The index of the target node
15551          * @param {HTMLElement} node The target node
15552          * @param {Roo.EventObject} e The raw event object
15553          */
15554             "beforeclick" : true,
15555         /**
15556          * @event click
15557          * Fires when a template node is clicked.
15558          * @param {Roo.View} this
15559          * @param {Number} index The index of the target node
15560          * @param {HTMLElement} node The target node
15561          * @param {Roo.EventObject} e The raw event object
15562          */
15563             "click" : true,
15564         /**
15565          * @event dblclick
15566          * Fires when a template node is double clicked.
15567          * @param {Roo.View} this
15568          * @param {Number} index The index of the target node
15569          * @param {HTMLElement} node The target node
15570          * @param {Roo.EventObject} e The raw event object
15571          */
15572             "dblclick" : true,
15573         /**
15574          * @event contextmenu
15575          * Fires when a template node is right clicked.
15576          * @param {Roo.View} this
15577          * @param {Number} index The index of the target node
15578          * @param {HTMLElement} node The target node
15579          * @param {Roo.EventObject} e The raw event object
15580          */
15581             "contextmenu" : true,
15582         /**
15583          * @event selectionchange
15584          * Fires when the selected nodes change.
15585          * @param {Roo.View} this
15586          * @param {Array} selections Array of the selected nodes
15587          */
15588             "selectionchange" : true,
15589     
15590         /**
15591          * @event beforeselect
15592          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15593          * @param {Roo.View} this
15594          * @param {HTMLElement} node The node to be selected
15595          * @param {Array} selections Array of currently selected nodes
15596          */
15597             "beforeselect" : true,
15598         /**
15599          * @event preparedata
15600          * Fires on every row to render, to allow you to change the data.
15601          * @param {Roo.View} this
15602          * @param {Object} data to be rendered (change this)
15603          */
15604           "preparedata" : true
15605           
15606           
15607         });
15608
15609
15610
15611     this.el.on({
15612         "click": this.onClick,
15613         "dblclick": this.onDblClick,
15614         "contextmenu": this.onContextMenu,
15615         scope:this
15616     });
15617
15618     this.selections = [];
15619     this.nodes = [];
15620     this.cmp = new Roo.CompositeElementLite([]);
15621     if(this.store){
15622         this.store = Roo.factory(this.store, Roo.data);
15623         this.setStore(this.store, true);
15624     }
15625     
15626     if ( this.footer && this.footer.xtype) {
15627            
15628          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15629         
15630         this.footer.dataSource = this.store;
15631         this.footer.container = fctr;
15632         this.footer = Roo.factory(this.footer, Roo);
15633         fctr.insertFirst(this.el);
15634         
15635         // this is a bit insane - as the paging toolbar seems to detach the el..
15636 //        dom.parentNode.parentNode.parentNode
15637          // they get detached?
15638     }
15639     
15640     
15641     Roo.View.superclass.constructor.call(this);
15642     
15643     
15644 };
15645
15646 Roo.extend(Roo.View, Roo.util.Observable, {
15647     
15648      /**
15649      * @cfg {Roo.data.Store} store Data store to load data from.
15650      */
15651     store : false,
15652     
15653     /**
15654      * @cfg {String|Roo.Element} el The container element.
15655      */
15656     el : '',
15657     
15658     /**
15659      * @cfg {String|Roo.Template} tpl The template used by this View 
15660      */
15661     tpl : false,
15662     /**
15663      * @cfg {String} dataName the named area of the template to use as the data area
15664      *                          Works with domtemplates roo-name="name"
15665      */
15666     dataName: false,
15667     /**
15668      * @cfg {String} selectedClass The css class to add to selected nodes
15669      */
15670     selectedClass : "x-view-selected",
15671      /**
15672      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15673      */
15674     emptyText : "",
15675     
15676     /**
15677      * @cfg {String} text to display on mask (default Loading)
15678      */
15679     mask : false,
15680     /**
15681      * @cfg {Boolean} multiSelect Allow multiple selection
15682      */
15683     multiSelect : false,
15684     /**
15685      * @cfg {Boolean} singleSelect Allow single selection
15686      */
15687     singleSelect:  false,
15688     
15689     /**
15690      * @cfg {Boolean} toggleSelect - selecting 
15691      */
15692     toggleSelect : false,
15693     
15694     /**
15695      * @cfg {Boolean} tickable - selecting 
15696      */
15697     tickable : false,
15698     
15699     /**
15700      * Returns the element this view is bound to.
15701      * @return {Roo.Element}
15702      */
15703     getEl : function(){
15704         return this.wrapEl;
15705     },
15706     
15707     
15708
15709     /**
15710      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15711      */
15712     refresh : function(){
15713         //Roo.log('refresh');
15714         var t = this.tpl;
15715         
15716         // if we are using something like 'domtemplate', then
15717         // the what gets used is:
15718         // t.applySubtemplate(NAME, data, wrapping data..)
15719         // the outer template then get' applied with
15720         //     the store 'extra data'
15721         // and the body get's added to the
15722         //      roo-name="data" node?
15723         //      <span class='roo-tpl-{name}'></span> ?????
15724         
15725         
15726         
15727         this.clearSelections();
15728         this.el.update("");
15729         var html = [];
15730         var records = this.store.getRange();
15731         if(records.length < 1) {
15732             
15733             // is this valid??  = should it render a template??
15734             
15735             this.el.update(this.emptyText);
15736             return;
15737         }
15738         var el = this.el;
15739         if (this.dataName) {
15740             this.el.update(t.apply(this.store.meta)); //????
15741             el = this.el.child('.roo-tpl-' + this.dataName);
15742         }
15743         
15744         for(var i = 0, len = records.length; i < len; i++){
15745             var data = this.prepareData(records[i].data, i, records[i]);
15746             this.fireEvent("preparedata", this, data, i, records[i]);
15747             
15748             var d = Roo.apply({}, data);
15749             
15750             if(this.tickable){
15751                 Roo.apply(d, {'roo-id' : Roo.id()});
15752                 
15753                 var _this = this;
15754             
15755                 Roo.each(this.parent.item, function(item){
15756                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15757                         return;
15758                     }
15759                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15760                 });
15761             }
15762             
15763             html[html.length] = Roo.util.Format.trim(
15764                 this.dataName ?
15765                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15766                     t.apply(d)
15767             );
15768         }
15769         
15770         
15771         
15772         el.update(html.join(""));
15773         this.nodes = el.dom.childNodes;
15774         this.updateIndexes(0);
15775     },
15776     
15777
15778     /**
15779      * Function to override to reformat the data that is sent to
15780      * the template for each node.
15781      * DEPRICATED - use the preparedata event handler.
15782      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15783      * a JSON object for an UpdateManager bound view).
15784      */
15785     prepareData : function(data, index, record)
15786     {
15787         this.fireEvent("preparedata", this, data, index, record);
15788         return data;
15789     },
15790
15791     onUpdate : function(ds, record){
15792         // Roo.log('on update');   
15793         this.clearSelections();
15794         var index = this.store.indexOf(record);
15795         var n = this.nodes[index];
15796         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15797         n.parentNode.removeChild(n);
15798         this.updateIndexes(index, index);
15799     },
15800
15801     
15802     
15803 // --------- FIXME     
15804     onAdd : function(ds, records, index)
15805     {
15806         //Roo.log(['on Add', ds, records, index] );        
15807         this.clearSelections();
15808         if(this.nodes.length == 0){
15809             this.refresh();
15810             return;
15811         }
15812         var n = this.nodes[index];
15813         for(var i = 0, len = records.length; i < len; i++){
15814             var d = this.prepareData(records[i].data, i, records[i]);
15815             if(n){
15816                 this.tpl.insertBefore(n, d);
15817             }else{
15818                 
15819                 this.tpl.append(this.el, d);
15820             }
15821         }
15822         this.updateIndexes(index);
15823     },
15824
15825     onRemove : function(ds, record, index){
15826        // Roo.log('onRemove');
15827         this.clearSelections();
15828         var el = this.dataName  ?
15829             this.el.child('.roo-tpl-' + this.dataName) :
15830             this.el; 
15831         
15832         el.dom.removeChild(this.nodes[index]);
15833         this.updateIndexes(index);
15834     },
15835
15836     /**
15837      * Refresh an individual node.
15838      * @param {Number} index
15839      */
15840     refreshNode : function(index){
15841         this.onUpdate(this.store, this.store.getAt(index));
15842     },
15843
15844     updateIndexes : function(startIndex, endIndex){
15845         var ns = this.nodes;
15846         startIndex = startIndex || 0;
15847         endIndex = endIndex || ns.length - 1;
15848         for(var i = startIndex; i <= endIndex; i++){
15849             ns[i].nodeIndex = i;
15850         }
15851     },
15852
15853     /**
15854      * Changes the data store this view uses and refresh the view.
15855      * @param {Store} store
15856      */
15857     setStore : function(store, initial){
15858         if(!initial && this.store){
15859             this.store.un("datachanged", this.refresh);
15860             this.store.un("add", this.onAdd);
15861             this.store.un("remove", this.onRemove);
15862             this.store.un("update", this.onUpdate);
15863             this.store.un("clear", this.refresh);
15864             this.store.un("beforeload", this.onBeforeLoad);
15865             this.store.un("load", this.onLoad);
15866             this.store.un("loadexception", this.onLoad);
15867         }
15868         if(store){
15869           
15870             store.on("datachanged", this.refresh, this);
15871             store.on("add", this.onAdd, this);
15872             store.on("remove", this.onRemove, this);
15873             store.on("update", this.onUpdate, this);
15874             store.on("clear", this.refresh, this);
15875             store.on("beforeload", this.onBeforeLoad, this);
15876             store.on("load", this.onLoad, this);
15877             store.on("loadexception", this.onLoad, this);
15878         }
15879         
15880         if(store){
15881             this.refresh();
15882         }
15883     },
15884     /**
15885      * onbeforeLoad - masks the loading area.
15886      *
15887      */
15888     onBeforeLoad : function(store,opts)
15889     {
15890          //Roo.log('onBeforeLoad');   
15891         if (!opts.add) {
15892             this.el.update("");
15893         }
15894         this.el.mask(this.mask ? this.mask : "Loading" ); 
15895     },
15896     onLoad : function ()
15897     {
15898         this.el.unmask();
15899     },
15900     
15901
15902     /**
15903      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15904      * @param {HTMLElement} node
15905      * @return {HTMLElement} The template node
15906      */
15907     findItemFromChild : function(node){
15908         var el = this.dataName  ?
15909             this.el.child('.roo-tpl-' + this.dataName,true) :
15910             this.el.dom; 
15911         
15912         if(!node || node.parentNode == el){
15913                     return node;
15914             }
15915             var p = node.parentNode;
15916             while(p && p != el){
15917             if(p.parentNode == el){
15918                 return p;
15919             }
15920             p = p.parentNode;
15921         }
15922             return null;
15923     },
15924
15925     /** @ignore */
15926     onClick : function(e){
15927         var item = this.findItemFromChild(e.getTarget());
15928         if(item){
15929             var index = this.indexOf(item);
15930             if(this.onItemClick(item, index, e) !== false){
15931                 this.fireEvent("click", this, index, item, e);
15932             }
15933         }else{
15934             this.clearSelections();
15935         }
15936     },
15937
15938     /** @ignore */
15939     onContextMenu : function(e){
15940         var item = this.findItemFromChild(e.getTarget());
15941         if(item){
15942             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15943         }
15944     },
15945
15946     /** @ignore */
15947     onDblClick : function(e){
15948         var item = this.findItemFromChild(e.getTarget());
15949         if(item){
15950             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15951         }
15952     },
15953
15954     onItemClick : function(item, index, e)
15955     {
15956         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15957             return false;
15958         }
15959         if (this.toggleSelect) {
15960             var m = this.isSelected(item) ? 'unselect' : 'select';
15961             //Roo.log(m);
15962             var _t = this;
15963             _t[m](item, true, false);
15964             return true;
15965         }
15966         if(this.multiSelect || this.singleSelect){
15967             if(this.multiSelect && e.shiftKey && this.lastSelection){
15968                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15969             }else{
15970                 this.select(item, this.multiSelect && e.ctrlKey);
15971                 this.lastSelection = item;
15972             }
15973             
15974             if(!this.tickable){
15975                 e.preventDefault();
15976             }
15977             
15978         }
15979         return true;
15980     },
15981
15982     /**
15983      * Get the number of selected nodes.
15984      * @return {Number}
15985      */
15986     getSelectionCount : function(){
15987         return this.selections.length;
15988     },
15989
15990     /**
15991      * Get the currently selected nodes.
15992      * @return {Array} An array of HTMLElements
15993      */
15994     getSelectedNodes : function(){
15995         return this.selections;
15996     },
15997
15998     /**
15999      * Get the indexes of the selected nodes.
16000      * @return {Array}
16001      */
16002     getSelectedIndexes : function(){
16003         var indexes = [], s = this.selections;
16004         for(var i = 0, len = s.length; i < len; i++){
16005             indexes.push(s[i].nodeIndex);
16006         }
16007         return indexes;
16008     },
16009
16010     /**
16011      * Clear all selections
16012      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16013      */
16014     clearSelections : function(suppressEvent){
16015         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16016             this.cmp.elements = this.selections;
16017             this.cmp.removeClass(this.selectedClass);
16018             this.selections = [];
16019             if(!suppressEvent){
16020                 this.fireEvent("selectionchange", this, this.selections);
16021             }
16022         }
16023     },
16024
16025     /**
16026      * Returns true if the passed node is selected
16027      * @param {HTMLElement/Number} node The node or node index
16028      * @return {Boolean}
16029      */
16030     isSelected : function(node){
16031         var s = this.selections;
16032         if(s.length < 1){
16033             return false;
16034         }
16035         node = this.getNode(node);
16036         return s.indexOf(node) !== -1;
16037     },
16038
16039     /**
16040      * Selects nodes.
16041      * @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
16042      * @param {Boolean} keepExisting (optional) true to keep existing selections
16043      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16044      */
16045     select : function(nodeInfo, keepExisting, suppressEvent){
16046         if(nodeInfo instanceof Array){
16047             if(!keepExisting){
16048                 this.clearSelections(true);
16049             }
16050             for(var i = 0, len = nodeInfo.length; i < len; i++){
16051                 this.select(nodeInfo[i], true, true);
16052             }
16053             return;
16054         } 
16055         var node = this.getNode(nodeInfo);
16056         if(!node || this.isSelected(node)){
16057             return; // already selected.
16058         }
16059         if(!keepExisting){
16060             this.clearSelections(true);
16061         }
16062         
16063         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16064             Roo.fly(node).addClass(this.selectedClass);
16065             this.selections.push(node);
16066             if(!suppressEvent){
16067                 this.fireEvent("selectionchange", this, this.selections);
16068             }
16069         }
16070         
16071         
16072     },
16073       /**
16074      * Unselects nodes.
16075      * @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
16076      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16077      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16078      */
16079     unselect : function(nodeInfo, keepExisting, suppressEvent)
16080     {
16081         if(nodeInfo instanceof Array){
16082             Roo.each(this.selections, function(s) {
16083                 this.unselect(s, nodeInfo);
16084             }, this);
16085             return;
16086         }
16087         var node = this.getNode(nodeInfo);
16088         if(!node || !this.isSelected(node)){
16089             //Roo.log("not selected");
16090             return; // not selected.
16091         }
16092         // fireevent???
16093         var ns = [];
16094         Roo.each(this.selections, function(s) {
16095             if (s == node ) {
16096                 Roo.fly(node).removeClass(this.selectedClass);
16097
16098                 return;
16099             }
16100             ns.push(s);
16101         },this);
16102         
16103         this.selections= ns;
16104         this.fireEvent("selectionchange", this, this.selections);
16105     },
16106
16107     /**
16108      * Gets a template node.
16109      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16110      * @return {HTMLElement} The node or null if it wasn't found
16111      */
16112     getNode : function(nodeInfo){
16113         if(typeof nodeInfo == "string"){
16114             return document.getElementById(nodeInfo);
16115         }else if(typeof nodeInfo == "number"){
16116             return this.nodes[nodeInfo];
16117         }
16118         return nodeInfo;
16119     },
16120
16121     /**
16122      * Gets a range template nodes.
16123      * @param {Number} startIndex
16124      * @param {Number} endIndex
16125      * @return {Array} An array of nodes
16126      */
16127     getNodes : function(start, end){
16128         var ns = this.nodes;
16129         start = start || 0;
16130         end = typeof end == "undefined" ? ns.length - 1 : end;
16131         var nodes = [];
16132         if(start <= end){
16133             for(var i = start; i <= end; i++){
16134                 nodes.push(ns[i]);
16135             }
16136         } else{
16137             for(var i = start; i >= end; i--){
16138                 nodes.push(ns[i]);
16139             }
16140         }
16141         return nodes;
16142     },
16143
16144     /**
16145      * Finds the index of the passed node
16146      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16147      * @return {Number} The index of the node or -1
16148      */
16149     indexOf : function(node){
16150         node = this.getNode(node);
16151         if(typeof node.nodeIndex == "number"){
16152             return node.nodeIndex;
16153         }
16154         var ns = this.nodes;
16155         for(var i = 0, len = ns.length; i < len; i++){
16156             if(ns[i] == node){
16157                 return i;
16158             }
16159         }
16160         return -1;
16161     }
16162 });
16163 /*
16164  * - LGPL
16165  *
16166  * based on jquery fullcalendar
16167  * 
16168  */
16169
16170 Roo.bootstrap = Roo.bootstrap || {};
16171 /**
16172  * @class Roo.bootstrap.Calendar
16173  * @extends Roo.bootstrap.Component
16174  * Bootstrap Calendar class
16175  * @cfg {Boolean} loadMask (true|false) default false
16176  * @cfg {Object} header generate the user specific header of the calendar, default false
16177
16178  * @constructor
16179  * Create a new Container
16180  * @param {Object} config The config object
16181  */
16182
16183
16184
16185 Roo.bootstrap.Calendar = function(config){
16186     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16187      this.addEvents({
16188         /**
16189              * @event select
16190              * Fires when a date is selected
16191              * @param {DatePicker} this
16192              * @param {Date} date The selected date
16193              */
16194         'select': true,
16195         /**
16196              * @event monthchange
16197              * Fires when the displayed month changes 
16198              * @param {DatePicker} this
16199              * @param {Date} date The selected month
16200              */
16201         'monthchange': true,
16202         /**
16203              * @event evententer
16204              * Fires when mouse over an event
16205              * @param {Calendar} this
16206              * @param {event} Event
16207              */
16208         'evententer': true,
16209         /**
16210              * @event eventleave
16211              * Fires when the mouse leaves an
16212              * @param {Calendar} this
16213              * @param {event}
16214              */
16215         'eventleave': true,
16216         /**
16217              * @event eventclick
16218              * Fires when the mouse click an
16219              * @param {Calendar} this
16220              * @param {event}
16221              */
16222         'eventclick': true
16223         
16224     });
16225
16226 };
16227
16228 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16229     
16230      /**
16231      * @cfg {Number} startDay
16232      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16233      */
16234     startDay : 0,
16235     
16236     loadMask : false,
16237     
16238     header : false,
16239       
16240     getAutoCreate : function(){
16241         
16242         
16243         var fc_button = function(name, corner, style, content ) {
16244             return Roo.apply({},{
16245                 tag : 'span',
16246                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16247                          (corner.length ?
16248                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16249                             ''
16250                         ),
16251                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16252                 unselectable: 'on'
16253             });
16254         };
16255         
16256         var header = {};
16257         
16258         if(!this.header){
16259             header = {
16260                 tag : 'table',
16261                 cls : 'fc-header',
16262                 style : 'width:100%',
16263                 cn : [
16264                     {
16265                         tag: 'tr',
16266                         cn : [
16267                             {
16268                                 tag : 'td',
16269                                 cls : 'fc-header-left',
16270                                 cn : [
16271                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16272                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16273                                     { tag: 'span', cls: 'fc-header-space' },
16274                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16275
16276
16277                                 ]
16278                             },
16279
16280                             {
16281                                 tag : 'td',
16282                                 cls : 'fc-header-center',
16283                                 cn : [
16284                                     {
16285                                         tag: 'span',
16286                                         cls: 'fc-header-title',
16287                                         cn : {
16288                                             tag: 'H2',
16289                                             html : 'month / year'
16290                                         }
16291                                     }
16292
16293                                 ]
16294                             },
16295                             {
16296                                 tag : 'td',
16297                                 cls : 'fc-header-right',
16298                                 cn : [
16299                               /*      fc_button('month', 'left', '', 'month' ),
16300                                     fc_button('week', '', '', 'week' ),
16301                                     fc_button('day', 'right', '', 'day' )
16302                                 */    
16303
16304                                 ]
16305                             }
16306
16307                         ]
16308                     }
16309                 ]
16310             };
16311         }
16312         
16313         header = this.header;
16314         
16315        
16316         var cal_heads = function() {
16317             var ret = [];
16318             // fixme - handle this.
16319             
16320             for (var i =0; i < Date.dayNames.length; i++) {
16321                 var d = Date.dayNames[i];
16322                 ret.push({
16323                     tag: 'th',
16324                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16325                     html : d.substring(0,3)
16326                 });
16327                 
16328             }
16329             ret[0].cls += ' fc-first';
16330             ret[6].cls += ' fc-last';
16331             return ret;
16332         };
16333         var cal_cell = function(n) {
16334             return  {
16335                 tag: 'td',
16336                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16337                 cn : [
16338                     {
16339                         cn : [
16340                             {
16341                                 cls: 'fc-day-number',
16342                                 html: 'D'
16343                             },
16344                             {
16345                                 cls: 'fc-day-content',
16346                              
16347                                 cn : [
16348                                      {
16349                                         style: 'position: relative;' // height: 17px;
16350                                     }
16351                                 ]
16352                             }
16353                             
16354                             
16355                         ]
16356                     }
16357                 ]
16358                 
16359             }
16360         };
16361         var cal_rows = function() {
16362             
16363             var ret = [];
16364             for (var r = 0; r < 6; r++) {
16365                 var row= {
16366                     tag : 'tr',
16367                     cls : 'fc-week',
16368                     cn : []
16369                 };
16370                 
16371                 for (var i =0; i < Date.dayNames.length; i++) {
16372                     var d = Date.dayNames[i];
16373                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16374
16375                 }
16376                 row.cn[0].cls+=' fc-first';
16377                 row.cn[0].cn[0].style = 'min-height:90px';
16378                 row.cn[6].cls+=' fc-last';
16379                 ret.push(row);
16380                 
16381             }
16382             ret[0].cls += ' fc-first';
16383             ret[4].cls += ' fc-prev-last';
16384             ret[5].cls += ' fc-last';
16385             return ret;
16386             
16387         };
16388         
16389         var cal_table = {
16390             tag: 'table',
16391             cls: 'fc-border-separate',
16392             style : 'width:100%',
16393             cellspacing  : 0,
16394             cn : [
16395                 { 
16396                     tag: 'thead',
16397                     cn : [
16398                         { 
16399                             tag: 'tr',
16400                             cls : 'fc-first fc-last',
16401                             cn : cal_heads()
16402                         }
16403                     ]
16404                 },
16405                 { 
16406                     tag: 'tbody',
16407                     cn : cal_rows()
16408                 }
16409                   
16410             ]
16411         };
16412          
16413          var cfg = {
16414             cls : 'fc fc-ltr',
16415             cn : [
16416                 header,
16417                 {
16418                     cls : 'fc-content',
16419                     style : "position: relative;",
16420                     cn : [
16421                         {
16422                             cls : 'fc-view fc-view-month fc-grid',
16423                             style : 'position: relative',
16424                             unselectable : 'on',
16425                             cn : [
16426                                 {
16427                                     cls : 'fc-event-container',
16428                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16429                                 },
16430                                 cal_table
16431                             ]
16432                         }
16433                     ]
16434     
16435                 }
16436            ] 
16437             
16438         };
16439         
16440          
16441         
16442         return cfg;
16443     },
16444     
16445     
16446     initEvents : function()
16447     {
16448         if(!this.store){
16449             throw "can not find store for calendar";
16450         }
16451         
16452         var mark = {
16453             tag: "div",
16454             cls:"x-dlg-mask",
16455             style: "text-align:center",
16456             cn: [
16457                 {
16458                     tag: "div",
16459                     style: "background-color:white;width:50%;margin:250 auto",
16460                     cn: [
16461                         {
16462                             tag: "img",
16463                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16464                         },
16465                         {
16466                             tag: "span",
16467                             html: "Loading"
16468                         }
16469                         
16470                     ]
16471                 }
16472             ]
16473         };
16474         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16475         
16476         var size = this.el.select('.fc-content', true).first().getSize();
16477         this.maskEl.setSize(size.width, size.height);
16478         this.maskEl.enableDisplayMode("block");
16479         if(!this.loadMask){
16480             this.maskEl.hide();
16481         }
16482         
16483         this.store = Roo.factory(this.store, Roo.data);
16484         this.store.on('load', this.onLoad, this);
16485         this.store.on('beforeload', this.onBeforeLoad, this);
16486         
16487         this.resize();
16488         
16489         this.cells = this.el.select('.fc-day',true);
16490         //Roo.log(this.cells);
16491         this.textNodes = this.el.query('.fc-day-number');
16492         this.cells.addClassOnOver('fc-state-hover');
16493         
16494         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16495         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16496         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16497         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16498         
16499         this.on('monthchange', this.onMonthChange, this);
16500         
16501         this.update(new Date().clearTime());
16502     },
16503     
16504     resize : function() {
16505         var sz  = this.el.getSize();
16506         
16507         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16508         this.el.select('.fc-day-content div',true).setHeight(34);
16509     },
16510     
16511     
16512     // private
16513     showPrevMonth : function(e){
16514         this.update(this.activeDate.add("mo", -1));
16515     },
16516     showToday : function(e){
16517         this.update(new Date().clearTime());
16518     },
16519     // private
16520     showNextMonth : function(e){
16521         this.update(this.activeDate.add("mo", 1));
16522     },
16523
16524     // private
16525     showPrevYear : function(){
16526         this.update(this.activeDate.add("y", -1));
16527     },
16528
16529     // private
16530     showNextYear : function(){
16531         this.update(this.activeDate.add("y", 1));
16532     },
16533
16534     
16535    // private
16536     update : function(date)
16537     {
16538         var vd = this.activeDate;
16539         this.activeDate = date;
16540 //        if(vd && this.el){
16541 //            var t = date.getTime();
16542 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16543 //                Roo.log('using add remove');
16544 //                
16545 //                this.fireEvent('monthchange', this, date);
16546 //                
16547 //                this.cells.removeClass("fc-state-highlight");
16548 //                this.cells.each(function(c){
16549 //                   if(c.dateValue == t){
16550 //                       c.addClass("fc-state-highlight");
16551 //                       setTimeout(function(){
16552 //                            try{c.dom.firstChild.focus();}catch(e){}
16553 //                       }, 50);
16554 //                       return false;
16555 //                   }
16556 //                   return true;
16557 //                });
16558 //                return;
16559 //            }
16560 //        }
16561         
16562         var days = date.getDaysInMonth();
16563         
16564         var firstOfMonth = date.getFirstDateOfMonth();
16565         var startingPos = firstOfMonth.getDay()-this.startDay;
16566         
16567         if(startingPos < this.startDay){
16568             startingPos += 7;
16569         }
16570         
16571         var pm = date.add(Date.MONTH, -1);
16572         var prevStart = pm.getDaysInMonth()-startingPos;
16573 //        
16574         this.cells = this.el.select('.fc-day',true);
16575         this.textNodes = this.el.query('.fc-day-number');
16576         this.cells.addClassOnOver('fc-state-hover');
16577         
16578         var cells = this.cells.elements;
16579         var textEls = this.textNodes;
16580         
16581         Roo.each(cells, function(cell){
16582             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16583         });
16584         
16585         days += startingPos;
16586
16587         // convert everything to numbers so it's fast
16588         var day = 86400000;
16589         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16590         //Roo.log(d);
16591         //Roo.log(pm);
16592         //Roo.log(prevStart);
16593         
16594         var today = new Date().clearTime().getTime();
16595         var sel = date.clearTime().getTime();
16596         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16597         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16598         var ddMatch = this.disabledDatesRE;
16599         var ddText = this.disabledDatesText;
16600         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16601         var ddaysText = this.disabledDaysText;
16602         var format = this.format;
16603         
16604         var setCellClass = function(cal, cell){
16605             cell.row = 0;
16606             cell.events = [];
16607             cell.more = [];
16608             //Roo.log('set Cell Class');
16609             cell.title = "";
16610             var t = d.getTime();
16611             
16612             //Roo.log(d);
16613             
16614             cell.dateValue = t;
16615             if(t == today){
16616                 cell.className += " fc-today";
16617                 cell.className += " fc-state-highlight";
16618                 cell.title = cal.todayText;
16619             }
16620             if(t == sel){
16621                 // disable highlight in other month..
16622                 //cell.className += " fc-state-highlight";
16623                 
16624             }
16625             // disabling
16626             if(t < min) {
16627                 cell.className = " fc-state-disabled";
16628                 cell.title = cal.minText;
16629                 return;
16630             }
16631             if(t > max) {
16632                 cell.className = " fc-state-disabled";
16633                 cell.title = cal.maxText;
16634                 return;
16635             }
16636             if(ddays){
16637                 if(ddays.indexOf(d.getDay()) != -1){
16638                     cell.title = ddaysText;
16639                     cell.className = " fc-state-disabled";
16640                 }
16641             }
16642             if(ddMatch && format){
16643                 var fvalue = d.dateFormat(format);
16644                 if(ddMatch.test(fvalue)){
16645                     cell.title = ddText.replace("%0", fvalue);
16646                     cell.className = " fc-state-disabled";
16647                 }
16648             }
16649             
16650             if (!cell.initialClassName) {
16651                 cell.initialClassName = cell.dom.className;
16652             }
16653             
16654             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16655         };
16656
16657         var i = 0;
16658         
16659         for(; i < startingPos; i++) {
16660             textEls[i].innerHTML = (++prevStart);
16661             d.setDate(d.getDate()+1);
16662             
16663             cells[i].className = "fc-past fc-other-month";
16664             setCellClass(this, cells[i]);
16665         }
16666         
16667         var intDay = 0;
16668         
16669         for(; i < days; i++){
16670             intDay = i - startingPos + 1;
16671             textEls[i].innerHTML = (intDay);
16672             d.setDate(d.getDate()+1);
16673             
16674             cells[i].className = ''; // "x-date-active";
16675             setCellClass(this, cells[i]);
16676         }
16677         var extraDays = 0;
16678         
16679         for(; i < 42; i++) {
16680             textEls[i].innerHTML = (++extraDays);
16681             d.setDate(d.getDate()+1);
16682             
16683             cells[i].className = "fc-future fc-other-month";
16684             setCellClass(this, cells[i]);
16685         }
16686         
16687         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16688         
16689         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16690         
16691         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16692         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16693         
16694         if(totalRows != 6){
16695             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16696             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16697         }
16698         
16699         this.fireEvent('monthchange', this, date);
16700         
16701         
16702         /*
16703         if(!this.internalRender){
16704             var main = this.el.dom.firstChild;
16705             var w = main.offsetWidth;
16706             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16707             Roo.fly(main).setWidth(w);
16708             this.internalRender = true;
16709             // opera does not respect the auto grow header center column
16710             // then, after it gets a width opera refuses to recalculate
16711             // without a second pass
16712             if(Roo.isOpera && !this.secondPass){
16713                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16714                 this.secondPass = true;
16715                 this.update.defer(10, this, [date]);
16716             }
16717         }
16718         */
16719         
16720     },
16721     
16722     findCell : function(dt) {
16723         dt = dt.clearTime().getTime();
16724         var ret = false;
16725         this.cells.each(function(c){
16726             //Roo.log("check " +c.dateValue + '?=' + dt);
16727             if(c.dateValue == dt){
16728                 ret = c;
16729                 return false;
16730             }
16731             return true;
16732         });
16733         
16734         return ret;
16735     },
16736     
16737     findCells : function(ev) {
16738         var s = ev.start.clone().clearTime().getTime();
16739        // Roo.log(s);
16740         var e= ev.end.clone().clearTime().getTime();
16741        // Roo.log(e);
16742         var ret = [];
16743         this.cells.each(function(c){
16744              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16745             
16746             if(c.dateValue > e){
16747                 return ;
16748             }
16749             if(c.dateValue < s){
16750                 return ;
16751             }
16752             ret.push(c);
16753         });
16754         
16755         return ret;    
16756     },
16757     
16758 //    findBestRow: function(cells)
16759 //    {
16760 //        var ret = 0;
16761 //        
16762 //        for (var i =0 ; i < cells.length;i++) {
16763 //            ret  = Math.max(cells[i].rows || 0,ret);
16764 //        }
16765 //        return ret;
16766 //        
16767 //    },
16768     
16769     
16770     addItem : function(ev)
16771     {
16772         // look for vertical location slot in
16773         var cells = this.findCells(ev);
16774         
16775 //        ev.row = this.findBestRow(cells);
16776         
16777         // work out the location.
16778         
16779         var crow = false;
16780         var rows = [];
16781         for(var i =0; i < cells.length; i++) {
16782             
16783             cells[i].row = cells[0].row;
16784             
16785             if(i == 0){
16786                 cells[i].row = cells[i].row + 1;
16787             }
16788             
16789             if (!crow) {
16790                 crow = {
16791                     start : cells[i],
16792                     end :  cells[i]
16793                 };
16794                 continue;
16795             }
16796             if (crow.start.getY() == cells[i].getY()) {
16797                 // on same row.
16798                 crow.end = cells[i];
16799                 continue;
16800             }
16801             // different row.
16802             rows.push(crow);
16803             crow = {
16804                 start: cells[i],
16805                 end : cells[i]
16806             };
16807             
16808         }
16809         
16810         rows.push(crow);
16811         ev.els = [];
16812         ev.rows = rows;
16813         ev.cells = cells;
16814         
16815         cells[0].events.push(ev);
16816         
16817         this.calevents.push(ev);
16818     },
16819     
16820     clearEvents: function() {
16821         
16822         if(!this.calevents){
16823             return;
16824         }
16825         
16826         Roo.each(this.cells.elements, function(c){
16827             c.row = 0;
16828             c.events = [];
16829             c.more = [];
16830         });
16831         
16832         Roo.each(this.calevents, function(e) {
16833             Roo.each(e.els, function(el) {
16834                 el.un('mouseenter' ,this.onEventEnter, this);
16835                 el.un('mouseleave' ,this.onEventLeave, this);
16836                 el.remove();
16837             },this);
16838         },this);
16839         
16840         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16841             e.remove();
16842         });
16843         
16844     },
16845     
16846     renderEvents: function()
16847     {   
16848         var _this = this;
16849         
16850         this.cells.each(function(c) {
16851             
16852             if(c.row < 5){
16853                 return;
16854             }
16855             
16856             var ev = c.events;
16857             
16858             var r = 4;
16859             if(c.row != c.events.length){
16860                 r = 4 - (4 - (c.row - c.events.length));
16861             }
16862             
16863             c.events = ev.slice(0, r);
16864             c.more = ev.slice(r);
16865             
16866             if(c.more.length && c.more.length == 1){
16867                 c.events.push(c.more.pop());
16868             }
16869             
16870             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16871             
16872         });
16873             
16874         this.cells.each(function(c) {
16875             
16876             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16877             
16878             
16879             for (var e = 0; e < c.events.length; e++){
16880                 var ev = c.events[e];
16881                 var rows = ev.rows;
16882                 
16883                 for(var i = 0; i < rows.length; i++) {
16884                 
16885                     // how many rows should it span..
16886
16887                     var  cfg = {
16888                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16889                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16890
16891                         unselectable : "on",
16892                         cn : [
16893                             {
16894                                 cls: 'fc-event-inner',
16895                                 cn : [
16896     //                                {
16897     //                                  tag:'span',
16898     //                                  cls: 'fc-event-time',
16899     //                                  html : cells.length > 1 ? '' : ev.time
16900     //                                },
16901                                     {
16902                                       tag:'span',
16903                                       cls: 'fc-event-title',
16904                                       html : String.format('{0}', ev.title)
16905                                     }
16906
16907
16908                                 ]
16909                             },
16910                             {
16911                                 cls: 'ui-resizable-handle ui-resizable-e',
16912                                 html : '&nbsp;&nbsp;&nbsp'
16913                             }
16914
16915                         ]
16916                     };
16917
16918                     if (i == 0) {
16919                         cfg.cls += ' fc-event-start';
16920                     }
16921                     if ((i+1) == rows.length) {
16922                         cfg.cls += ' fc-event-end';
16923                     }
16924
16925                     var ctr = _this.el.select('.fc-event-container',true).first();
16926                     var cg = ctr.createChild(cfg);
16927
16928                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16929                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16930
16931                     var r = (c.more.length) ? 1 : 0;
16932                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16933                     cg.setWidth(ebox.right - sbox.x -2);
16934
16935                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16936                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16937                     cg.on('click', _this.onEventClick, _this, ev);
16938
16939                     ev.els.push(cg);
16940                     
16941                 }
16942                 
16943             }
16944             
16945             
16946             if(c.more.length){
16947                 var  cfg = {
16948                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16949                     style : 'position: absolute',
16950                     unselectable : "on",
16951                     cn : [
16952                         {
16953                             cls: 'fc-event-inner',
16954                             cn : [
16955                                 {
16956                                   tag:'span',
16957                                   cls: 'fc-event-title',
16958                                   html : 'More'
16959                                 }
16960
16961
16962                             ]
16963                         },
16964                         {
16965                             cls: 'ui-resizable-handle ui-resizable-e',
16966                             html : '&nbsp;&nbsp;&nbsp'
16967                         }
16968
16969                     ]
16970                 };
16971
16972                 var ctr = _this.el.select('.fc-event-container',true).first();
16973                 var cg = ctr.createChild(cfg);
16974
16975                 var sbox = c.select('.fc-day-content',true).first().getBox();
16976                 var ebox = c.select('.fc-day-content',true).first().getBox();
16977                 //Roo.log(cg);
16978                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16979                 cg.setWidth(ebox.right - sbox.x -2);
16980
16981                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16982                 
16983             }
16984             
16985         });
16986         
16987         
16988         
16989     },
16990     
16991     onEventEnter: function (e, el,event,d) {
16992         this.fireEvent('evententer', this, el, event);
16993     },
16994     
16995     onEventLeave: function (e, el,event,d) {
16996         this.fireEvent('eventleave', this, el, event);
16997     },
16998     
16999     onEventClick: function (e, el,event,d) {
17000         this.fireEvent('eventclick', this, el, event);
17001     },
17002     
17003     onMonthChange: function () {
17004         this.store.load();
17005     },
17006     
17007     onMoreEventClick: function(e, el, more)
17008     {
17009         var _this = this;
17010         
17011         this.calpopover.placement = 'right';
17012         this.calpopover.setTitle('More');
17013         
17014         this.calpopover.setContent('');
17015         
17016         var ctr = this.calpopover.el.select('.popover-content', true).first();
17017         
17018         Roo.each(more, function(m){
17019             var cfg = {
17020                 cls : 'fc-event-hori fc-event-draggable',
17021                 html : m.title
17022             };
17023             var cg = ctr.createChild(cfg);
17024             
17025             cg.on('click', _this.onEventClick, _this, m);
17026         });
17027         
17028         this.calpopover.show(el);
17029         
17030         
17031     },
17032     
17033     onLoad: function () 
17034     {   
17035         this.calevents = [];
17036         var cal = this;
17037         
17038         if(this.store.getCount() > 0){
17039             this.store.data.each(function(d){
17040                cal.addItem({
17041                     id : d.data.id,
17042                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17043                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17044                     time : d.data.start_time,
17045                     title : d.data.title,
17046                     description : d.data.description,
17047                     venue : d.data.venue
17048                 });
17049             });
17050         }
17051         
17052         this.renderEvents();
17053         
17054         if(this.calevents.length && this.loadMask){
17055             this.maskEl.hide();
17056         }
17057     },
17058     
17059     onBeforeLoad: function()
17060     {
17061         this.clearEvents();
17062         if(this.loadMask){
17063             this.maskEl.show();
17064         }
17065     }
17066 });
17067
17068  
17069  /*
17070  * - LGPL
17071  *
17072  * element
17073  * 
17074  */
17075
17076 /**
17077  * @class Roo.bootstrap.Popover
17078  * @extends Roo.bootstrap.Component
17079  * Bootstrap Popover class
17080  * @cfg {String} html contents of the popover   (or false to use children..)
17081  * @cfg {String} title of popover (or false to hide)
17082  * @cfg {String} placement how it is placed
17083  * @cfg {String} trigger click || hover (or false to trigger manually)
17084  * @cfg {String} over what (parent or false to trigger manually.)
17085  * @cfg {Number} delay - delay before showing
17086  
17087  * @constructor
17088  * Create a new Popover
17089  * @param {Object} config The config object
17090  */
17091
17092 Roo.bootstrap.Popover = function(config){
17093     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17094     
17095     this.addEvents({
17096         // raw events
17097          /**
17098          * @event show
17099          * After the popover show
17100          * 
17101          * @param {Roo.bootstrap.Popover} this
17102          */
17103         "show" : true,
17104         /**
17105          * @event hide
17106          * After the popover hide
17107          * 
17108          * @param {Roo.bootstrap.Popover} this
17109          */
17110         "hide" : true
17111     });
17112 };
17113
17114 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17115     
17116     title: 'Fill in a title',
17117     html: false,
17118     
17119     placement : 'right',
17120     trigger : 'hover', // hover
17121     
17122     delay : 0,
17123     
17124     over: 'parent',
17125     
17126     can_build_overlaid : false,
17127     
17128     getChildContainer : function()
17129     {
17130         return this.el.select('.popover-content',true).first();
17131     },
17132     
17133     getAutoCreate : function(){
17134          
17135         var cfg = {
17136            cls : 'popover roo-dynamic',
17137            style: 'display:block',
17138            cn : [
17139                 {
17140                     cls : 'arrow'
17141                 },
17142                 {
17143                     cls : 'popover-inner',
17144                     cn : [
17145                         {
17146                             tag: 'h3',
17147                             cls: 'popover-title',
17148                             html : this.title
17149                         },
17150                         {
17151                             cls : 'popover-content',
17152                             html : this.html
17153                         }
17154                     ]
17155                     
17156                 }
17157            ]
17158         };
17159         
17160         return cfg;
17161     },
17162     setTitle: function(str)
17163     {
17164         this.title = str;
17165         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17166     },
17167     setContent: function(str)
17168     {
17169         this.html = str;
17170         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17171     },
17172     // as it get's added to the bottom of the page.
17173     onRender : function(ct, position)
17174     {
17175         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17176         if(!this.el){
17177             var cfg = Roo.apply({},  this.getAutoCreate());
17178             cfg.id = Roo.id();
17179             
17180             if (this.cls) {
17181                 cfg.cls += ' ' + this.cls;
17182             }
17183             if (this.style) {
17184                 cfg.style = this.style;
17185             }
17186             //Roo.log("adding to ");
17187             this.el = Roo.get(document.body).createChild(cfg, position);
17188 //            Roo.log(this.el);
17189         }
17190         this.initEvents();
17191     },
17192     
17193     initEvents : function()
17194     {
17195         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17196         this.el.enableDisplayMode('block');
17197         this.el.hide();
17198         if (this.over === false) {
17199             return; 
17200         }
17201         if (this.triggers === false) {
17202             return;
17203         }
17204         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17205         var triggers = this.trigger ? this.trigger.split(' ') : [];
17206         Roo.each(triggers, function(trigger) {
17207         
17208             if (trigger == 'click') {
17209                 on_el.on('click', this.toggle, this);
17210             } else if (trigger != 'manual') {
17211                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17212                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17213       
17214                 on_el.on(eventIn  ,this.enter, this);
17215                 on_el.on(eventOut, this.leave, this);
17216             }
17217         }, this);
17218         
17219     },
17220     
17221     
17222     // private
17223     timeout : null,
17224     hoverState : null,
17225     
17226     toggle : function () {
17227         this.hoverState == 'in' ? this.leave() : this.enter();
17228     },
17229     
17230     enter : function () {
17231         
17232         clearTimeout(this.timeout);
17233     
17234         this.hoverState = 'in';
17235     
17236         if (!this.delay || !this.delay.show) {
17237             this.show();
17238             return;
17239         }
17240         var _t = this;
17241         this.timeout = setTimeout(function () {
17242             if (_t.hoverState == 'in') {
17243                 _t.show();
17244             }
17245         }, this.delay.show)
17246     },
17247     
17248     leave : function() {
17249         clearTimeout(this.timeout);
17250     
17251         this.hoverState = 'out';
17252     
17253         if (!this.delay || !this.delay.hide) {
17254             this.hide();
17255             return;
17256         }
17257         var _t = this;
17258         this.timeout = setTimeout(function () {
17259             if (_t.hoverState == 'out') {
17260                 _t.hide();
17261             }
17262         }, this.delay.hide)
17263     },
17264     
17265     show : function (on_el)
17266     {
17267         if (!on_el) {
17268             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17269         }
17270         
17271         // set content.
17272         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17273         if (this.html !== false) {
17274             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17275         }
17276         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17277         if (!this.title.length) {
17278             this.el.select('.popover-title',true).hide();
17279         }
17280         
17281         var placement = typeof this.placement == 'function' ?
17282             this.placement.call(this, this.el, on_el) :
17283             this.placement;
17284             
17285         var autoToken = /\s?auto?\s?/i;
17286         var autoPlace = autoToken.test(placement);
17287         if (autoPlace) {
17288             placement = placement.replace(autoToken, '') || 'top';
17289         }
17290         
17291         //this.el.detach()
17292         //this.el.setXY([0,0]);
17293         this.el.show();
17294         this.el.dom.style.display='block';
17295         this.el.addClass(placement);
17296         
17297         //this.el.appendTo(on_el);
17298         
17299         var p = this.getPosition();
17300         var box = this.el.getBox();
17301         
17302         if (autoPlace) {
17303             // fixme..
17304         }
17305         var align = Roo.bootstrap.Popover.alignment[placement];
17306         this.el.alignTo(on_el, align[0],align[1]);
17307         //var arrow = this.el.select('.arrow',true).first();
17308         //arrow.set(align[2], 
17309         
17310         this.el.addClass('in');
17311         
17312         
17313         if (this.el.hasClass('fade')) {
17314             // fade it?
17315         }
17316         
17317         this.hoverState = 'in';
17318         
17319         this.fireEvent('show', this);
17320         
17321     },
17322     hide : function()
17323     {
17324         this.el.setXY([0,0]);
17325         this.el.removeClass('in');
17326         this.el.hide();
17327         this.hoverState = null;
17328         
17329         this.fireEvent('hide', this);
17330     }
17331     
17332 });
17333
17334 Roo.bootstrap.Popover.alignment = {
17335     'left' : ['r-l', [-10,0], 'right'],
17336     'right' : ['l-r', [10,0], 'left'],
17337     'bottom' : ['t-b', [0,10], 'top'],
17338     'top' : [ 'b-t', [0,-10], 'bottom']
17339 };
17340
17341  /*
17342  * - LGPL
17343  *
17344  * Progress
17345  * 
17346  */
17347
17348 /**
17349  * @class Roo.bootstrap.Progress
17350  * @extends Roo.bootstrap.Component
17351  * Bootstrap Progress class
17352  * @cfg {Boolean} striped striped of the progress bar
17353  * @cfg {Boolean} active animated of the progress bar
17354  * 
17355  * 
17356  * @constructor
17357  * Create a new Progress
17358  * @param {Object} config The config object
17359  */
17360
17361 Roo.bootstrap.Progress = function(config){
17362     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17363 };
17364
17365 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17366     
17367     striped : false,
17368     active: false,
17369     
17370     getAutoCreate : function(){
17371         var cfg = {
17372             tag: 'div',
17373             cls: 'progress'
17374         };
17375         
17376         
17377         if(this.striped){
17378             cfg.cls += ' progress-striped';
17379         }
17380       
17381         if(this.active){
17382             cfg.cls += ' active';
17383         }
17384         
17385         
17386         return cfg;
17387     }
17388    
17389 });
17390
17391  
17392
17393  /*
17394  * - LGPL
17395  *
17396  * ProgressBar
17397  * 
17398  */
17399
17400 /**
17401  * @class Roo.bootstrap.ProgressBar
17402  * @extends Roo.bootstrap.Component
17403  * Bootstrap ProgressBar class
17404  * @cfg {Number} aria_valuenow aria-value now
17405  * @cfg {Number} aria_valuemin aria-value min
17406  * @cfg {Number} aria_valuemax aria-value max
17407  * @cfg {String} label label for the progress bar
17408  * @cfg {String} panel (success | info | warning | danger )
17409  * @cfg {String} role role of the progress bar
17410  * @cfg {String} sr_only text
17411  * 
17412  * 
17413  * @constructor
17414  * Create a new ProgressBar
17415  * @param {Object} config The config object
17416  */
17417
17418 Roo.bootstrap.ProgressBar = function(config){
17419     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17420 };
17421
17422 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17423     
17424     aria_valuenow : 0,
17425     aria_valuemin : 0,
17426     aria_valuemax : 100,
17427     label : false,
17428     panel : false,
17429     role : false,
17430     sr_only: false,
17431     
17432     getAutoCreate : function()
17433     {
17434         
17435         var cfg = {
17436             tag: 'div',
17437             cls: 'progress-bar',
17438             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17439         };
17440         
17441         if(this.sr_only){
17442             cfg.cn = {
17443                 tag: 'span',
17444                 cls: 'sr-only',
17445                 html: this.sr_only
17446             }
17447         }
17448         
17449         if(this.role){
17450             cfg.role = this.role;
17451         }
17452         
17453         if(this.aria_valuenow){
17454             cfg['aria-valuenow'] = this.aria_valuenow;
17455         }
17456         
17457         if(this.aria_valuemin){
17458             cfg['aria-valuemin'] = this.aria_valuemin;
17459         }
17460         
17461         if(this.aria_valuemax){
17462             cfg['aria-valuemax'] = this.aria_valuemax;
17463         }
17464         
17465         if(this.label && !this.sr_only){
17466             cfg.html = this.label;
17467         }
17468         
17469         if(this.panel){
17470             cfg.cls += ' progress-bar-' + this.panel;
17471         }
17472         
17473         return cfg;
17474     },
17475     
17476     update : function(aria_valuenow)
17477     {
17478         this.aria_valuenow = aria_valuenow;
17479         
17480         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17481     }
17482    
17483 });
17484
17485  
17486
17487  /*
17488  * - LGPL
17489  *
17490  * column
17491  * 
17492  */
17493
17494 /**
17495  * @class Roo.bootstrap.TabGroup
17496  * @extends Roo.bootstrap.Column
17497  * Bootstrap Column class
17498  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17499  * @cfg {Boolean} carousel true to make the group behave like a carousel
17500  * @cfg {Boolean} bullets show bullets for the panels
17501  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17502  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17503  * @cfg {Boolean} showarrow (true|false) show arrow default true
17504  * 
17505  * @constructor
17506  * Create a new TabGroup
17507  * @param {Object} config The config object
17508  */
17509
17510 Roo.bootstrap.TabGroup = function(config){
17511     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17512     if (!this.navId) {
17513         this.navId = Roo.id();
17514     }
17515     this.tabs = [];
17516     Roo.bootstrap.TabGroup.register(this);
17517     
17518 };
17519
17520 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17521     
17522     carousel : false,
17523     transition : false,
17524     bullets : 0,
17525     timer : 0,
17526     autoslide : false,
17527     slideFn : false,
17528     slideOnTouch : false,
17529     showarrow : true,
17530     
17531     getAutoCreate : function()
17532     {
17533         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17534         
17535         cfg.cls += ' tab-content';
17536         
17537         if (this.carousel) {
17538             cfg.cls += ' carousel slide';
17539             
17540             cfg.cn = [{
17541                cls : 'carousel-inner',
17542                cn : []
17543             }];
17544         
17545             if(this.bullets  && !Roo.isTouch){
17546                 
17547                 var bullets = {
17548                     cls : 'carousel-bullets',
17549                     cn : []
17550                 };
17551                
17552                 if(this.bullets_cls){
17553                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17554                 }
17555                 
17556                 bullets.cn.push({
17557                     cls : 'clear'
17558                 });
17559                 
17560                 cfg.cn[0].cn.push(bullets);
17561             }
17562             
17563             if(this.showarrow){
17564                 cfg.cn[0].cn.push({
17565                     tag : 'div',
17566                     class : 'carousel-arrow',
17567                     cn : [
17568                         {
17569                             tag : 'div',
17570                             class : 'carousel-prev',
17571                             cn : [
17572                                 {
17573                                     tag : 'i',
17574                                     class : 'fa fa-chevron-left'
17575                                 }
17576                             ]
17577                         },
17578                         {
17579                             tag : 'div',
17580                             class : 'carousel-next',
17581                             cn : [
17582                                 {
17583                                     tag : 'i',
17584                                     class : 'fa fa-chevron-right'
17585                                 }
17586                             ]
17587                         }
17588                     ]
17589                 });
17590             }
17591             
17592         }
17593         
17594         return cfg;
17595     },
17596     
17597     initEvents:  function()
17598     {
17599 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17600 //            this.el.on("touchstart", this.onTouchStart, this);
17601 //        }
17602         
17603         if(this.autoslide){
17604             var _this = this;
17605             
17606             this.slideFn = window.setInterval(function() {
17607                 _this.showPanelNext();
17608             }, this.timer);
17609         }
17610         
17611         if(this.showarrow){
17612             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17613             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17614         }
17615         
17616         
17617     },
17618     
17619 //    onTouchStart : function(e, el, o)
17620 //    {
17621 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17622 //            return;
17623 //        }
17624 //        
17625 //        this.showPanelNext();
17626 //    },
17627     
17628     
17629     getChildContainer : function()
17630     {
17631         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17632     },
17633     
17634     /**
17635     * register a Navigation item
17636     * @param {Roo.bootstrap.NavItem} the navitem to add
17637     */
17638     register : function(item)
17639     {
17640         this.tabs.push( item);
17641         item.navId = this.navId; // not really needed..
17642         this.addBullet();
17643     
17644     },
17645     
17646     getActivePanel : function()
17647     {
17648         var r = false;
17649         Roo.each(this.tabs, function(t) {
17650             if (t.active) {
17651                 r = t;
17652                 return false;
17653             }
17654             return null;
17655         });
17656         return r;
17657         
17658     },
17659     getPanelByName : function(n)
17660     {
17661         var r = false;
17662         Roo.each(this.tabs, function(t) {
17663             if (t.tabId == n) {
17664                 r = t;
17665                 return false;
17666             }
17667             return null;
17668         });
17669         return r;
17670     },
17671     indexOfPanel : function(p)
17672     {
17673         var r = false;
17674         Roo.each(this.tabs, function(t,i) {
17675             if (t.tabId == p.tabId) {
17676                 r = i;
17677                 return false;
17678             }
17679             return null;
17680         });
17681         return r;
17682     },
17683     /**
17684      * show a specific panel
17685      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17686      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17687      */
17688     showPanel : function (pan)
17689     {
17690         if(this.transition || typeof(pan) == 'undefined'){
17691             Roo.log("waiting for the transitionend");
17692             return;
17693         }
17694         
17695         if (typeof(pan) == 'number') {
17696             pan = this.tabs[pan];
17697         }
17698         
17699         if (typeof(pan) == 'string') {
17700             pan = this.getPanelByName(pan);
17701         }
17702         
17703         var cur = this.getActivePanel();
17704         
17705         if(!pan || !cur){
17706             Roo.log('pan or acitve pan is undefined');
17707             return false;
17708         }
17709         
17710         if (pan.tabId == this.getActivePanel().tabId) {
17711             return true;
17712         }
17713         
17714         if (false === cur.fireEvent('beforedeactivate')) {
17715             return false;
17716         }
17717         
17718         if(this.bullets > 0 && !Roo.isTouch){
17719             this.setActiveBullet(this.indexOfPanel(pan));
17720         }
17721         
17722         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17723             
17724             this.transition = true;
17725             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17726             var lr = dir == 'next' ? 'left' : 'right';
17727             pan.el.addClass(dir); // or prev
17728             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17729             cur.el.addClass(lr); // or right
17730             pan.el.addClass(lr);
17731             
17732             var _this = this;
17733             cur.el.on('transitionend', function() {
17734                 Roo.log("trans end?");
17735                 
17736                 pan.el.removeClass([lr,dir]);
17737                 pan.setActive(true);
17738                 
17739                 cur.el.removeClass([lr]);
17740                 cur.setActive(false);
17741                 
17742                 _this.transition = false;
17743                 
17744             }, this, { single:  true } );
17745             
17746             return true;
17747         }
17748         
17749         cur.setActive(false);
17750         pan.setActive(true);
17751         
17752         return true;
17753         
17754     },
17755     showPanelNext : function()
17756     {
17757         var i = this.indexOfPanel(this.getActivePanel());
17758         
17759         if (i >= this.tabs.length - 1 && !this.autoslide) {
17760             return;
17761         }
17762         
17763         if (i >= this.tabs.length - 1 && this.autoslide) {
17764             i = -1;
17765         }
17766         
17767         this.showPanel(this.tabs[i+1]);
17768     },
17769     
17770     showPanelPrev : function()
17771     {
17772         var i = this.indexOfPanel(this.getActivePanel());
17773         
17774         if (i  < 1 && !this.autoslide) {
17775             return;
17776         }
17777         
17778         if (i < 1 && this.autoslide) {
17779             i = this.tabs.length;
17780         }
17781         
17782         this.showPanel(this.tabs[i-1]);
17783     },
17784     
17785     
17786     addBullet: function()
17787     {
17788         if(!this.bullets || Roo.isTouch){
17789             return;
17790         }
17791         var ctr = this.el.select('.carousel-bullets',true).first();
17792         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17793         var bullet = ctr.createChild({
17794             cls : 'bullet bullet-' + i
17795         },ctr.dom.lastChild);
17796         
17797         
17798         var _this = this;
17799         
17800         bullet.on('click', (function(e, el, o, ii, t){
17801
17802             e.preventDefault();
17803
17804             this.showPanel(ii);
17805
17806             if(this.autoslide && this.slideFn){
17807                 clearInterval(this.slideFn);
17808                 this.slideFn = window.setInterval(function() {
17809                     _this.showPanelNext();
17810                 }, this.timer);
17811             }
17812
17813         }).createDelegate(this, [i, bullet], true));
17814                 
17815         
17816     },
17817      
17818     setActiveBullet : function(i)
17819     {
17820         if(Roo.isTouch){
17821             return;
17822         }
17823         
17824         Roo.each(this.el.select('.bullet', true).elements, function(el){
17825             el.removeClass('selected');
17826         });
17827
17828         var bullet = this.el.select('.bullet-' + i, true).first();
17829         
17830         if(!bullet){
17831             return;
17832         }
17833         
17834         bullet.addClass('selected');
17835     }
17836     
17837     
17838   
17839 });
17840
17841  
17842
17843  
17844  
17845 Roo.apply(Roo.bootstrap.TabGroup, {
17846     
17847     groups: {},
17848      /**
17849     * register a Navigation Group
17850     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17851     */
17852     register : function(navgrp)
17853     {
17854         this.groups[navgrp.navId] = navgrp;
17855         
17856     },
17857     /**
17858     * fetch a Navigation Group based on the navigation ID
17859     * if one does not exist , it will get created.
17860     * @param {string} the navgroup to add
17861     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17862     */
17863     get: function(navId) {
17864         if (typeof(this.groups[navId]) == 'undefined') {
17865             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17866         }
17867         return this.groups[navId] ;
17868     }
17869     
17870     
17871     
17872 });
17873
17874  /*
17875  * - LGPL
17876  *
17877  * TabPanel
17878  * 
17879  */
17880
17881 /**
17882  * @class Roo.bootstrap.TabPanel
17883  * @extends Roo.bootstrap.Component
17884  * Bootstrap TabPanel class
17885  * @cfg {Boolean} active panel active
17886  * @cfg {String} html panel content
17887  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17888  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17889  * @cfg {String} href click to link..
17890  * 
17891  * 
17892  * @constructor
17893  * Create a new TabPanel
17894  * @param {Object} config The config object
17895  */
17896
17897 Roo.bootstrap.TabPanel = function(config){
17898     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17899     this.addEvents({
17900         /**
17901              * @event changed
17902              * Fires when the active status changes
17903              * @param {Roo.bootstrap.TabPanel} this
17904              * @param {Boolean} state the new state
17905             
17906          */
17907         'changed': true,
17908         /**
17909              * @event beforedeactivate
17910              * Fires before a tab is de-activated - can be used to do validation on a form.
17911              * @param {Roo.bootstrap.TabPanel} this
17912              * @return {Boolean} false if there is an error
17913             
17914          */
17915         'beforedeactivate': true
17916      });
17917     
17918     this.tabId = this.tabId || Roo.id();
17919   
17920 };
17921
17922 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17923     
17924     active: false,
17925     html: false,
17926     tabId: false,
17927     navId : false,
17928     href : '',
17929     
17930     getAutoCreate : function(){
17931         var cfg = {
17932             tag: 'div',
17933             // item is needed for carousel - not sure if it has any effect otherwise
17934             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17935             html: this.html || ''
17936         };
17937         
17938         if(this.active){
17939             cfg.cls += ' active';
17940         }
17941         
17942         if(this.tabId){
17943             cfg.tabId = this.tabId;
17944         }
17945         
17946         
17947         return cfg;
17948     },
17949     
17950     initEvents:  function()
17951     {
17952         var p = this.parent();
17953         
17954         this.navId = this.navId || p.navId;
17955         
17956         if (typeof(this.navId) != 'undefined') {
17957             // not really needed.. but just in case.. parent should be a NavGroup.
17958             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17959             
17960             tg.register(this);
17961             
17962             var i = tg.tabs.length - 1;
17963             
17964             if(this.active && tg.bullets > 0 && i < tg.bullets){
17965                 tg.setActiveBullet(i);
17966             }
17967         }
17968         
17969         this.el.on('click', this.onClick, this);
17970         
17971         if(Roo.isTouch){
17972             this.el.on("touchstart", this.onTouchStart, this);
17973             this.el.on("touchmove", this.onTouchMove, this);
17974             this.el.on("touchend", this.onTouchEnd, this);
17975         }
17976         
17977     },
17978     
17979     onRender : function(ct, position)
17980     {
17981         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17982     },
17983     
17984     setActive : function(state)
17985     {
17986         Roo.log("panel - set active " + this.tabId + "=" + state);
17987         
17988         this.active = state;
17989         if (!state) {
17990             this.el.removeClass('active');
17991             
17992         } else  if (!this.el.hasClass('active')) {
17993             this.el.addClass('active');
17994         }
17995         
17996         this.fireEvent('changed', this, state);
17997     },
17998     
17999     onClick : function(e)
18000     {
18001         e.preventDefault();
18002         
18003         if(!this.href.length){
18004             return;
18005         }
18006         
18007         window.location.href = this.href;
18008     },
18009     
18010     startX : 0,
18011     startY : 0,
18012     endX : 0,
18013     endY : 0,
18014     swiping : false,
18015     
18016     onTouchStart : function(e)
18017     {
18018         this.swiping = false;
18019         
18020         this.startX = e.browserEvent.touches[0].clientX;
18021         this.startY = e.browserEvent.touches[0].clientY;
18022     },
18023     
18024     onTouchMove : function(e)
18025     {
18026         this.swiping = true;
18027         
18028         this.endX = e.browserEvent.touches[0].clientX;
18029         this.endY = e.browserEvent.touches[0].clientY;
18030     },
18031     
18032     onTouchEnd : function(e)
18033     {
18034         if(!this.swiping){
18035             this.onClick(e);
18036             return;
18037         }
18038         
18039         var tabGroup = this.parent();
18040         
18041         if(this.endX > this.startX){ // swiping right
18042             tabGroup.showPanelPrev();
18043             return;
18044         }
18045         
18046         if(this.startX > this.endX){ // swiping left
18047             tabGroup.showPanelNext();
18048             return;
18049         }
18050     }
18051     
18052     
18053 });
18054  
18055
18056  
18057
18058  /*
18059  * - LGPL
18060  *
18061  * DateField
18062  * 
18063  */
18064
18065 /**
18066  * @class Roo.bootstrap.DateField
18067  * @extends Roo.bootstrap.Input
18068  * Bootstrap DateField class
18069  * @cfg {Number} weekStart default 0
18070  * @cfg {String} viewMode default empty, (months|years)
18071  * @cfg {String} minViewMode default empty, (months|years)
18072  * @cfg {Number} startDate default -Infinity
18073  * @cfg {Number} endDate default Infinity
18074  * @cfg {Boolean} todayHighlight default false
18075  * @cfg {Boolean} todayBtn default false
18076  * @cfg {Boolean} calendarWeeks default false
18077  * @cfg {Object} daysOfWeekDisabled default empty
18078  * @cfg {Boolean} singleMode default false (true | false)
18079  * 
18080  * @cfg {Boolean} keyboardNavigation default true
18081  * @cfg {String} language default en
18082  * 
18083  * @constructor
18084  * Create a new DateField
18085  * @param {Object} config The config object
18086  */
18087
18088 Roo.bootstrap.DateField = function(config){
18089     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18090      this.addEvents({
18091             /**
18092              * @event show
18093              * Fires when this field show.
18094              * @param {Roo.bootstrap.DateField} this
18095              * @param {Mixed} date The date value
18096              */
18097             show : true,
18098             /**
18099              * @event show
18100              * Fires when this field hide.
18101              * @param {Roo.bootstrap.DateField} this
18102              * @param {Mixed} date The date value
18103              */
18104             hide : true,
18105             /**
18106              * @event select
18107              * Fires when select a date.
18108              * @param {Roo.bootstrap.DateField} this
18109              * @param {Mixed} date The date value
18110              */
18111             select : true,
18112             /**
18113              * @event beforeselect
18114              * Fires when before select a date.
18115              * @param {Roo.bootstrap.DateField} this
18116              * @param {Mixed} date The date value
18117              */
18118             beforeselect : true
18119         });
18120 };
18121
18122 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18123     
18124     /**
18125      * @cfg {String} format
18126      * The default date format string which can be overriden for localization support.  The format must be
18127      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18128      */
18129     format : "m/d/y",
18130     /**
18131      * @cfg {String} altFormats
18132      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18133      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18134      */
18135     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18136     
18137     weekStart : 0,
18138     
18139     viewMode : '',
18140     
18141     minViewMode : '',
18142     
18143     todayHighlight : false,
18144     
18145     todayBtn: false,
18146     
18147     language: 'en',
18148     
18149     keyboardNavigation: true,
18150     
18151     calendarWeeks: false,
18152     
18153     startDate: -Infinity,
18154     
18155     endDate: Infinity,
18156     
18157     daysOfWeekDisabled: [],
18158     
18159     _events: [],
18160     
18161     singleMode : false,
18162     
18163     UTCDate: function()
18164     {
18165         return new Date(Date.UTC.apply(Date, arguments));
18166     },
18167     
18168     UTCToday: function()
18169     {
18170         var today = new Date();
18171         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18172     },
18173     
18174     getDate: function() {
18175             var d = this.getUTCDate();
18176             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18177     },
18178     
18179     getUTCDate: function() {
18180             return this.date;
18181     },
18182     
18183     setDate: function(d) {
18184             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18185     },
18186     
18187     setUTCDate: function(d) {
18188             this.date = d;
18189             this.setValue(this.formatDate(this.date));
18190     },
18191         
18192     onRender: function(ct, position)
18193     {
18194         
18195         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18196         
18197         this.language = this.language || 'en';
18198         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18199         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18200         
18201         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18202         this.format = this.format || 'm/d/y';
18203         this.isInline = false;
18204         this.isInput = true;
18205         this.component = this.el.select('.add-on', true).first() || false;
18206         this.component = (this.component && this.component.length === 0) ? false : this.component;
18207         this.hasInput = this.component && this.inputEl().length;
18208         
18209         if (typeof(this.minViewMode === 'string')) {
18210             switch (this.minViewMode) {
18211                 case 'months':
18212                     this.minViewMode = 1;
18213                     break;
18214                 case 'years':
18215                     this.minViewMode = 2;
18216                     break;
18217                 default:
18218                     this.minViewMode = 0;
18219                     break;
18220             }
18221         }
18222         
18223         if (typeof(this.viewMode === 'string')) {
18224             switch (this.viewMode) {
18225                 case 'months':
18226                     this.viewMode = 1;
18227                     break;
18228                 case 'years':
18229                     this.viewMode = 2;
18230                     break;
18231                 default:
18232                     this.viewMode = 0;
18233                     break;
18234             }
18235         }
18236                 
18237         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18238         
18239 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18240         
18241         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18242         
18243         this.picker().on('mousedown', this.onMousedown, this);
18244         this.picker().on('click', this.onClick, this);
18245         
18246         this.picker().addClass('datepicker-dropdown');
18247         
18248         this.startViewMode = this.viewMode;
18249         
18250         if(this.singleMode){
18251             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18252                 v.setVisibilityMode(Roo.Element.DISPLAY);
18253                 v.hide();
18254             });
18255             
18256             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18257                 v.setStyle('width', '189px');
18258             });
18259         }
18260         
18261         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18262             if(!this.calendarWeeks){
18263                 v.remove();
18264                 return;
18265             }
18266             
18267             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18268             v.attr('colspan', function(i, val){
18269                 return parseInt(val) + 1;
18270             });
18271         });
18272                         
18273         
18274         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18275         
18276         this.setStartDate(this.startDate);
18277         this.setEndDate(this.endDate);
18278         
18279         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18280         
18281         this.fillDow();
18282         this.fillMonths();
18283         this.update();
18284         this.showMode();
18285         
18286         if(this.isInline) {
18287             this.show();
18288         }
18289     },
18290     
18291     picker : function()
18292     {
18293         return this.pickerEl;
18294 //        return this.el.select('.datepicker', true).first();
18295     },
18296     
18297     fillDow: function()
18298     {
18299         var dowCnt = this.weekStart;
18300         
18301         var dow = {
18302             tag: 'tr',
18303             cn: [
18304                 
18305             ]
18306         };
18307         
18308         if(this.calendarWeeks){
18309             dow.cn.push({
18310                 tag: 'th',
18311                 cls: 'cw',
18312                 html: '&nbsp;'
18313             })
18314         }
18315         
18316         while (dowCnt < this.weekStart + 7) {
18317             dow.cn.push({
18318                 tag: 'th',
18319                 cls: 'dow',
18320                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18321             });
18322         }
18323         
18324         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18325     },
18326     
18327     fillMonths: function()
18328     {    
18329         var i = 0;
18330         var months = this.picker().select('>.datepicker-months td', true).first();
18331         
18332         months.dom.innerHTML = '';
18333         
18334         while (i < 12) {
18335             var month = {
18336                 tag: 'span',
18337                 cls: 'month',
18338                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18339             };
18340             
18341             months.createChild(month);
18342         }
18343         
18344     },
18345     
18346     update: function()
18347     {
18348         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;
18349         
18350         if (this.date < this.startDate) {
18351             this.viewDate = new Date(this.startDate);
18352         } else if (this.date > this.endDate) {
18353             this.viewDate = new Date(this.endDate);
18354         } else {
18355             this.viewDate = new Date(this.date);
18356         }
18357         
18358         this.fill();
18359     },
18360     
18361     fill: function() 
18362     {
18363         var d = new Date(this.viewDate),
18364                 year = d.getUTCFullYear(),
18365                 month = d.getUTCMonth(),
18366                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18367                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18368                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18369                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18370                 currentDate = this.date && this.date.valueOf(),
18371                 today = this.UTCToday();
18372         
18373         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18374         
18375 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18376         
18377 //        this.picker.select('>tfoot th.today').
18378 //                                              .text(dates[this.language].today)
18379 //                                              .toggle(this.todayBtn !== false);
18380     
18381         this.updateNavArrows();
18382         this.fillMonths();
18383                                                 
18384         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18385         
18386         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18387          
18388         prevMonth.setUTCDate(day);
18389         
18390         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18391         
18392         var nextMonth = new Date(prevMonth);
18393         
18394         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18395         
18396         nextMonth = nextMonth.valueOf();
18397         
18398         var fillMonths = false;
18399         
18400         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18401         
18402         while(prevMonth.valueOf() < nextMonth) {
18403             var clsName = '';
18404             
18405             if (prevMonth.getUTCDay() === this.weekStart) {
18406                 if(fillMonths){
18407                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18408                 }
18409                     
18410                 fillMonths = {
18411                     tag: 'tr',
18412                     cn: []
18413                 };
18414                 
18415                 if(this.calendarWeeks){
18416                     // ISO 8601: First week contains first thursday.
18417                     // ISO also states week starts on Monday, but we can be more abstract here.
18418                     var
18419                     // Start of current week: based on weekstart/current date
18420                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18421                     // Thursday of this week
18422                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18423                     // First Thursday of year, year from thursday
18424                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18425                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18426                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18427                     
18428                     fillMonths.cn.push({
18429                         tag: 'td',
18430                         cls: 'cw',
18431                         html: calWeek
18432                     });
18433                 }
18434             }
18435             
18436             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18437                 clsName += ' old';
18438             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18439                 clsName += ' new';
18440             }
18441             if (this.todayHighlight &&
18442                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18443                 prevMonth.getUTCMonth() == today.getMonth() &&
18444                 prevMonth.getUTCDate() == today.getDate()) {
18445                 clsName += ' today';
18446             }
18447             
18448             if (currentDate && prevMonth.valueOf() === currentDate) {
18449                 clsName += ' active';
18450             }
18451             
18452             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18453                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18454                     clsName += ' disabled';
18455             }
18456             
18457             fillMonths.cn.push({
18458                 tag: 'td',
18459                 cls: 'day ' + clsName,
18460                 html: prevMonth.getDate()
18461             });
18462             
18463             prevMonth.setDate(prevMonth.getDate()+1);
18464         }
18465           
18466         var currentYear = this.date && this.date.getUTCFullYear();
18467         var currentMonth = this.date && this.date.getUTCMonth();
18468         
18469         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18470         
18471         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18472             v.removeClass('active');
18473             
18474             if(currentYear === year && k === currentMonth){
18475                 v.addClass('active');
18476             }
18477             
18478             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18479                 v.addClass('disabled');
18480             }
18481             
18482         });
18483         
18484         
18485         year = parseInt(year/10, 10) * 10;
18486         
18487         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18488         
18489         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18490         
18491         year -= 1;
18492         for (var i = -1; i < 11; i++) {
18493             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18494                 tag: 'span',
18495                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18496                 html: year
18497             });
18498             
18499             year += 1;
18500         }
18501     },
18502     
18503     showMode: function(dir) 
18504     {
18505         if (dir) {
18506             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18507         }
18508         
18509         Roo.each(this.picker().select('>div',true).elements, function(v){
18510             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18511             v.hide();
18512         });
18513         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18514     },
18515     
18516     place: function()
18517     {
18518         if(this.isInline) {
18519             return;
18520         }
18521         
18522         this.picker().removeClass(['bottom', 'top']);
18523         
18524         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18525             /*
18526              * place to the top of element!
18527              *
18528              */
18529             
18530             this.picker().addClass('top');
18531             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18532             
18533             return;
18534         }
18535         
18536         this.picker().addClass('bottom');
18537         
18538         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18539     },
18540     
18541     parseDate : function(value)
18542     {
18543         if(!value || value instanceof Date){
18544             return value;
18545         }
18546         var v = Date.parseDate(value, this.format);
18547         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18548             v = Date.parseDate(value, 'Y-m-d');
18549         }
18550         if(!v && this.altFormats){
18551             if(!this.altFormatsArray){
18552                 this.altFormatsArray = this.altFormats.split("|");
18553             }
18554             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18555                 v = Date.parseDate(value, this.altFormatsArray[i]);
18556             }
18557         }
18558         return v;
18559     },
18560     
18561     formatDate : function(date, fmt)
18562     {   
18563         return (!date || !(date instanceof Date)) ?
18564         date : date.dateFormat(fmt || this.format);
18565     },
18566     
18567     onFocus : function()
18568     {
18569         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18570         this.show();
18571     },
18572     
18573     onBlur : function()
18574     {
18575         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18576         
18577         var d = this.inputEl().getValue();
18578         
18579         this.setValue(d);
18580                 
18581         this.hide();
18582     },
18583     
18584     show : function()
18585     {
18586         this.picker().show();
18587         this.update();
18588         this.place();
18589         
18590         this.fireEvent('show', this, this.date);
18591     },
18592     
18593     hide : function()
18594     {
18595         if(this.isInline) {
18596             return;
18597         }
18598         this.picker().hide();
18599         this.viewMode = this.startViewMode;
18600         this.showMode();
18601         
18602         this.fireEvent('hide', this, this.date);
18603         
18604     },
18605     
18606     onMousedown: function(e)
18607     {
18608         e.stopPropagation();
18609         e.preventDefault();
18610     },
18611     
18612     keyup: function(e)
18613     {
18614         Roo.bootstrap.DateField.superclass.keyup.call(this);
18615         this.update();
18616     },
18617
18618     setValue: function(v)
18619     {
18620         if(this.fireEvent('beforeselect', this, v) !== false){
18621             var d = new Date(this.parseDate(v) ).clearTime();
18622         
18623             if(isNaN(d.getTime())){
18624                 this.date = this.viewDate = '';
18625                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18626                 return;
18627             }
18628
18629             v = this.formatDate(d);
18630
18631             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18632
18633             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18634
18635             this.update();
18636
18637             this.fireEvent('select', this, this.date);
18638         }
18639     },
18640     
18641     getValue: function()
18642     {
18643         return this.formatDate(this.date);
18644     },
18645     
18646     fireKey: function(e)
18647     {
18648         if (!this.picker().isVisible()){
18649             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18650                 this.show();
18651             }
18652             return;
18653         }
18654         
18655         var dateChanged = false,
18656         dir, day, month,
18657         newDate, newViewDate;
18658         
18659         switch(e.keyCode){
18660             case 27: // escape
18661                 this.hide();
18662                 e.preventDefault();
18663                 break;
18664             case 37: // left
18665             case 39: // right
18666                 if (!this.keyboardNavigation) {
18667                     break;
18668                 }
18669                 dir = e.keyCode == 37 ? -1 : 1;
18670                 
18671                 if (e.ctrlKey){
18672                     newDate = this.moveYear(this.date, dir);
18673                     newViewDate = this.moveYear(this.viewDate, dir);
18674                 } else if (e.shiftKey){
18675                     newDate = this.moveMonth(this.date, dir);
18676                     newViewDate = this.moveMonth(this.viewDate, dir);
18677                 } else {
18678                     newDate = new Date(this.date);
18679                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18680                     newViewDate = new Date(this.viewDate);
18681                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18682                 }
18683                 if (this.dateWithinRange(newDate)){
18684                     this.date = newDate;
18685                     this.viewDate = newViewDate;
18686                     this.setValue(this.formatDate(this.date));
18687 //                    this.update();
18688                     e.preventDefault();
18689                     dateChanged = true;
18690                 }
18691                 break;
18692             case 38: // up
18693             case 40: // down
18694                 if (!this.keyboardNavigation) {
18695                     break;
18696                 }
18697                 dir = e.keyCode == 38 ? -1 : 1;
18698                 if (e.ctrlKey){
18699                     newDate = this.moveYear(this.date, dir);
18700                     newViewDate = this.moveYear(this.viewDate, dir);
18701                 } else if (e.shiftKey){
18702                     newDate = this.moveMonth(this.date, dir);
18703                     newViewDate = this.moveMonth(this.viewDate, dir);
18704                 } else {
18705                     newDate = new Date(this.date);
18706                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18707                     newViewDate = new Date(this.viewDate);
18708                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18709                 }
18710                 if (this.dateWithinRange(newDate)){
18711                     this.date = newDate;
18712                     this.viewDate = newViewDate;
18713                     this.setValue(this.formatDate(this.date));
18714 //                    this.update();
18715                     e.preventDefault();
18716                     dateChanged = true;
18717                 }
18718                 break;
18719             case 13: // enter
18720                 this.setValue(this.formatDate(this.date));
18721                 this.hide();
18722                 e.preventDefault();
18723                 break;
18724             case 9: // tab
18725                 this.setValue(this.formatDate(this.date));
18726                 this.hide();
18727                 break;
18728             case 16: // shift
18729             case 17: // ctrl
18730             case 18: // alt
18731                 break;
18732             default :
18733                 this.hide();
18734                 
18735         }
18736     },
18737     
18738     
18739     onClick: function(e) 
18740     {
18741         e.stopPropagation();
18742         e.preventDefault();
18743         
18744         var target = e.getTarget();
18745         
18746         if(target.nodeName.toLowerCase() === 'i'){
18747             target = Roo.get(target).dom.parentNode;
18748         }
18749         
18750         var nodeName = target.nodeName;
18751         var className = target.className;
18752         var html = target.innerHTML;
18753         //Roo.log(nodeName);
18754         
18755         switch(nodeName.toLowerCase()) {
18756             case 'th':
18757                 switch(className) {
18758                     case 'switch':
18759                         this.showMode(1);
18760                         break;
18761                     case 'prev':
18762                     case 'next':
18763                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18764                         switch(this.viewMode){
18765                                 case 0:
18766                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18767                                         break;
18768                                 case 1:
18769                                 case 2:
18770                                         this.viewDate = this.moveYear(this.viewDate, dir);
18771                                         break;
18772                         }
18773                         this.fill();
18774                         break;
18775                     case 'today':
18776                         var date = new Date();
18777                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18778 //                        this.fill()
18779                         this.setValue(this.formatDate(this.date));
18780                         
18781                         this.hide();
18782                         break;
18783                 }
18784                 break;
18785             case 'span':
18786                 if (className.indexOf('disabled') < 0) {
18787                     this.viewDate.setUTCDate(1);
18788                     if (className.indexOf('month') > -1) {
18789                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18790                     } else {
18791                         var year = parseInt(html, 10) || 0;
18792                         this.viewDate.setUTCFullYear(year);
18793                         
18794                     }
18795                     
18796                     if(this.singleMode){
18797                         this.setValue(this.formatDate(this.viewDate));
18798                         this.hide();
18799                         return;
18800                     }
18801                     
18802                     this.showMode(-1);
18803                     this.fill();
18804                 }
18805                 break;
18806                 
18807             case 'td':
18808                 //Roo.log(className);
18809                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18810                     var day = parseInt(html, 10) || 1;
18811                     var year = this.viewDate.getUTCFullYear(),
18812                         month = this.viewDate.getUTCMonth();
18813
18814                     if (className.indexOf('old') > -1) {
18815                         if(month === 0 ){
18816                             month = 11;
18817                             year -= 1;
18818                         }else{
18819                             month -= 1;
18820                         }
18821                     } else if (className.indexOf('new') > -1) {
18822                         if (month == 11) {
18823                             month = 0;
18824                             year += 1;
18825                         } else {
18826                             month += 1;
18827                         }
18828                     }
18829                     //Roo.log([year,month,day]);
18830                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18831                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18832 //                    this.fill();
18833                     //Roo.log(this.formatDate(this.date));
18834                     this.setValue(this.formatDate(this.date));
18835                     this.hide();
18836                 }
18837                 break;
18838         }
18839     },
18840     
18841     setStartDate: function(startDate)
18842     {
18843         this.startDate = startDate || -Infinity;
18844         if (this.startDate !== -Infinity) {
18845             this.startDate = this.parseDate(this.startDate);
18846         }
18847         this.update();
18848         this.updateNavArrows();
18849     },
18850
18851     setEndDate: function(endDate)
18852     {
18853         this.endDate = endDate || Infinity;
18854         if (this.endDate !== Infinity) {
18855             this.endDate = this.parseDate(this.endDate);
18856         }
18857         this.update();
18858         this.updateNavArrows();
18859     },
18860     
18861     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18862     {
18863         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18864         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18865             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18866         }
18867         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18868             return parseInt(d, 10);
18869         });
18870         this.update();
18871         this.updateNavArrows();
18872     },
18873     
18874     updateNavArrows: function() 
18875     {
18876         if(this.singleMode){
18877             return;
18878         }
18879         
18880         var d = new Date(this.viewDate),
18881         year = d.getUTCFullYear(),
18882         month = d.getUTCMonth();
18883         
18884         Roo.each(this.picker().select('.prev', true).elements, function(v){
18885             v.show();
18886             switch (this.viewMode) {
18887                 case 0:
18888
18889                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18890                         v.hide();
18891                     }
18892                     break;
18893                 case 1:
18894                 case 2:
18895                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18896                         v.hide();
18897                     }
18898                     break;
18899             }
18900         });
18901         
18902         Roo.each(this.picker().select('.next', true).elements, function(v){
18903             v.show();
18904             switch (this.viewMode) {
18905                 case 0:
18906
18907                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18908                         v.hide();
18909                     }
18910                     break;
18911                 case 1:
18912                 case 2:
18913                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18914                         v.hide();
18915                     }
18916                     break;
18917             }
18918         })
18919     },
18920     
18921     moveMonth: function(date, dir)
18922     {
18923         if (!dir) {
18924             return date;
18925         }
18926         var new_date = new Date(date.valueOf()),
18927         day = new_date.getUTCDate(),
18928         month = new_date.getUTCMonth(),
18929         mag = Math.abs(dir),
18930         new_month, test;
18931         dir = dir > 0 ? 1 : -1;
18932         if (mag == 1){
18933             test = dir == -1
18934             // If going back one month, make sure month is not current month
18935             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18936             ? function(){
18937                 return new_date.getUTCMonth() == month;
18938             }
18939             // If going forward one month, make sure month is as expected
18940             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18941             : function(){
18942                 return new_date.getUTCMonth() != new_month;
18943             };
18944             new_month = month + dir;
18945             new_date.setUTCMonth(new_month);
18946             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18947             if (new_month < 0 || new_month > 11) {
18948                 new_month = (new_month + 12) % 12;
18949             }
18950         } else {
18951             // For magnitudes >1, move one month at a time...
18952             for (var i=0; i<mag; i++) {
18953                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18954                 new_date = this.moveMonth(new_date, dir);
18955             }
18956             // ...then reset the day, keeping it in the new month
18957             new_month = new_date.getUTCMonth();
18958             new_date.setUTCDate(day);
18959             test = function(){
18960                 return new_month != new_date.getUTCMonth();
18961             };
18962         }
18963         // Common date-resetting loop -- if date is beyond end of month, make it
18964         // end of month
18965         while (test()){
18966             new_date.setUTCDate(--day);
18967             new_date.setUTCMonth(new_month);
18968         }
18969         return new_date;
18970     },
18971
18972     moveYear: function(date, dir)
18973     {
18974         return this.moveMonth(date, dir*12);
18975     },
18976
18977     dateWithinRange: function(date)
18978     {
18979         return date >= this.startDate && date <= this.endDate;
18980     },
18981
18982     
18983     remove: function() 
18984     {
18985         this.picker().remove();
18986     },
18987     
18988     validateValue : function(value)
18989     {
18990         if(value.length < 1)  {
18991             if(this.allowBlank){
18992                 return true;
18993             }
18994             return false;
18995         }
18996         
18997         if(value.length < this.minLength){
18998             return false;
18999         }
19000         if(value.length > this.maxLength){
19001             return false;
19002         }
19003         if(this.vtype){
19004             var vt = Roo.form.VTypes;
19005             if(!vt[this.vtype](value, this)){
19006                 return false;
19007             }
19008         }
19009         if(typeof this.validator == "function"){
19010             var msg = this.validator(value);
19011             if(msg !== true){
19012                 return false;
19013             }
19014         }
19015         
19016         if(this.regex && !this.regex.test(value)){
19017             return false;
19018         }
19019         
19020         if(typeof(this.parseDate(value)) == 'undefined'){
19021             return false;
19022         }
19023         
19024         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19025             return false;
19026         }      
19027         
19028         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19029             return false;
19030         } 
19031         
19032         
19033         return true;
19034     }
19035    
19036 });
19037
19038 Roo.apply(Roo.bootstrap.DateField,  {
19039     
19040     head : {
19041         tag: 'thead',
19042         cn: [
19043         {
19044             tag: 'tr',
19045             cn: [
19046             {
19047                 tag: 'th',
19048                 cls: 'prev',
19049                 html: '<i class="fa fa-arrow-left"/>'
19050             },
19051             {
19052                 tag: 'th',
19053                 cls: 'switch',
19054                 colspan: '5'
19055             },
19056             {
19057                 tag: 'th',
19058                 cls: 'next',
19059                 html: '<i class="fa fa-arrow-right"/>'
19060             }
19061
19062             ]
19063         }
19064         ]
19065     },
19066     
19067     content : {
19068         tag: 'tbody',
19069         cn: [
19070         {
19071             tag: 'tr',
19072             cn: [
19073             {
19074                 tag: 'td',
19075                 colspan: '7'
19076             }
19077             ]
19078         }
19079         ]
19080     },
19081     
19082     footer : {
19083         tag: 'tfoot',
19084         cn: [
19085         {
19086             tag: 'tr',
19087             cn: [
19088             {
19089                 tag: 'th',
19090                 colspan: '7',
19091                 cls: 'today'
19092             }
19093                     
19094             ]
19095         }
19096         ]
19097     },
19098     
19099     dates:{
19100         en: {
19101             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19102             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19103             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19104             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19105             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19106             today: "Today"
19107         }
19108     },
19109     
19110     modes: [
19111     {
19112         clsName: 'days',
19113         navFnc: 'Month',
19114         navStep: 1
19115     },
19116     {
19117         clsName: 'months',
19118         navFnc: 'FullYear',
19119         navStep: 1
19120     },
19121     {
19122         clsName: 'years',
19123         navFnc: 'FullYear',
19124         navStep: 10
19125     }]
19126 });
19127
19128 Roo.apply(Roo.bootstrap.DateField,  {
19129   
19130     template : {
19131         tag: 'div',
19132         cls: 'datepicker dropdown-menu roo-dynamic',
19133         cn: [
19134         {
19135             tag: 'div',
19136             cls: 'datepicker-days',
19137             cn: [
19138             {
19139                 tag: 'table',
19140                 cls: 'table-condensed',
19141                 cn:[
19142                 Roo.bootstrap.DateField.head,
19143                 {
19144                     tag: 'tbody'
19145                 },
19146                 Roo.bootstrap.DateField.footer
19147                 ]
19148             }
19149             ]
19150         },
19151         {
19152             tag: 'div',
19153             cls: 'datepicker-months',
19154             cn: [
19155             {
19156                 tag: 'table',
19157                 cls: 'table-condensed',
19158                 cn:[
19159                 Roo.bootstrap.DateField.head,
19160                 Roo.bootstrap.DateField.content,
19161                 Roo.bootstrap.DateField.footer
19162                 ]
19163             }
19164             ]
19165         },
19166         {
19167             tag: 'div',
19168             cls: 'datepicker-years',
19169             cn: [
19170             {
19171                 tag: 'table',
19172                 cls: 'table-condensed',
19173                 cn:[
19174                 Roo.bootstrap.DateField.head,
19175                 Roo.bootstrap.DateField.content,
19176                 Roo.bootstrap.DateField.footer
19177                 ]
19178             }
19179             ]
19180         }
19181         ]
19182     }
19183 });
19184
19185  
19186
19187  /*
19188  * - LGPL
19189  *
19190  * TimeField
19191  * 
19192  */
19193
19194 /**
19195  * @class Roo.bootstrap.TimeField
19196  * @extends Roo.bootstrap.Input
19197  * Bootstrap DateField class
19198  * 
19199  * 
19200  * @constructor
19201  * Create a new TimeField
19202  * @param {Object} config The config object
19203  */
19204
19205 Roo.bootstrap.TimeField = function(config){
19206     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19207     this.addEvents({
19208             /**
19209              * @event show
19210              * Fires when this field show.
19211              * @param {Roo.bootstrap.DateField} thisthis
19212              * @param {Mixed} date The date value
19213              */
19214             show : true,
19215             /**
19216              * @event show
19217              * Fires when this field hide.
19218              * @param {Roo.bootstrap.DateField} this
19219              * @param {Mixed} date The date value
19220              */
19221             hide : true,
19222             /**
19223              * @event select
19224              * Fires when select a date.
19225              * @param {Roo.bootstrap.DateField} this
19226              * @param {Mixed} date The date value
19227              */
19228             select : true
19229         });
19230 };
19231
19232 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19233     
19234     /**
19235      * @cfg {String} format
19236      * The default time format string which can be overriden for localization support.  The format must be
19237      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19238      */
19239     format : "H:i",
19240        
19241     onRender: function(ct, position)
19242     {
19243         
19244         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19245                 
19246         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19247         
19248         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19249         
19250         this.pop = this.picker().select('>.datepicker-time',true).first();
19251         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19252         
19253         this.picker().on('mousedown', this.onMousedown, this);
19254         this.picker().on('click', this.onClick, this);
19255         
19256         this.picker().addClass('datepicker-dropdown');
19257     
19258         this.fillTime();
19259         this.update();
19260             
19261         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19262         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19263         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19264         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19265         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19266         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19267
19268     },
19269     
19270     fireKey: function(e){
19271         if (!this.picker().isVisible()){
19272             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19273                 this.show();
19274             }
19275             return;
19276         }
19277
19278         e.preventDefault();
19279         
19280         switch(e.keyCode){
19281             case 27: // escape
19282                 this.hide();
19283                 break;
19284             case 37: // left
19285             case 39: // right
19286                 this.onTogglePeriod();
19287                 break;
19288             case 38: // up
19289                 this.onIncrementMinutes();
19290                 break;
19291             case 40: // down
19292                 this.onDecrementMinutes();
19293                 break;
19294             case 13: // enter
19295             case 9: // tab
19296                 this.setTime();
19297                 break;
19298         }
19299     },
19300     
19301     onClick: function(e) {
19302         e.stopPropagation();
19303         e.preventDefault();
19304     },
19305     
19306     picker : function()
19307     {
19308         return this.el.select('.datepicker', true).first();
19309     },
19310     
19311     fillTime: function()
19312     {    
19313         var time = this.pop.select('tbody', true).first();
19314         
19315         time.dom.innerHTML = '';
19316         
19317         time.createChild({
19318             tag: 'tr',
19319             cn: [
19320                 {
19321                     tag: 'td',
19322                     cn: [
19323                         {
19324                             tag: 'a',
19325                             href: '#',
19326                             cls: 'btn',
19327                             cn: [
19328                                 {
19329                                     tag: 'span',
19330                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19331                                 }
19332                             ]
19333                         } 
19334                     ]
19335                 },
19336                 {
19337                     tag: 'td',
19338                     cls: 'separator'
19339                 },
19340                 {
19341                     tag: 'td',
19342                     cn: [
19343                         {
19344                             tag: 'a',
19345                             href: '#',
19346                             cls: 'btn',
19347                             cn: [
19348                                 {
19349                                     tag: 'span',
19350                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19351                                 }
19352                             ]
19353                         }
19354                     ]
19355                 },
19356                 {
19357                     tag: 'td',
19358                     cls: 'separator'
19359                 }
19360             ]
19361         });
19362         
19363         time.createChild({
19364             tag: 'tr',
19365             cn: [
19366                 {
19367                     tag: 'td',
19368                     cn: [
19369                         {
19370                             tag: 'span',
19371                             cls: 'timepicker-hour',
19372                             html: '00'
19373                         }  
19374                     ]
19375                 },
19376                 {
19377                     tag: 'td',
19378                     cls: 'separator',
19379                     html: ':'
19380                 },
19381                 {
19382                     tag: 'td',
19383                     cn: [
19384                         {
19385                             tag: 'span',
19386                             cls: 'timepicker-minute',
19387                             html: '00'
19388                         }  
19389                     ]
19390                 },
19391                 {
19392                     tag: 'td',
19393                     cls: 'separator'
19394                 },
19395                 {
19396                     tag: 'td',
19397                     cn: [
19398                         {
19399                             tag: 'button',
19400                             type: 'button',
19401                             cls: 'btn btn-primary period',
19402                             html: 'AM'
19403                             
19404                         }
19405                     ]
19406                 }
19407             ]
19408         });
19409         
19410         time.createChild({
19411             tag: 'tr',
19412             cn: [
19413                 {
19414                     tag: 'td',
19415                     cn: [
19416                         {
19417                             tag: 'a',
19418                             href: '#',
19419                             cls: 'btn',
19420                             cn: [
19421                                 {
19422                                     tag: 'span',
19423                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19424                                 }
19425                             ]
19426                         }
19427                     ]
19428                 },
19429                 {
19430                     tag: 'td',
19431                     cls: 'separator'
19432                 },
19433                 {
19434                     tag: 'td',
19435                     cn: [
19436                         {
19437                             tag: 'a',
19438                             href: '#',
19439                             cls: 'btn',
19440                             cn: [
19441                                 {
19442                                     tag: 'span',
19443                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19444                                 }
19445                             ]
19446                         }
19447                     ]
19448                 },
19449                 {
19450                     tag: 'td',
19451                     cls: 'separator'
19452                 }
19453             ]
19454         });
19455         
19456     },
19457     
19458     update: function()
19459     {
19460         
19461         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19462         
19463         this.fill();
19464     },
19465     
19466     fill: function() 
19467     {
19468         var hours = this.time.getHours();
19469         var minutes = this.time.getMinutes();
19470         var period = 'AM';
19471         
19472         if(hours > 11){
19473             period = 'PM';
19474         }
19475         
19476         if(hours == 0){
19477             hours = 12;
19478         }
19479         
19480         
19481         if(hours > 12){
19482             hours = hours - 12;
19483         }
19484         
19485         if(hours < 10){
19486             hours = '0' + hours;
19487         }
19488         
19489         if(minutes < 10){
19490             minutes = '0' + minutes;
19491         }
19492         
19493         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19494         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19495         this.pop.select('button', true).first().dom.innerHTML = period;
19496         
19497     },
19498     
19499     place: function()
19500     {   
19501         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19502         
19503         var cls = ['bottom'];
19504         
19505         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19506             cls.pop();
19507             cls.push('top');
19508         }
19509         
19510         cls.push('right');
19511         
19512         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19513             cls.pop();
19514             cls.push('left');
19515         }
19516         
19517         this.picker().addClass(cls.join('-'));
19518         
19519         var _this = this;
19520         
19521         Roo.each(cls, function(c){
19522             if(c == 'bottom'){
19523                 _this.picker().setTop(_this.inputEl().getHeight());
19524                 return;
19525             }
19526             if(c == 'top'){
19527                 _this.picker().setTop(0 - _this.picker().getHeight());
19528                 return;
19529             }
19530             
19531             if(c == 'left'){
19532                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19533                 return;
19534             }
19535             if(c == 'right'){
19536                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19537                 return;
19538             }
19539         });
19540         
19541     },
19542   
19543     onFocus : function()
19544     {
19545         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19546         this.show();
19547     },
19548     
19549     onBlur : function()
19550     {
19551         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19552         this.hide();
19553     },
19554     
19555     show : function()
19556     {
19557         this.picker().show();
19558         this.pop.show();
19559         this.update();
19560         this.place();
19561         
19562         this.fireEvent('show', this, this.date);
19563     },
19564     
19565     hide : function()
19566     {
19567         this.picker().hide();
19568         this.pop.hide();
19569         
19570         this.fireEvent('hide', this, this.date);
19571     },
19572     
19573     setTime : function()
19574     {
19575         this.hide();
19576         this.setValue(this.time.format(this.format));
19577         
19578         this.fireEvent('select', this, this.date);
19579         
19580         
19581     },
19582     
19583     onMousedown: function(e){
19584         e.stopPropagation();
19585         e.preventDefault();
19586     },
19587     
19588     onIncrementHours: function()
19589     {
19590         Roo.log('onIncrementHours');
19591         this.time = this.time.add(Date.HOUR, 1);
19592         this.update();
19593         
19594     },
19595     
19596     onDecrementHours: function()
19597     {
19598         Roo.log('onDecrementHours');
19599         this.time = this.time.add(Date.HOUR, -1);
19600         this.update();
19601     },
19602     
19603     onIncrementMinutes: function()
19604     {
19605         Roo.log('onIncrementMinutes');
19606         this.time = this.time.add(Date.MINUTE, 1);
19607         this.update();
19608     },
19609     
19610     onDecrementMinutes: function()
19611     {
19612         Roo.log('onDecrementMinutes');
19613         this.time = this.time.add(Date.MINUTE, -1);
19614         this.update();
19615     },
19616     
19617     onTogglePeriod: function()
19618     {
19619         Roo.log('onTogglePeriod');
19620         this.time = this.time.add(Date.HOUR, 12);
19621         this.update();
19622     }
19623     
19624    
19625 });
19626
19627 Roo.apply(Roo.bootstrap.TimeField,  {
19628     
19629     content : {
19630         tag: 'tbody',
19631         cn: [
19632             {
19633                 tag: 'tr',
19634                 cn: [
19635                 {
19636                     tag: 'td',
19637                     colspan: '7'
19638                 }
19639                 ]
19640             }
19641         ]
19642     },
19643     
19644     footer : {
19645         tag: 'tfoot',
19646         cn: [
19647             {
19648                 tag: 'tr',
19649                 cn: [
19650                 {
19651                     tag: 'th',
19652                     colspan: '7',
19653                     cls: '',
19654                     cn: [
19655                         {
19656                             tag: 'button',
19657                             cls: 'btn btn-info ok',
19658                             html: 'OK'
19659                         }
19660                     ]
19661                 }
19662
19663                 ]
19664             }
19665         ]
19666     }
19667 });
19668
19669 Roo.apply(Roo.bootstrap.TimeField,  {
19670   
19671     template : {
19672         tag: 'div',
19673         cls: 'datepicker dropdown-menu',
19674         cn: [
19675             {
19676                 tag: 'div',
19677                 cls: 'datepicker-time',
19678                 cn: [
19679                 {
19680                     tag: 'table',
19681                     cls: 'table-condensed',
19682                     cn:[
19683                     Roo.bootstrap.TimeField.content,
19684                     Roo.bootstrap.TimeField.footer
19685                     ]
19686                 }
19687                 ]
19688             }
19689         ]
19690     }
19691 });
19692
19693  
19694
19695  /*
19696  * - LGPL
19697  *
19698  * MonthField
19699  * 
19700  */
19701
19702 /**
19703  * @class Roo.bootstrap.MonthField
19704  * @extends Roo.bootstrap.Input
19705  * Bootstrap MonthField class
19706  * 
19707  * @cfg {String} language default en
19708  * 
19709  * @constructor
19710  * Create a new MonthField
19711  * @param {Object} config The config object
19712  */
19713
19714 Roo.bootstrap.MonthField = function(config){
19715     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19716     
19717     this.addEvents({
19718         /**
19719          * @event show
19720          * Fires when this field show.
19721          * @param {Roo.bootstrap.MonthField} this
19722          * @param {Mixed} date The date value
19723          */
19724         show : true,
19725         /**
19726          * @event show
19727          * Fires when this field hide.
19728          * @param {Roo.bootstrap.MonthField} this
19729          * @param {Mixed} date The date value
19730          */
19731         hide : true,
19732         /**
19733          * @event select
19734          * Fires when select a date.
19735          * @param {Roo.bootstrap.MonthField} this
19736          * @param {String} oldvalue The old value
19737          * @param {String} newvalue The new value
19738          */
19739         select : true
19740     });
19741 };
19742
19743 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19744     
19745     onRender: function(ct, position)
19746     {
19747         
19748         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19749         
19750         this.language = this.language || 'en';
19751         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19752         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19753         
19754         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19755         this.isInline = false;
19756         this.isInput = true;
19757         this.component = this.el.select('.add-on', true).first() || false;
19758         this.component = (this.component && this.component.length === 0) ? false : this.component;
19759         this.hasInput = this.component && this.inputEL().length;
19760         
19761         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19762         
19763         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19764         
19765         this.picker().on('mousedown', this.onMousedown, this);
19766         this.picker().on('click', this.onClick, this);
19767         
19768         this.picker().addClass('datepicker-dropdown');
19769         
19770         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19771             v.setStyle('width', '189px');
19772         });
19773         
19774         this.fillMonths();
19775         
19776         this.update();
19777         
19778         if(this.isInline) {
19779             this.show();
19780         }
19781         
19782     },
19783     
19784     setValue: function(v, suppressEvent)
19785     {   
19786         var o = this.getValue();
19787         
19788         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19789         
19790         this.update();
19791
19792         if(suppressEvent !== true){
19793             this.fireEvent('select', this, o, v);
19794         }
19795         
19796     },
19797     
19798     getValue: function()
19799     {
19800         return this.value;
19801     },
19802     
19803     onClick: function(e) 
19804     {
19805         e.stopPropagation();
19806         e.preventDefault();
19807         
19808         var target = e.getTarget();
19809         
19810         if(target.nodeName.toLowerCase() === 'i'){
19811             target = Roo.get(target).dom.parentNode;
19812         }
19813         
19814         var nodeName = target.nodeName;
19815         var className = target.className;
19816         var html = target.innerHTML;
19817         
19818         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19819             return;
19820         }
19821         
19822         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19823         
19824         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19825         
19826         this.hide();
19827                         
19828     },
19829     
19830     picker : function()
19831     {
19832         return this.pickerEl;
19833     },
19834     
19835     fillMonths: function()
19836     {    
19837         var i = 0;
19838         var months = this.picker().select('>.datepicker-months td', true).first();
19839         
19840         months.dom.innerHTML = '';
19841         
19842         while (i < 12) {
19843             var month = {
19844                 tag: 'span',
19845                 cls: 'month',
19846                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19847             };
19848             
19849             months.createChild(month);
19850         }
19851         
19852     },
19853     
19854     update: function()
19855     {
19856         var _this = this;
19857         
19858         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19859             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19860         }
19861         
19862         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19863             e.removeClass('active');
19864             
19865             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19866                 e.addClass('active');
19867             }
19868         })
19869     },
19870     
19871     place: function()
19872     {
19873         if(this.isInline) {
19874             return;
19875         }
19876         
19877         this.picker().removeClass(['bottom', 'top']);
19878         
19879         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19880             /*
19881              * place to the top of element!
19882              *
19883              */
19884             
19885             this.picker().addClass('top');
19886             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19887             
19888             return;
19889         }
19890         
19891         this.picker().addClass('bottom');
19892         
19893         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19894     },
19895     
19896     onFocus : function()
19897     {
19898         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19899         this.show();
19900     },
19901     
19902     onBlur : function()
19903     {
19904         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19905         
19906         var d = this.inputEl().getValue();
19907         
19908         this.setValue(d);
19909                 
19910         this.hide();
19911     },
19912     
19913     show : function()
19914     {
19915         this.picker().show();
19916         this.picker().select('>.datepicker-months', true).first().show();
19917         this.update();
19918         this.place();
19919         
19920         this.fireEvent('show', this, this.date);
19921     },
19922     
19923     hide : function()
19924     {
19925         if(this.isInline) {
19926             return;
19927         }
19928         this.picker().hide();
19929         this.fireEvent('hide', this, this.date);
19930         
19931     },
19932     
19933     onMousedown: function(e)
19934     {
19935         e.stopPropagation();
19936         e.preventDefault();
19937     },
19938     
19939     keyup: function(e)
19940     {
19941         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19942         this.update();
19943     },
19944
19945     fireKey: function(e)
19946     {
19947         if (!this.picker().isVisible()){
19948             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19949                 this.show();
19950             }
19951             return;
19952         }
19953         
19954         var dir;
19955         
19956         switch(e.keyCode){
19957             case 27: // escape
19958                 this.hide();
19959                 e.preventDefault();
19960                 break;
19961             case 37: // left
19962             case 39: // right
19963                 dir = e.keyCode == 37 ? -1 : 1;
19964                 
19965                 this.vIndex = this.vIndex + dir;
19966                 
19967                 if(this.vIndex < 0){
19968                     this.vIndex = 0;
19969                 }
19970                 
19971                 if(this.vIndex > 11){
19972                     this.vIndex = 11;
19973                 }
19974                 
19975                 if(isNaN(this.vIndex)){
19976                     this.vIndex = 0;
19977                 }
19978                 
19979                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19980                 
19981                 break;
19982             case 38: // up
19983             case 40: // down
19984                 
19985                 dir = e.keyCode == 38 ? -1 : 1;
19986                 
19987                 this.vIndex = this.vIndex + dir * 4;
19988                 
19989                 if(this.vIndex < 0){
19990                     this.vIndex = 0;
19991                 }
19992                 
19993                 if(this.vIndex > 11){
19994                     this.vIndex = 11;
19995                 }
19996                 
19997                 if(isNaN(this.vIndex)){
19998                     this.vIndex = 0;
19999                 }
20000                 
20001                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20002                 break;
20003                 
20004             case 13: // enter
20005                 
20006                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20007                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20008                 }
20009                 
20010                 this.hide();
20011                 e.preventDefault();
20012                 break;
20013             case 9: // tab
20014                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20015                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20016                 }
20017                 this.hide();
20018                 break;
20019             case 16: // shift
20020             case 17: // ctrl
20021             case 18: // alt
20022                 break;
20023             default :
20024                 this.hide();
20025                 
20026         }
20027     },
20028     
20029     remove: function() 
20030     {
20031         this.picker().remove();
20032     }
20033    
20034 });
20035
20036 Roo.apply(Roo.bootstrap.MonthField,  {
20037     
20038     content : {
20039         tag: 'tbody',
20040         cn: [
20041         {
20042             tag: 'tr',
20043             cn: [
20044             {
20045                 tag: 'td',
20046                 colspan: '7'
20047             }
20048             ]
20049         }
20050         ]
20051     },
20052     
20053     dates:{
20054         en: {
20055             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20056             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20057         }
20058     }
20059 });
20060
20061 Roo.apply(Roo.bootstrap.MonthField,  {
20062   
20063     template : {
20064         tag: 'div',
20065         cls: 'datepicker dropdown-menu roo-dynamic',
20066         cn: [
20067             {
20068                 tag: 'div',
20069                 cls: 'datepicker-months',
20070                 cn: [
20071                 {
20072                     tag: 'table',
20073                     cls: 'table-condensed',
20074                     cn:[
20075                         Roo.bootstrap.DateField.content
20076                     ]
20077                 }
20078                 ]
20079             }
20080         ]
20081     }
20082 });
20083
20084  
20085
20086  
20087  /*
20088  * - LGPL
20089  *
20090  * CheckBox
20091  * 
20092  */
20093
20094 /**
20095  * @class Roo.bootstrap.CheckBox
20096  * @extends Roo.bootstrap.Input
20097  * Bootstrap CheckBox class
20098  * 
20099  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20100  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20101  * @cfg {String} boxLabel The text that appears beside the checkbox
20102  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20103  * @cfg {Boolean} checked initnal the element
20104  * @cfg {Boolean} inline inline the element (default false)
20105  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20106  * 
20107  * @constructor
20108  * Create a new CheckBox
20109  * @param {Object} config The config object
20110  */
20111
20112 Roo.bootstrap.CheckBox = function(config){
20113     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20114    
20115     this.addEvents({
20116         /**
20117         * @event check
20118         * Fires when the element is checked or unchecked.
20119         * @param {Roo.bootstrap.CheckBox} this This input
20120         * @param {Boolean} checked The new checked value
20121         */
20122        check : true
20123     });
20124     
20125 };
20126
20127 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20128   
20129     inputType: 'checkbox',
20130     inputValue: 1,
20131     valueOff: 0,
20132     boxLabel: false,
20133     checked: false,
20134     weight : false,
20135     inline: false,
20136     
20137     getAutoCreate : function()
20138     {
20139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20140         
20141         var id = Roo.id();
20142         
20143         var cfg = {};
20144         
20145         cfg.cls = 'form-group ' + this.inputType; //input-group
20146         
20147         if(this.inline){
20148             cfg.cls += ' ' + this.inputType + '-inline';
20149         }
20150         
20151         var input =  {
20152             tag: 'input',
20153             id : id,
20154             type : this.inputType,
20155             value : this.inputValue,
20156             cls : 'roo-' + this.inputType, //'form-box',
20157             placeholder : this.placeholder || ''
20158             
20159         };
20160         
20161         if(this.inputType != 'radio'){
20162             var hidden =  {
20163                 tag: 'input',
20164                 type : 'hidden',
20165                 cls : 'roo-hidden-value',
20166                 value : this.checked ? this.valueOff : this.inputValue
20167             };
20168         }
20169         
20170             
20171         if (this.weight) { // Validity check?
20172             cfg.cls += " " + this.inputType + "-" + this.weight;
20173         }
20174         
20175         if (this.disabled) {
20176             input.disabled=true;
20177         }
20178         
20179         if(this.checked){
20180             input.checked = this.checked;
20181             
20182         }
20183         
20184         
20185         if (this.name) {
20186             
20187             input.name = this.name;
20188             
20189             if(this.inputType != 'radio'){
20190                 hidden.name = this.name;
20191                 input.name = '_hidden_' + this.name;
20192             }
20193         }
20194         
20195         if (this.size) {
20196             input.cls += ' input-' + this.size;
20197         }
20198         
20199         var settings=this;
20200         
20201         ['xs','sm','md','lg'].map(function(size){
20202             if (settings[size]) {
20203                 cfg.cls += ' col-' + size + '-' + settings[size];
20204             }
20205         });
20206         
20207         var inputblock = input;
20208          
20209         if (this.before || this.after) {
20210             
20211             inputblock = {
20212                 cls : 'input-group',
20213                 cn :  [] 
20214             };
20215             
20216             if (this.before) {
20217                 inputblock.cn.push({
20218                     tag :'span',
20219                     cls : 'input-group-addon',
20220                     html : this.before
20221                 });
20222             }
20223             
20224             inputblock.cn.push(input);
20225             
20226             if(this.inputType != 'radio'){
20227                 inputblock.cn.push(hidden);
20228             }
20229             
20230             if (this.after) {
20231                 inputblock.cn.push({
20232                     tag :'span',
20233                     cls : 'input-group-addon',
20234                     html : this.after
20235                 });
20236             }
20237             
20238         }
20239         
20240         if (align ==='left' && this.fieldLabel.length) {
20241 //                Roo.log("left and has label");
20242             cfg.cn = [
20243                 {
20244                     tag: 'label',
20245                     'for' :  id,
20246                     cls : 'control-label',
20247                     html : this.fieldLabel
20248
20249                 },
20250                 {
20251                     cls : "", 
20252                     cn: [
20253                         inputblock
20254                     ]
20255                 }
20256             ];
20257             
20258             if(this.labelWidth > 12){
20259                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20260             }
20261             
20262             if(this.labelWidth < 13 && this.labelmd == 0){
20263                 this.labelmd = this.labelWidth;
20264             }
20265             
20266             if(this.labellg > 0){
20267                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20268                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20269             }
20270             
20271             if(this.labelmd > 0){
20272                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20273                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20274             }
20275             
20276             if(this.labelsm > 0){
20277                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20278                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20279             }
20280             
20281             if(this.labelxs > 0){
20282                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20283                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20284             }
20285             
20286         } else if ( this.fieldLabel.length) {
20287 //                Roo.log(" label");
20288                 cfg.cn = [
20289                    
20290                     {
20291                         tag: this.boxLabel ? 'span' : 'label',
20292                         'for': id,
20293                         cls: 'control-label box-input-label',
20294                         //cls : 'input-group-addon',
20295                         html : this.fieldLabel
20296                         
20297                     },
20298                     
20299                     inputblock
20300                     
20301                 ];
20302
20303         } else {
20304             
20305 //                Roo.log(" no label && no align");
20306                 cfg.cn = [  inputblock ] ;
20307                 
20308                 
20309         }
20310         
20311         if(this.boxLabel){
20312              var boxLabelCfg = {
20313                 tag: 'label',
20314                 //'for': id, // box label is handled by onclick - so no for...
20315                 cls: 'box-label',
20316                 html: this.boxLabel
20317             };
20318             
20319             if(this.tooltip){
20320                 boxLabelCfg.tooltip = this.tooltip;
20321             }
20322              
20323             cfg.cn.push(boxLabelCfg);
20324         }
20325         
20326         if(this.inputType != 'radio'){
20327             cfg.cn.push(hidden);
20328         }
20329         
20330         return cfg;
20331         
20332     },
20333     
20334     /**
20335      * return the real input element.
20336      */
20337     inputEl: function ()
20338     {
20339         return this.el.select('input.roo-' + this.inputType,true).first();
20340     },
20341     hiddenEl: function ()
20342     {
20343         return this.el.select('input.roo-hidden-value',true).first();
20344     },
20345     
20346     labelEl: function()
20347     {
20348         return this.el.select('label.control-label',true).first();
20349     },
20350     /* depricated... */
20351     
20352     label: function()
20353     {
20354         return this.labelEl();
20355     },
20356     
20357     boxLabelEl: function()
20358     {
20359         return this.el.select('label.box-label',true).first();
20360     },
20361     
20362     initEvents : function()
20363     {
20364 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20365         
20366         this.inputEl().on('click', this.onClick,  this);
20367         
20368         if (this.boxLabel) { 
20369             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20370         }
20371         
20372         this.startValue = this.getValue();
20373         
20374         if(this.groupId){
20375             Roo.bootstrap.CheckBox.register(this);
20376         }
20377     },
20378     
20379     onClick : function()
20380     {   
20381         this.setChecked(!this.checked);
20382     },
20383     
20384     setChecked : function(state,suppressEvent)
20385     {
20386         this.startValue = this.getValue();
20387
20388         if(this.inputType == 'radio'){
20389             
20390             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20391                 e.dom.checked = false;
20392             });
20393             
20394             this.inputEl().dom.checked = true;
20395             
20396             this.inputEl().dom.value = this.inputValue;
20397             
20398             if(suppressEvent !== true){
20399                 this.fireEvent('check', this, true);
20400             }
20401             
20402             this.validate();
20403             
20404             return;
20405         }
20406         
20407         this.checked = state;
20408         
20409         this.inputEl().dom.checked = state;
20410         
20411         
20412         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20413         
20414         if(suppressEvent !== true){
20415             this.fireEvent('check', this, state);
20416         }
20417         
20418         this.validate();
20419     },
20420     
20421     getValue : function()
20422     {
20423         if(this.inputType == 'radio'){
20424             return this.getGroupValue();
20425         }
20426         
20427         return this.hiddenEl().dom.value;
20428         
20429     },
20430     
20431     getGroupValue : function()
20432     {
20433         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20434             return '';
20435         }
20436         
20437         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20438     },
20439     
20440     setValue : function(v,suppressEvent)
20441     {
20442         if(this.inputType == 'radio'){
20443             this.setGroupValue(v, suppressEvent);
20444             return;
20445         }
20446         
20447         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20448         
20449         this.validate();
20450     },
20451     
20452     setGroupValue : function(v, suppressEvent)
20453     {
20454         this.startValue = this.getValue();
20455         
20456         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20457             e.dom.checked = false;
20458             
20459             if(e.dom.value == v){
20460                 e.dom.checked = true;
20461             }
20462         });
20463         
20464         if(suppressEvent !== true){
20465             this.fireEvent('check', this, true);
20466         }
20467
20468         this.validate();
20469         
20470         return;
20471     },
20472     
20473     validate : function()
20474     {
20475         if(
20476                 this.disabled || 
20477                 (this.inputType == 'radio' && this.validateRadio()) ||
20478                 (this.inputType == 'checkbox' && this.validateCheckbox())
20479         ){
20480             this.markValid();
20481             return true;
20482         }
20483         
20484         this.markInvalid();
20485         return false;
20486     },
20487     
20488     validateRadio : function()
20489     {
20490         if(this.allowBlank){
20491             return true;
20492         }
20493         
20494         var valid = false;
20495         
20496         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20497             if(!e.dom.checked){
20498                 return;
20499             }
20500             
20501             valid = true;
20502             
20503             return false;
20504         });
20505         
20506         return valid;
20507     },
20508     
20509     validateCheckbox : function()
20510     {
20511         if(!this.groupId){
20512             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20513             //return (this.getValue() == this.inputValue) ? true : false;
20514         }
20515         
20516         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20517         
20518         if(!group){
20519             return false;
20520         }
20521         
20522         var r = false;
20523         
20524         for(var i in group){
20525             if(r){
20526                 break;
20527             }
20528             
20529             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20530         }
20531         
20532         return r;
20533     },
20534     
20535     /**
20536      * Mark this field as valid
20537      */
20538     markValid : function()
20539     {
20540         var _this = this;
20541         
20542         this.fireEvent('valid', this);
20543         
20544         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20545         
20546         if(this.groupId){
20547             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20548         }
20549         
20550         if(label){
20551             label.markValid();
20552         }
20553
20554         if(this.inputType == 'radio'){
20555             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20556                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20557                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20558             });
20559             
20560             return;
20561         }
20562
20563         if(!this.groupId){
20564             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20565             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20566             return;
20567         }
20568         
20569         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20570         
20571         if(!group){
20572             return;
20573         }
20574         
20575         for(var i in group){
20576             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20577             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20578         }
20579     },
20580     
20581      /**
20582      * Mark this field as invalid
20583      * @param {String} msg The validation message
20584      */
20585     markInvalid : function(msg)
20586     {
20587         if(this.allowBlank){
20588             return;
20589         }
20590         
20591         var _this = this;
20592         
20593         this.fireEvent('invalid', this, msg);
20594         
20595         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20596         
20597         if(this.groupId){
20598             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20599         }
20600         
20601         if(label){
20602             label.markInvalid();
20603         }
20604             
20605         if(this.inputType == 'radio'){
20606             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20607                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20608                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20609             });
20610             
20611             return;
20612         }
20613         
20614         if(!this.groupId){
20615             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20616             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20617             return;
20618         }
20619         
20620         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20621         
20622         if(!group){
20623             return;
20624         }
20625         
20626         for(var i in group){
20627             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20628             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20629         }
20630         
20631     },
20632     
20633     clearInvalid : function()
20634     {
20635         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20636         
20637         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20638         
20639         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20640         
20641         if (label) {
20642             label.iconEl.removeClass(label.validClass);
20643             label.iconEl.removeClass(label.invalidClass);
20644         }
20645     },
20646     
20647     disable : function()
20648     {
20649         if(this.inputType != 'radio'){
20650             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20651             return;
20652         }
20653         
20654         var _this = this;
20655         
20656         if(this.rendered){
20657             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20658                 _this.getActionEl().addClass(this.disabledClass);
20659                 e.dom.disabled = true;
20660             });
20661         }
20662         
20663         this.disabled = true;
20664         this.fireEvent("disable", this);
20665         return this;
20666     },
20667
20668     enable : function()
20669     {
20670         if(this.inputType != 'radio'){
20671             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20672             return;
20673         }
20674         
20675         var _this = this;
20676         
20677         if(this.rendered){
20678             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20679                 _this.getActionEl().removeClass(this.disabledClass);
20680                 e.dom.disabled = false;
20681             });
20682         }
20683         
20684         this.disabled = false;
20685         this.fireEvent("enable", this);
20686         return this;
20687     }
20688
20689 });
20690
20691 Roo.apply(Roo.bootstrap.CheckBox, {
20692     
20693     groups: {},
20694     
20695      /**
20696     * register a CheckBox Group
20697     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20698     */
20699     register : function(checkbox)
20700     {
20701         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20702             this.groups[checkbox.groupId] = {};
20703         }
20704         
20705         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20706             return;
20707         }
20708         
20709         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20710         
20711     },
20712     /**
20713     * fetch a CheckBox Group based on the group ID
20714     * @param {string} the group ID
20715     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20716     */
20717     get: function(groupId) {
20718         if (typeof(this.groups[groupId]) == 'undefined') {
20719             return false;
20720         }
20721         
20722         return this.groups[groupId] ;
20723     }
20724     
20725     
20726 });
20727 /*
20728  * - LGPL
20729  *
20730  * RadioItem
20731  * 
20732  */
20733
20734 /**
20735  * @class Roo.bootstrap.Radio
20736  * @extends Roo.bootstrap.Component
20737  * Bootstrap Radio class
20738  * @cfg {String} boxLabel - the label associated
20739  * @cfg {String} value - the value of radio
20740  * 
20741  * @constructor
20742  * Create a new Radio
20743  * @param {Object} config The config object
20744  */
20745 Roo.bootstrap.Radio = function(config){
20746     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20747     
20748 };
20749
20750 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20751     
20752     boxLabel : '',
20753     
20754     value : '',
20755     
20756     getAutoCreate : function()
20757     {
20758         var cfg = {
20759             tag : 'div',
20760             cls : 'form-group radio',
20761             cn : [
20762                 {
20763                     tag : 'label',
20764                     cls : 'box-label',
20765                     html : this.boxLabel
20766                 }
20767             ]
20768         };
20769         
20770         return cfg;
20771     },
20772     
20773     initEvents : function() 
20774     {
20775         this.parent().register(this);
20776         
20777         this.el.on('click', this.onClick, this);
20778         
20779     },
20780     
20781     onClick : function()
20782     {
20783         this.setChecked(true);
20784     },
20785     
20786     setChecked : function(state, suppressEvent)
20787     {
20788         this.parent().setValue(this.value, suppressEvent);
20789         
20790     },
20791     
20792     setBoxLabel : function(v)
20793     {
20794         this.boxLabel = v;
20795         
20796         if(this.rendered){
20797             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20798         }
20799     }
20800     
20801 });
20802  
20803
20804  /*
20805  * - LGPL
20806  *
20807  * Input
20808  * 
20809  */
20810
20811 /**
20812  * @class Roo.bootstrap.SecurePass
20813  * @extends Roo.bootstrap.Input
20814  * Bootstrap SecurePass class
20815  *
20816  * 
20817  * @constructor
20818  * Create a new SecurePass
20819  * @param {Object} config The config object
20820  */
20821  
20822 Roo.bootstrap.SecurePass = function (config) {
20823     // these go here, so the translation tool can replace them..
20824     this.errors = {
20825         PwdEmpty: "Please type a password, and then retype it to confirm.",
20826         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20827         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20828         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20829         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20830         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20831         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20832         TooWeak: "Your password is Too Weak."
20833     },
20834     this.meterLabel = "Password strength:";
20835     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20836     this.meterClass = [
20837         "roo-password-meter-tooweak", 
20838         "roo-password-meter-weak", 
20839         "roo-password-meter-medium", 
20840         "roo-password-meter-strong", 
20841         "roo-password-meter-grey"
20842     ];
20843     
20844     this.errors = {};
20845     
20846     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20847 }
20848
20849 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20850     /**
20851      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20852      * {
20853      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20854      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20855      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20856      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20857      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20858      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20859      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20860      * })
20861      */
20862     // private
20863     
20864     meterWidth: 300,
20865     errorMsg :'',    
20866     errors: false,
20867     imageRoot: '/',
20868     /**
20869      * @cfg {String/Object} Label for the strength meter (defaults to
20870      * 'Password strength:')
20871      */
20872     // private
20873     meterLabel: '',
20874     /**
20875      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20876      * ['Weak', 'Medium', 'Strong'])
20877      */
20878     // private    
20879     pwdStrengths: false,    
20880     // private
20881     strength: 0,
20882     // private
20883     _lastPwd: null,
20884     // private
20885     kCapitalLetter: 0,
20886     kSmallLetter: 1,
20887     kDigit: 2,
20888     kPunctuation: 3,
20889     
20890     insecure: false,
20891     // private
20892     initEvents: function ()
20893     {
20894         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20895
20896         if (this.el.is('input[type=password]') && Roo.isSafari) {
20897             this.el.on('keydown', this.SafariOnKeyDown, this);
20898         }
20899
20900         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20901     },
20902     // private
20903     onRender: function (ct, position)
20904     {
20905         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20906         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20907         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20908
20909         this.trigger.createChild({
20910                    cn: [
20911                     {
20912                     //id: 'PwdMeter',
20913                     tag: 'div',
20914                     cls: 'roo-password-meter-grey col-xs-12',
20915                     style: {
20916                         //width: 0,
20917                         //width: this.meterWidth + 'px'                                                
20918                         }
20919                     },
20920                     {                            
20921                          cls: 'roo-password-meter-text'                          
20922                     }
20923                 ]            
20924         });
20925
20926          
20927         if (this.hideTrigger) {
20928             this.trigger.setDisplayed(false);
20929         }
20930         this.setSize(this.width || '', this.height || '');
20931     },
20932     // private
20933     onDestroy: function ()
20934     {
20935         if (this.trigger) {
20936             this.trigger.removeAllListeners();
20937             this.trigger.remove();
20938         }
20939         if (this.wrap) {
20940             this.wrap.remove();
20941         }
20942         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20943     },
20944     // private
20945     checkStrength: function ()
20946     {
20947         var pwd = this.inputEl().getValue();
20948         if (pwd == this._lastPwd) {
20949             return;
20950         }
20951
20952         var strength;
20953         if (this.ClientSideStrongPassword(pwd)) {
20954             strength = 3;
20955         } else if (this.ClientSideMediumPassword(pwd)) {
20956             strength = 2;
20957         } else if (this.ClientSideWeakPassword(pwd)) {
20958             strength = 1;
20959         } else {
20960             strength = 0;
20961         }
20962         
20963         Roo.log('strength1: ' + strength);
20964         
20965         //var pm = this.trigger.child('div/div/div').dom;
20966         var pm = this.trigger.child('div/div');
20967         pm.removeClass(this.meterClass);
20968         pm.addClass(this.meterClass[strength]);
20969                 
20970         
20971         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20972                 
20973         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20974         
20975         this._lastPwd = pwd;
20976     },
20977     reset: function ()
20978     {
20979         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20980         
20981         this._lastPwd = '';
20982         
20983         var pm = this.trigger.child('div/div');
20984         pm.removeClass(this.meterClass);
20985         pm.addClass('roo-password-meter-grey');        
20986         
20987         
20988         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20989         
20990         pt.innerHTML = '';
20991         this.inputEl().dom.type='password';
20992     },
20993     // private
20994     validateValue: function (value)
20995     {
20996         
20997         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20998             return false;
20999         }
21000         if (value.length == 0) {
21001             if (this.allowBlank) {
21002                 this.clearInvalid();
21003                 return true;
21004             }
21005
21006             this.markInvalid(this.errors.PwdEmpty);
21007             this.errorMsg = this.errors.PwdEmpty;
21008             return false;
21009         }
21010         
21011         if(this.insecure){
21012             return true;
21013         }
21014         
21015         if ('[\x21-\x7e]*'.match(value)) {
21016             this.markInvalid(this.errors.PwdBadChar);
21017             this.errorMsg = this.errors.PwdBadChar;
21018             return false;
21019         }
21020         if (value.length < 6) {
21021             this.markInvalid(this.errors.PwdShort);
21022             this.errorMsg = this.errors.PwdShort;
21023             return false;
21024         }
21025         if (value.length > 16) {
21026             this.markInvalid(this.errors.PwdLong);
21027             this.errorMsg = this.errors.PwdLong;
21028             return false;
21029         }
21030         var strength;
21031         if (this.ClientSideStrongPassword(value)) {
21032             strength = 3;
21033         } else if (this.ClientSideMediumPassword(value)) {
21034             strength = 2;
21035         } else if (this.ClientSideWeakPassword(value)) {
21036             strength = 1;
21037         } else {
21038             strength = 0;
21039         }
21040
21041         
21042         if (strength < 2) {
21043             //this.markInvalid(this.errors.TooWeak);
21044             this.errorMsg = this.errors.TooWeak;
21045             //return false;
21046         }
21047         
21048         
21049         console.log('strength2: ' + strength);
21050         
21051         //var pm = this.trigger.child('div/div/div').dom;
21052         
21053         var pm = this.trigger.child('div/div');
21054         pm.removeClass(this.meterClass);
21055         pm.addClass(this.meterClass[strength]);
21056                 
21057         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21058                 
21059         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21060         
21061         this.errorMsg = ''; 
21062         return true;
21063     },
21064     // private
21065     CharacterSetChecks: function (type)
21066     {
21067         this.type = type;
21068         this.fResult = false;
21069     },
21070     // private
21071     isctype: function (character, type)
21072     {
21073         switch (type) {  
21074             case this.kCapitalLetter:
21075                 if (character >= 'A' && character <= 'Z') {
21076                     return true;
21077                 }
21078                 break;
21079             
21080             case this.kSmallLetter:
21081                 if (character >= 'a' && character <= 'z') {
21082                     return true;
21083                 }
21084                 break;
21085             
21086             case this.kDigit:
21087                 if (character >= '0' && character <= '9') {
21088                     return true;
21089                 }
21090                 break;
21091             
21092             case this.kPunctuation:
21093                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21094                     return true;
21095                 }
21096                 break;
21097             
21098             default:
21099                 return false;
21100         }
21101
21102     },
21103     // private
21104     IsLongEnough: function (pwd, size)
21105     {
21106         return !(pwd == null || isNaN(size) || pwd.length < size);
21107     },
21108     // private
21109     SpansEnoughCharacterSets: function (word, nb)
21110     {
21111         if (!this.IsLongEnough(word, nb))
21112         {
21113             return false;
21114         }
21115
21116         var characterSetChecks = new Array(
21117             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21118             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21119         );
21120         
21121         for (var index = 0; index < word.length; ++index) {
21122             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21123                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21124                     characterSetChecks[nCharSet].fResult = true;
21125                     break;
21126                 }
21127             }
21128         }
21129
21130         var nCharSets = 0;
21131         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21132             if (characterSetChecks[nCharSet].fResult) {
21133                 ++nCharSets;
21134             }
21135         }
21136
21137         if (nCharSets < nb) {
21138             return false;
21139         }
21140         return true;
21141     },
21142     // private
21143     ClientSideStrongPassword: function (pwd)
21144     {
21145         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21146     },
21147     // private
21148     ClientSideMediumPassword: function (pwd)
21149     {
21150         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21151     },
21152     // private
21153     ClientSideWeakPassword: function (pwd)
21154     {
21155         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21156     }
21157           
21158 })//<script type="text/javascript">
21159
21160 /*
21161  * Based  Ext JS Library 1.1.1
21162  * Copyright(c) 2006-2007, Ext JS, LLC.
21163  * LGPL
21164  *
21165  */
21166  
21167 /**
21168  * @class Roo.HtmlEditorCore
21169  * @extends Roo.Component
21170  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21171  *
21172  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21173  */
21174
21175 Roo.HtmlEditorCore = function(config){
21176     
21177     
21178     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21179     
21180     
21181     this.addEvents({
21182         /**
21183          * @event initialize
21184          * Fires when the editor is fully initialized (including the iframe)
21185          * @param {Roo.HtmlEditorCore} this
21186          */
21187         initialize: true,
21188         /**
21189          * @event activate
21190          * Fires when the editor is first receives the focus. Any insertion must wait
21191          * until after this event.
21192          * @param {Roo.HtmlEditorCore} this
21193          */
21194         activate: true,
21195          /**
21196          * @event beforesync
21197          * Fires before the textarea is updated with content from the editor iframe. Return false
21198          * to cancel the sync.
21199          * @param {Roo.HtmlEditorCore} this
21200          * @param {String} html
21201          */
21202         beforesync: true,
21203          /**
21204          * @event beforepush
21205          * Fires before the iframe editor is updated with content from the textarea. Return false
21206          * to cancel the push.
21207          * @param {Roo.HtmlEditorCore} this
21208          * @param {String} html
21209          */
21210         beforepush: true,
21211          /**
21212          * @event sync
21213          * Fires when the textarea is updated with content from the editor iframe.
21214          * @param {Roo.HtmlEditorCore} this
21215          * @param {String} html
21216          */
21217         sync: true,
21218          /**
21219          * @event push
21220          * Fires when the iframe editor is updated with content from the textarea.
21221          * @param {Roo.HtmlEditorCore} this
21222          * @param {String} html
21223          */
21224         push: true,
21225         
21226         /**
21227          * @event editorevent
21228          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21229          * @param {Roo.HtmlEditorCore} this
21230          */
21231         editorevent: true
21232         
21233     });
21234     
21235     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21236     
21237     // defaults : white / black...
21238     this.applyBlacklists();
21239     
21240     
21241     
21242 };
21243
21244
21245 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21246
21247
21248      /**
21249      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21250      */
21251     
21252     owner : false,
21253     
21254      /**
21255      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21256      *                        Roo.resizable.
21257      */
21258     resizable : false,
21259      /**
21260      * @cfg {Number} height (in pixels)
21261      */   
21262     height: 300,
21263    /**
21264      * @cfg {Number} width (in pixels)
21265      */   
21266     width: 500,
21267     
21268     /**
21269      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21270      * 
21271      */
21272     stylesheets: false,
21273     
21274     // id of frame..
21275     frameId: false,
21276     
21277     // private properties
21278     validationEvent : false,
21279     deferHeight: true,
21280     initialized : false,
21281     activated : false,
21282     sourceEditMode : false,
21283     onFocus : Roo.emptyFn,
21284     iframePad:3,
21285     hideMode:'offsets',
21286     
21287     clearUp: true,
21288     
21289     // blacklist + whitelisted elements..
21290     black: false,
21291     white: false,
21292      
21293     
21294
21295     /**
21296      * Protected method that will not generally be called directly. It
21297      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21298      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21299      */
21300     getDocMarkup : function(){
21301         // body styles..
21302         var st = '';
21303         
21304         // inherit styels from page...?? 
21305         if (this.stylesheets === false) {
21306             
21307             Roo.get(document.head).select('style').each(function(node) {
21308                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21309             });
21310             
21311             Roo.get(document.head).select('link').each(function(node) { 
21312                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21313             });
21314             
21315         } else if (!this.stylesheets.length) {
21316                 // simple..
21317                 st = '<style type="text/css">' +
21318                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21319                    '</style>';
21320         } else { 
21321             st = '<style type="text/css">' +
21322                     this.stylesheets +
21323                 '</style>';
21324         }
21325         
21326         st +=  '<style type="text/css">' +
21327             'IMG { cursor: pointer } ' +
21328         '</style>';
21329
21330         var cls = 'roo-htmleditor-body';
21331         
21332         if(this.bodyCls.length){
21333             cls += ' ' + this.bodyCls;
21334         }
21335         
21336         return '<html><head>' + st  +
21337             //<style type="text/css">' +
21338             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21339             //'</style>' +
21340             ' </head><body class="' +  cls + '"></body></html>';
21341     },
21342
21343     // private
21344     onRender : function(ct, position)
21345     {
21346         var _t = this;
21347         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21348         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21349         
21350         
21351         this.el.dom.style.border = '0 none';
21352         this.el.dom.setAttribute('tabIndex', -1);
21353         this.el.addClass('x-hidden hide');
21354         
21355         
21356         
21357         if(Roo.isIE){ // fix IE 1px bogus margin
21358             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21359         }
21360        
21361         
21362         this.frameId = Roo.id();
21363         
21364          
21365         
21366         var iframe = this.owner.wrap.createChild({
21367             tag: 'iframe',
21368             cls: 'form-control', // bootstrap..
21369             id: this.frameId,
21370             name: this.frameId,
21371             frameBorder : 'no',
21372             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21373         }, this.el
21374         );
21375         
21376         
21377         this.iframe = iframe.dom;
21378
21379          this.assignDocWin();
21380         
21381         this.doc.designMode = 'on';
21382        
21383         this.doc.open();
21384         this.doc.write(this.getDocMarkup());
21385         this.doc.close();
21386
21387         
21388         var task = { // must defer to wait for browser to be ready
21389             run : function(){
21390                 //console.log("run task?" + this.doc.readyState);
21391                 this.assignDocWin();
21392                 if(this.doc.body || this.doc.readyState == 'complete'){
21393                     try {
21394                         this.doc.designMode="on";
21395                     } catch (e) {
21396                         return;
21397                     }
21398                     Roo.TaskMgr.stop(task);
21399                     this.initEditor.defer(10, this);
21400                 }
21401             },
21402             interval : 10,
21403             duration: 10000,
21404             scope: this
21405         };
21406         Roo.TaskMgr.start(task);
21407
21408     },
21409
21410     // private
21411     onResize : function(w, h)
21412     {
21413          Roo.log('resize: ' +w + ',' + h );
21414         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21415         if(!this.iframe){
21416             return;
21417         }
21418         if(typeof w == 'number'){
21419             
21420             this.iframe.style.width = w + 'px';
21421         }
21422         if(typeof h == 'number'){
21423             
21424             this.iframe.style.height = h + 'px';
21425             if(this.doc){
21426                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21427             }
21428         }
21429         
21430     },
21431
21432     /**
21433      * Toggles the editor between standard and source edit mode.
21434      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21435      */
21436     toggleSourceEdit : function(sourceEditMode){
21437         
21438         this.sourceEditMode = sourceEditMode === true;
21439         
21440         if(this.sourceEditMode){
21441  
21442             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21443             
21444         }else{
21445             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21446             //this.iframe.className = '';
21447             this.deferFocus();
21448         }
21449         //this.setSize(this.owner.wrap.getSize());
21450         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21451     },
21452
21453     
21454   
21455
21456     /**
21457      * Protected method that will not generally be called directly. If you need/want
21458      * custom HTML cleanup, this is the method you should override.
21459      * @param {String} html The HTML to be cleaned
21460      * return {String} The cleaned HTML
21461      */
21462     cleanHtml : function(html){
21463         html = String(html);
21464         if(html.length > 5){
21465             if(Roo.isSafari){ // strip safari nonsense
21466                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21467             }
21468         }
21469         if(html == '&nbsp;'){
21470             html = '';
21471         }
21472         return html;
21473     },
21474
21475     /**
21476      * HTML Editor -> Textarea
21477      * Protected method that will not generally be called directly. Syncs the contents
21478      * of the editor iframe with the textarea.
21479      */
21480     syncValue : function(){
21481         if(this.initialized){
21482             var bd = (this.doc.body || this.doc.documentElement);
21483             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21484             var html = bd.innerHTML;
21485             if(Roo.isSafari){
21486                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21487                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21488                 if(m && m[1]){
21489                     html = '<div style="'+m[0]+'">' + html + '</div>';
21490                 }
21491             }
21492             html = this.cleanHtml(html);
21493             // fix up the special chars.. normaly like back quotes in word...
21494             // however we do not want to do this with chinese..
21495             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21496                 var cc = b.charCodeAt();
21497                 if (
21498                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21499                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21500                     (cc >= 0xf900 && cc < 0xfb00 )
21501                 ) {
21502                         return b;
21503                 }
21504                 return "&#"+cc+";" 
21505             });
21506             if(this.owner.fireEvent('beforesync', this, html) !== false){
21507                 this.el.dom.value = html;
21508                 this.owner.fireEvent('sync', this, html);
21509             }
21510         }
21511     },
21512
21513     /**
21514      * Protected method that will not generally be called directly. Pushes the value of the textarea
21515      * into the iframe editor.
21516      */
21517     pushValue : function(){
21518         if(this.initialized){
21519             var v = this.el.dom.value.trim();
21520             
21521 //            if(v.length < 1){
21522 //                v = '&#160;';
21523 //            }
21524             
21525             if(this.owner.fireEvent('beforepush', this, v) !== false){
21526                 var d = (this.doc.body || this.doc.documentElement);
21527                 d.innerHTML = v;
21528                 this.cleanUpPaste();
21529                 this.el.dom.value = d.innerHTML;
21530                 this.owner.fireEvent('push', this, v);
21531             }
21532         }
21533     },
21534
21535     // private
21536     deferFocus : function(){
21537         this.focus.defer(10, this);
21538     },
21539
21540     // doc'ed in Field
21541     focus : function(){
21542         if(this.win && !this.sourceEditMode){
21543             this.win.focus();
21544         }else{
21545             this.el.focus();
21546         }
21547     },
21548     
21549     assignDocWin: function()
21550     {
21551         var iframe = this.iframe;
21552         
21553          if(Roo.isIE){
21554             this.doc = iframe.contentWindow.document;
21555             this.win = iframe.contentWindow;
21556         } else {
21557 //            if (!Roo.get(this.frameId)) {
21558 //                return;
21559 //            }
21560 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21561 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21562             
21563             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21564                 return;
21565             }
21566             
21567             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21568             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21569         }
21570     },
21571     
21572     // private
21573     initEditor : function(){
21574         //console.log("INIT EDITOR");
21575         this.assignDocWin();
21576         
21577         
21578         
21579         this.doc.designMode="on";
21580         this.doc.open();
21581         this.doc.write(this.getDocMarkup());
21582         this.doc.close();
21583         
21584         var dbody = (this.doc.body || this.doc.documentElement);
21585         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21586         // this copies styles from the containing element into thsi one..
21587         // not sure why we need all of this..
21588         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21589         
21590         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21591         //ss['background-attachment'] = 'fixed'; // w3c
21592         dbody.bgProperties = 'fixed'; // ie
21593         //Roo.DomHelper.applyStyles(dbody, ss);
21594         Roo.EventManager.on(this.doc, {
21595             //'mousedown': this.onEditorEvent,
21596             'mouseup': this.onEditorEvent,
21597             'dblclick': this.onEditorEvent,
21598             'click': this.onEditorEvent,
21599             'keyup': this.onEditorEvent,
21600             buffer:100,
21601             scope: this
21602         });
21603         if(Roo.isGecko){
21604             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21605         }
21606         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21607             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21608         }
21609         this.initialized = true;
21610
21611         this.owner.fireEvent('initialize', this);
21612         this.pushValue();
21613     },
21614
21615     // private
21616     onDestroy : function(){
21617         
21618         
21619         
21620         if(this.rendered){
21621             
21622             //for (var i =0; i < this.toolbars.length;i++) {
21623             //    // fixme - ask toolbars for heights?
21624             //    this.toolbars[i].onDestroy();
21625            // }
21626             
21627             //this.wrap.dom.innerHTML = '';
21628             //this.wrap.remove();
21629         }
21630     },
21631
21632     // private
21633     onFirstFocus : function(){
21634         
21635         this.assignDocWin();
21636         
21637         
21638         this.activated = true;
21639          
21640     
21641         if(Roo.isGecko){ // prevent silly gecko errors
21642             this.win.focus();
21643             var s = this.win.getSelection();
21644             if(!s.focusNode || s.focusNode.nodeType != 3){
21645                 var r = s.getRangeAt(0);
21646                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21647                 r.collapse(true);
21648                 this.deferFocus();
21649             }
21650             try{
21651                 this.execCmd('useCSS', true);
21652                 this.execCmd('styleWithCSS', false);
21653             }catch(e){}
21654         }
21655         this.owner.fireEvent('activate', this);
21656     },
21657
21658     // private
21659     adjustFont: function(btn){
21660         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21661         //if(Roo.isSafari){ // safari
21662         //    adjust *= 2;
21663        // }
21664         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21665         if(Roo.isSafari){ // safari
21666             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21667             v =  (v < 10) ? 10 : v;
21668             v =  (v > 48) ? 48 : v;
21669             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21670             
21671         }
21672         
21673         
21674         v = Math.max(1, v+adjust);
21675         
21676         this.execCmd('FontSize', v  );
21677     },
21678
21679     onEditorEvent : function(e)
21680     {
21681         this.owner.fireEvent('editorevent', this, e);
21682       //  this.updateToolbar();
21683         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21684     },
21685
21686     insertTag : function(tg)
21687     {
21688         // could be a bit smarter... -> wrap the current selected tRoo..
21689         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21690             
21691             range = this.createRange(this.getSelection());
21692             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21693             wrappingNode.appendChild(range.extractContents());
21694             range.insertNode(wrappingNode);
21695
21696             return;
21697             
21698             
21699             
21700         }
21701         this.execCmd("formatblock",   tg);
21702         
21703     },
21704     
21705     insertText : function(txt)
21706     {
21707         
21708         
21709         var range = this.createRange();
21710         range.deleteContents();
21711                //alert(Sender.getAttribute('label'));
21712                
21713         range.insertNode(this.doc.createTextNode(txt));
21714     } ,
21715     
21716      
21717
21718     /**
21719      * Executes a Midas editor command on the editor document and performs necessary focus and
21720      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21721      * @param {String} cmd The Midas command
21722      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21723      */
21724     relayCmd : function(cmd, value){
21725         this.win.focus();
21726         this.execCmd(cmd, value);
21727         this.owner.fireEvent('editorevent', this);
21728         //this.updateToolbar();
21729         this.owner.deferFocus();
21730     },
21731
21732     /**
21733      * Executes a Midas editor command directly on the editor document.
21734      * For visual commands, you should use {@link #relayCmd} instead.
21735      * <b>This should only be called after the editor is initialized.</b>
21736      * @param {String} cmd The Midas command
21737      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21738      */
21739     execCmd : function(cmd, value){
21740         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21741         this.syncValue();
21742     },
21743  
21744  
21745    
21746     /**
21747      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21748      * to insert tRoo.
21749      * @param {String} text | dom node.. 
21750      */
21751     insertAtCursor : function(text)
21752     {
21753         
21754         if(!this.activated){
21755             return;
21756         }
21757         /*
21758         if(Roo.isIE){
21759             this.win.focus();
21760             var r = this.doc.selection.createRange();
21761             if(r){
21762                 r.collapse(true);
21763                 r.pasteHTML(text);
21764                 this.syncValue();
21765                 this.deferFocus();
21766             
21767             }
21768             return;
21769         }
21770         */
21771         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21772             this.win.focus();
21773             
21774             
21775             // from jquery ui (MIT licenced)
21776             var range, node;
21777             var win = this.win;
21778             
21779             if (win.getSelection && win.getSelection().getRangeAt) {
21780                 range = win.getSelection().getRangeAt(0);
21781                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21782                 range.insertNode(node);
21783             } else if (win.document.selection && win.document.selection.createRange) {
21784                 // no firefox support
21785                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21786                 win.document.selection.createRange().pasteHTML(txt);
21787             } else {
21788                 // no firefox support
21789                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21790                 this.execCmd('InsertHTML', txt);
21791             } 
21792             
21793             this.syncValue();
21794             
21795             this.deferFocus();
21796         }
21797     },
21798  // private
21799     mozKeyPress : function(e){
21800         if(e.ctrlKey){
21801             var c = e.getCharCode(), cmd;
21802           
21803             if(c > 0){
21804                 c = String.fromCharCode(c).toLowerCase();
21805                 switch(c){
21806                     case 'b':
21807                         cmd = 'bold';
21808                         break;
21809                     case 'i':
21810                         cmd = 'italic';
21811                         break;
21812                     
21813                     case 'u':
21814                         cmd = 'underline';
21815                         break;
21816                     
21817                     case 'v':
21818                         this.cleanUpPaste.defer(100, this);
21819                         return;
21820                         
21821                 }
21822                 if(cmd){
21823                     this.win.focus();
21824                     this.execCmd(cmd);
21825                     this.deferFocus();
21826                     e.preventDefault();
21827                 }
21828                 
21829             }
21830         }
21831     },
21832
21833     // private
21834     fixKeys : function(){ // load time branching for fastest keydown performance
21835         if(Roo.isIE){
21836             return function(e){
21837                 var k = e.getKey(), r;
21838                 if(k == e.TAB){
21839                     e.stopEvent();
21840                     r = this.doc.selection.createRange();
21841                     if(r){
21842                         r.collapse(true);
21843                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21844                         this.deferFocus();
21845                     }
21846                     return;
21847                 }
21848                 
21849                 if(k == e.ENTER){
21850                     r = this.doc.selection.createRange();
21851                     if(r){
21852                         var target = r.parentElement();
21853                         if(!target || target.tagName.toLowerCase() != 'li'){
21854                             e.stopEvent();
21855                             r.pasteHTML('<br />');
21856                             r.collapse(false);
21857                             r.select();
21858                         }
21859                     }
21860                 }
21861                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21862                     this.cleanUpPaste.defer(100, this);
21863                     return;
21864                 }
21865                 
21866                 
21867             };
21868         }else if(Roo.isOpera){
21869             return function(e){
21870                 var k = e.getKey();
21871                 if(k == e.TAB){
21872                     e.stopEvent();
21873                     this.win.focus();
21874                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21875                     this.deferFocus();
21876                 }
21877                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21878                     this.cleanUpPaste.defer(100, this);
21879                     return;
21880                 }
21881                 
21882             };
21883         }else if(Roo.isSafari){
21884             return function(e){
21885                 var k = e.getKey();
21886                 
21887                 if(k == e.TAB){
21888                     e.stopEvent();
21889                     this.execCmd('InsertText','\t');
21890                     this.deferFocus();
21891                     return;
21892                 }
21893                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21894                     this.cleanUpPaste.defer(100, this);
21895                     return;
21896                 }
21897                 
21898              };
21899         }
21900     }(),
21901     
21902     getAllAncestors: function()
21903     {
21904         var p = this.getSelectedNode();
21905         var a = [];
21906         if (!p) {
21907             a.push(p); // push blank onto stack..
21908             p = this.getParentElement();
21909         }
21910         
21911         
21912         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21913             a.push(p);
21914             p = p.parentNode;
21915         }
21916         a.push(this.doc.body);
21917         return a;
21918     },
21919     lastSel : false,
21920     lastSelNode : false,
21921     
21922     
21923     getSelection : function() 
21924     {
21925         this.assignDocWin();
21926         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21927     },
21928     
21929     getSelectedNode: function() 
21930     {
21931         // this may only work on Gecko!!!
21932         
21933         // should we cache this!!!!
21934         
21935         
21936         
21937          
21938         var range = this.createRange(this.getSelection()).cloneRange();
21939         
21940         if (Roo.isIE) {
21941             var parent = range.parentElement();
21942             while (true) {
21943                 var testRange = range.duplicate();
21944                 testRange.moveToElementText(parent);
21945                 if (testRange.inRange(range)) {
21946                     break;
21947                 }
21948                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21949                     break;
21950                 }
21951                 parent = parent.parentElement;
21952             }
21953             return parent;
21954         }
21955         
21956         // is ancestor a text element.
21957         var ac =  range.commonAncestorContainer;
21958         if (ac.nodeType == 3) {
21959             ac = ac.parentNode;
21960         }
21961         
21962         var ar = ac.childNodes;
21963          
21964         var nodes = [];
21965         var other_nodes = [];
21966         var has_other_nodes = false;
21967         for (var i=0;i<ar.length;i++) {
21968             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21969                 continue;
21970             }
21971             // fullly contained node.
21972             
21973             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21974                 nodes.push(ar[i]);
21975                 continue;
21976             }
21977             
21978             // probably selected..
21979             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21980                 other_nodes.push(ar[i]);
21981                 continue;
21982             }
21983             // outer..
21984             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21985                 continue;
21986             }
21987             
21988             
21989             has_other_nodes = true;
21990         }
21991         if (!nodes.length && other_nodes.length) {
21992             nodes= other_nodes;
21993         }
21994         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21995             return false;
21996         }
21997         
21998         return nodes[0];
21999     },
22000     createRange: function(sel)
22001     {
22002         // this has strange effects when using with 
22003         // top toolbar - not sure if it's a great idea.
22004         //this.editor.contentWindow.focus();
22005         if (typeof sel != "undefined") {
22006             try {
22007                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22008             } catch(e) {
22009                 return this.doc.createRange();
22010             }
22011         } else {
22012             return this.doc.createRange();
22013         }
22014     },
22015     getParentElement: function()
22016     {
22017         
22018         this.assignDocWin();
22019         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22020         
22021         var range = this.createRange(sel);
22022          
22023         try {
22024             var p = range.commonAncestorContainer;
22025             while (p.nodeType == 3) { // text node
22026                 p = p.parentNode;
22027             }
22028             return p;
22029         } catch (e) {
22030             return null;
22031         }
22032     
22033     },
22034     /***
22035      *
22036      * Range intersection.. the hard stuff...
22037      *  '-1' = before
22038      *  '0' = hits..
22039      *  '1' = after.
22040      *         [ -- selected range --- ]
22041      *   [fail]                        [fail]
22042      *
22043      *    basically..
22044      *      if end is before start or  hits it. fail.
22045      *      if start is after end or hits it fail.
22046      *
22047      *   if either hits (but other is outside. - then it's not 
22048      *   
22049      *    
22050      **/
22051     
22052     
22053     // @see http://www.thismuchiknow.co.uk/?p=64.
22054     rangeIntersectsNode : function(range, node)
22055     {
22056         var nodeRange = node.ownerDocument.createRange();
22057         try {
22058             nodeRange.selectNode(node);
22059         } catch (e) {
22060             nodeRange.selectNodeContents(node);
22061         }
22062     
22063         var rangeStartRange = range.cloneRange();
22064         rangeStartRange.collapse(true);
22065     
22066         var rangeEndRange = range.cloneRange();
22067         rangeEndRange.collapse(false);
22068     
22069         var nodeStartRange = nodeRange.cloneRange();
22070         nodeStartRange.collapse(true);
22071     
22072         var nodeEndRange = nodeRange.cloneRange();
22073         nodeEndRange.collapse(false);
22074     
22075         return rangeStartRange.compareBoundaryPoints(
22076                  Range.START_TO_START, nodeEndRange) == -1 &&
22077                rangeEndRange.compareBoundaryPoints(
22078                  Range.START_TO_START, nodeStartRange) == 1;
22079         
22080          
22081     },
22082     rangeCompareNode : function(range, node)
22083     {
22084         var nodeRange = node.ownerDocument.createRange();
22085         try {
22086             nodeRange.selectNode(node);
22087         } catch (e) {
22088             nodeRange.selectNodeContents(node);
22089         }
22090         
22091         
22092         range.collapse(true);
22093     
22094         nodeRange.collapse(true);
22095      
22096         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22097         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22098          
22099         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22100         
22101         var nodeIsBefore   =  ss == 1;
22102         var nodeIsAfter    = ee == -1;
22103         
22104         if (nodeIsBefore && nodeIsAfter) {
22105             return 0; // outer
22106         }
22107         if (!nodeIsBefore && nodeIsAfter) {
22108             return 1; //right trailed.
22109         }
22110         
22111         if (nodeIsBefore && !nodeIsAfter) {
22112             return 2;  // left trailed.
22113         }
22114         // fully contined.
22115         return 3;
22116     },
22117
22118     // private? - in a new class?
22119     cleanUpPaste :  function()
22120     {
22121         // cleans up the whole document..
22122         Roo.log('cleanuppaste');
22123         
22124         this.cleanUpChildren(this.doc.body);
22125         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22126         if (clean != this.doc.body.innerHTML) {
22127             this.doc.body.innerHTML = clean;
22128         }
22129         
22130     },
22131     
22132     cleanWordChars : function(input) {// change the chars to hex code
22133         var he = Roo.HtmlEditorCore;
22134         
22135         var output = input;
22136         Roo.each(he.swapCodes, function(sw) { 
22137             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22138             
22139             output = output.replace(swapper, sw[1]);
22140         });
22141         
22142         return output;
22143     },
22144     
22145     
22146     cleanUpChildren : function (n)
22147     {
22148         if (!n.childNodes.length) {
22149             return;
22150         }
22151         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22152            this.cleanUpChild(n.childNodes[i]);
22153         }
22154     },
22155     
22156     
22157         
22158     
22159     cleanUpChild : function (node)
22160     {
22161         var ed = this;
22162         //console.log(node);
22163         if (node.nodeName == "#text") {
22164             // clean up silly Windows -- stuff?
22165             return; 
22166         }
22167         if (node.nodeName == "#comment") {
22168             node.parentNode.removeChild(node);
22169             // clean up silly Windows -- stuff?
22170             return; 
22171         }
22172         var lcname = node.tagName.toLowerCase();
22173         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22174         // whitelist of tags..
22175         
22176         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22177             // remove node.
22178             node.parentNode.removeChild(node);
22179             return;
22180             
22181         }
22182         
22183         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22184         
22185         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22186         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22187         
22188         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22189         //    remove_keep_children = true;
22190         //}
22191         
22192         if (remove_keep_children) {
22193             this.cleanUpChildren(node);
22194             // inserts everything just before this node...
22195             while (node.childNodes.length) {
22196                 var cn = node.childNodes[0];
22197                 node.removeChild(cn);
22198                 node.parentNode.insertBefore(cn, node);
22199             }
22200             node.parentNode.removeChild(node);
22201             return;
22202         }
22203         
22204         if (!node.attributes || !node.attributes.length) {
22205             this.cleanUpChildren(node);
22206             return;
22207         }
22208         
22209         function cleanAttr(n,v)
22210         {
22211             
22212             if (v.match(/^\./) || v.match(/^\//)) {
22213                 return;
22214             }
22215             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22216                 return;
22217             }
22218             if (v.match(/^#/)) {
22219                 return;
22220             }
22221 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22222             node.removeAttribute(n);
22223             
22224         }
22225         
22226         var cwhite = this.cwhite;
22227         var cblack = this.cblack;
22228             
22229         function cleanStyle(n,v)
22230         {
22231             if (v.match(/expression/)) { //XSS?? should we even bother..
22232                 node.removeAttribute(n);
22233                 return;
22234             }
22235             
22236             var parts = v.split(/;/);
22237             var clean = [];
22238             
22239             Roo.each(parts, function(p) {
22240                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22241                 if (!p.length) {
22242                     return true;
22243                 }
22244                 var l = p.split(':').shift().replace(/\s+/g,'');
22245                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22246                 
22247                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22248 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22249                     //node.removeAttribute(n);
22250                     return true;
22251                 }
22252                 //Roo.log()
22253                 // only allow 'c whitelisted system attributes'
22254                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22255 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22256                     //node.removeAttribute(n);
22257                     return true;
22258                 }
22259                 
22260                 
22261                  
22262                 
22263                 clean.push(p);
22264                 return true;
22265             });
22266             if (clean.length) { 
22267                 node.setAttribute(n, clean.join(';'));
22268             } else {
22269                 node.removeAttribute(n);
22270             }
22271             
22272         }
22273         
22274         
22275         for (var i = node.attributes.length-1; i > -1 ; i--) {
22276             var a = node.attributes[i];
22277             //console.log(a);
22278             
22279             if (a.name.toLowerCase().substr(0,2)=='on')  {
22280                 node.removeAttribute(a.name);
22281                 continue;
22282             }
22283             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22284                 node.removeAttribute(a.name);
22285                 continue;
22286             }
22287             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22288                 cleanAttr(a.name,a.value); // fixme..
22289                 continue;
22290             }
22291             if (a.name == 'style') {
22292                 cleanStyle(a.name,a.value);
22293                 continue;
22294             }
22295             /// clean up MS crap..
22296             // tecnically this should be a list of valid class'es..
22297             
22298             
22299             if (a.name == 'class') {
22300                 if (a.value.match(/^Mso/)) {
22301                     node.className = '';
22302                 }
22303                 
22304                 if (a.value.match(/^body$/)) {
22305                     node.className = '';
22306                 }
22307                 continue;
22308             }
22309             
22310             // style cleanup!?
22311             // class cleanup?
22312             
22313         }
22314         
22315         
22316         this.cleanUpChildren(node);
22317         
22318         
22319     },
22320     
22321     /**
22322      * Clean up MS wordisms...
22323      */
22324     cleanWord : function(node)
22325     {
22326         
22327         
22328         if (!node) {
22329             this.cleanWord(this.doc.body);
22330             return;
22331         }
22332         if (node.nodeName == "#text") {
22333             // clean up silly Windows -- stuff?
22334             return; 
22335         }
22336         if (node.nodeName == "#comment") {
22337             node.parentNode.removeChild(node);
22338             // clean up silly Windows -- stuff?
22339             return; 
22340         }
22341         
22342         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22343             node.parentNode.removeChild(node);
22344             return;
22345         }
22346         
22347         // remove - but keep children..
22348         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22349             while (node.childNodes.length) {
22350                 var cn = node.childNodes[0];
22351                 node.removeChild(cn);
22352                 node.parentNode.insertBefore(cn, node);
22353             }
22354             node.parentNode.removeChild(node);
22355             this.iterateChildren(node, this.cleanWord);
22356             return;
22357         }
22358         // clean styles
22359         if (node.className.length) {
22360             
22361             var cn = node.className.split(/\W+/);
22362             var cna = [];
22363             Roo.each(cn, function(cls) {
22364                 if (cls.match(/Mso[a-zA-Z]+/)) {
22365                     return;
22366                 }
22367                 cna.push(cls);
22368             });
22369             node.className = cna.length ? cna.join(' ') : '';
22370             if (!cna.length) {
22371                 node.removeAttribute("class");
22372             }
22373         }
22374         
22375         if (node.hasAttribute("lang")) {
22376             node.removeAttribute("lang");
22377         }
22378         
22379         if (node.hasAttribute("style")) {
22380             
22381             var styles = node.getAttribute("style").split(";");
22382             var nstyle = [];
22383             Roo.each(styles, function(s) {
22384                 if (!s.match(/:/)) {
22385                     return;
22386                 }
22387                 var kv = s.split(":");
22388                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22389                     return;
22390                 }
22391                 // what ever is left... we allow.
22392                 nstyle.push(s);
22393             });
22394             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22395             if (!nstyle.length) {
22396                 node.removeAttribute('style');
22397             }
22398         }
22399         this.iterateChildren(node, this.cleanWord);
22400         
22401         
22402         
22403     },
22404     /**
22405      * iterateChildren of a Node, calling fn each time, using this as the scole..
22406      * @param {DomNode} node node to iterate children of.
22407      * @param {Function} fn method of this class to call on each item.
22408      */
22409     iterateChildren : function(node, fn)
22410     {
22411         if (!node.childNodes.length) {
22412                 return;
22413         }
22414         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22415            fn.call(this, node.childNodes[i])
22416         }
22417     },
22418     
22419     
22420     /**
22421      * cleanTableWidths.
22422      *
22423      * Quite often pasting from word etc.. results in tables with column and widths.
22424      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22425      *
22426      */
22427     cleanTableWidths : function(node)
22428     {
22429          
22430          
22431         if (!node) {
22432             this.cleanTableWidths(this.doc.body);
22433             return;
22434         }
22435         
22436         // ignore list...
22437         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22438             return; 
22439         }
22440         Roo.log(node.tagName);
22441         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22442             this.iterateChildren(node, this.cleanTableWidths);
22443             return;
22444         }
22445         if (node.hasAttribute('width')) {
22446             node.removeAttribute('width');
22447         }
22448         
22449          
22450         if (node.hasAttribute("style")) {
22451             // pretty basic...
22452             
22453             var styles = node.getAttribute("style").split(";");
22454             var nstyle = [];
22455             Roo.each(styles, function(s) {
22456                 if (!s.match(/:/)) {
22457                     return;
22458                 }
22459                 var kv = s.split(":");
22460                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22461                     return;
22462                 }
22463                 // what ever is left... we allow.
22464                 nstyle.push(s);
22465             });
22466             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22467             if (!nstyle.length) {
22468                 node.removeAttribute('style');
22469             }
22470         }
22471         
22472         this.iterateChildren(node, this.cleanTableWidths);
22473         
22474         
22475     },
22476     
22477     
22478     
22479     
22480     domToHTML : function(currentElement, depth, nopadtext) {
22481         
22482         depth = depth || 0;
22483         nopadtext = nopadtext || false;
22484     
22485         if (!currentElement) {
22486             return this.domToHTML(this.doc.body);
22487         }
22488         
22489         //Roo.log(currentElement);
22490         var j;
22491         var allText = false;
22492         var nodeName = currentElement.nodeName;
22493         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22494         
22495         if  (nodeName == '#text') {
22496             
22497             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22498         }
22499         
22500         
22501         var ret = '';
22502         if (nodeName != 'BODY') {
22503              
22504             var i = 0;
22505             // Prints the node tagName, such as <A>, <IMG>, etc
22506             if (tagName) {
22507                 var attr = [];
22508                 for(i = 0; i < currentElement.attributes.length;i++) {
22509                     // quoting?
22510                     var aname = currentElement.attributes.item(i).name;
22511                     if (!currentElement.attributes.item(i).value.length) {
22512                         continue;
22513                     }
22514                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22515                 }
22516                 
22517                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22518             } 
22519             else {
22520                 
22521                 // eack
22522             }
22523         } else {
22524             tagName = false;
22525         }
22526         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22527             return ret;
22528         }
22529         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22530             nopadtext = true;
22531         }
22532         
22533         
22534         // Traverse the tree
22535         i = 0;
22536         var currentElementChild = currentElement.childNodes.item(i);
22537         var allText = true;
22538         var innerHTML  = '';
22539         lastnode = '';
22540         while (currentElementChild) {
22541             // Formatting code (indent the tree so it looks nice on the screen)
22542             var nopad = nopadtext;
22543             if (lastnode == 'SPAN') {
22544                 nopad  = true;
22545             }
22546             // text
22547             if  (currentElementChild.nodeName == '#text') {
22548                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22549                 toadd = nopadtext ? toadd : toadd.trim();
22550                 if (!nopad && toadd.length > 80) {
22551                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22552                 }
22553                 innerHTML  += toadd;
22554                 
22555                 i++;
22556                 currentElementChild = currentElement.childNodes.item(i);
22557                 lastNode = '';
22558                 continue;
22559             }
22560             allText = false;
22561             
22562             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22563                 
22564             // Recursively traverse the tree structure of the child node
22565             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22566             lastnode = currentElementChild.nodeName;
22567             i++;
22568             currentElementChild=currentElement.childNodes.item(i);
22569         }
22570         
22571         ret += innerHTML;
22572         
22573         if (!allText) {
22574                 // The remaining code is mostly for formatting the tree
22575             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22576         }
22577         
22578         
22579         if (tagName) {
22580             ret+= "</"+tagName+">";
22581         }
22582         return ret;
22583         
22584     },
22585         
22586     applyBlacklists : function()
22587     {
22588         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22589         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22590         
22591         this.white = [];
22592         this.black = [];
22593         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22594             if (b.indexOf(tag) > -1) {
22595                 return;
22596             }
22597             this.white.push(tag);
22598             
22599         }, this);
22600         
22601         Roo.each(w, function(tag) {
22602             if (b.indexOf(tag) > -1) {
22603                 return;
22604             }
22605             if (this.white.indexOf(tag) > -1) {
22606                 return;
22607             }
22608             this.white.push(tag);
22609             
22610         }, this);
22611         
22612         
22613         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22614             if (w.indexOf(tag) > -1) {
22615                 return;
22616             }
22617             this.black.push(tag);
22618             
22619         }, this);
22620         
22621         Roo.each(b, function(tag) {
22622             if (w.indexOf(tag) > -1) {
22623                 return;
22624             }
22625             if (this.black.indexOf(tag) > -1) {
22626                 return;
22627             }
22628             this.black.push(tag);
22629             
22630         }, this);
22631         
22632         
22633         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22634         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22635         
22636         this.cwhite = [];
22637         this.cblack = [];
22638         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22639             if (b.indexOf(tag) > -1) {
22640                 return;
22641             }
22642             this.cwhite.push(tag);
22643             
22644         }, this);
22645         
22646         Roo.each(w, function(tag) {
22647             if (b.indexOf(tag) > -1) {
22648                 return;
22649             }
22650             if (this.cwhite.indexOf(tag) > -1) {
22651                 return;
22652             }
22653             this.cwhite.push(tag);
22654             
22655         }, this);
22656         
22657         
22658         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22659             if (w.indexOf(tag) > -1) {
22660                 return;
22661             }
22662             this.cblack.push(tag);
22663             
22664         }, this);
22665         
22666         Roo.each(b, function(tag) {
22667             if (w.indexOf(tag) > -1) {
22668                 return;
22669             }
22670             if (this.cblack.indexOf(tag) > -1) {
22671                 return;
22672             }
22673             this.cblack.push(tag);
22674             
22675         }, this);
22676     },
22677     
22678     setStylesheets : function(stylesheets)
22679     {
22680         if(typeof(stylesheets) == 'string'){
22681             Roo.get(this.iframe.contentDocument.head).createChild({
22682                 tag : 'link',
22683                 rel : 'stylesheet',
22684                 type : 'text/css',
22685                 href : stylesheets
22686             });
22687             
22688             return;
22689         }
22690         var _this = this;
22691      
22692         Roo.each(stylesheets, function(s) {
22693             if(!s.length){
22694                 return;
22695             }
22696             
22697             Roo.get(_this.iframe.contentDocument.head).createChild({
22698                 tag : 'link',
22699                 rel : 'stylesheet',
22700                 type : 'text/css',
22701                 href : s
22702             });
22703         });
22704
22705         
22706     },
22707     
22708     removeStylesheets : function()
22709     {
22710         var _this = this;
22711         
22712         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22713             s.remove();
22714         });
22715     },
22716     
22717     setStyle : function(style)
22718     {
22719         Roo.get(this.iframe.contentDocument.head).createChild({
22720             tag : 'style',
22721             type : 'text/css',
22722             html : style
22723         });
22724
22725         return;
22726     }
22727     
22728     // hide stuff that is not compatible
22729     /**
22730      * @event blur
22731      * @hide
22732      */
22733     /**
22734      * @event change
22735      * @hide
22736      */
22737     /**
22738      * @event focus
22739      * @hide
22740      */
22741     /**
22742      * @event specialkey
22743      * @hide
22744      */
22745     /**
22746      * @cfg {String} fieldClass @hide
22747      */
22748     /**
22749      * @cfg {String} focusClass @hide
22750      */
22751     /**
22752      * @cfg {String} autoCreate @hide
22753      */
22754     /**
22755      * @cfg {String} inputType @hide
22756      */
22757     /**
22758      * @cfg {String} invalidClass @hide
22759      */
22760     /**
22761      * @cfg {String} invalidText @hide
22762      */
22763     /**
22764      * @cfg {String} msgFx @hide
22765      */
22766     /**
22767      * @cfg {String} validateOnBlur @hide
22768      */
22769 });
22770
22771 Roo.HtmlEditorCore.white = [
22772         'area', 'br', 'img', 'input', 'hr', 'wbr',
22773         
22774        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22775        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22776        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22777        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22778        'table',   'ul',         'xmp', 
22779        
22780        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22781       'thead',   'tr', 
22782      
22783       'dir', 'menu', 'ol', 'ul', 'dl',
22784        
22785       'embed',  'object'
22786 ];
22787
22788
22789 Roo.HtmlEditorCore.black = [
22790     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22791         'applet', // 
22792         'base',   'basefont', 'bgsound', 'blink',  'body', 
22793         'frame',  'frameset', 'head',    'html',   'ilayer', 
22794         'iframe', 'layer',  'link',     'meta',    'object',   
22795         'script', 'style' ,'title',  'xml' // clean later..
22796 ];
22797 Roo.HtmlEditorCore.clean = [
22798     'script', 'style', 'title', 'xml'
22799 ];
22800 Roo.HtmlEditorCore.remove = [
22801     'font'
22802 ];
22803 // attributes..
22804
22805 Roo.HtmlEditorCore.ablack = [
22806     'on'
22807 ];
22808     
22809 Roo.HtmlEditorCore.aclean = [ 
22810     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22811 ];
22812
22813 // protocols..
22814 Roo.HtmlEditorCore.pwhite= [
22815         'http',  'https',  'mailto'
22816 ];
22817
22818 // white listed style attributes.
22819 Roo.HtmlEditorCore.cwhite= [
22820       //  'text-align', /// default is to allow most things..
22821       
22822          
22823 //        'font-size'//??
22824 ];
22825
22826 // black listed style attributes.
22827 Roo.HtmlEditorCore.cblack= [
22828       //  'font-size' -- this can be set by the project 
22829 ];
22830
22831
22832 Roo.HtmlEditorCore.swapCodes   =[ 
22833     [    8211, "--" ], 
22834     [    8212, "--" ], 
22835     [    8216,  "'" ],  
22836     [    8217, "'" ],  
22837     [    8220, '"' ],  
22838     [    8221, '"' ],  
22839     [    8226, "*" ],  
22840     [    8230, "..." ]
22841 ]; 
22842
22843     /*
22844  * - LGPL
22845  *
22846  * HtmlEditor
22847  * 
22848  */
22849
22850 /**
22851  * @class Roo.bootstrap.HtmlEditor
22852  * @extends Roo.bootstrap.TextArea
22853  * Bootstrap HtmlEditor class
22854
22855  * @constructor
22856  * Create a new HtmlEditor
22857  * @param {Object} config The config object
22858  */
22859
22860 Roo.bootstrap.HtmlEditor = function(config){
22861     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22862     if (!this.toolbars) {
22863         this.toolbars = [];
22864     }
22865     
22866     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22867     this.addEvents({
22868             /**
22869              * @event initialize
22870              * Fires when the editor is fully initialized (including the iframe)
22871              * @param {HtmlEditor} this
22872              */
22873             initialize: true,
22874             /**
22875              * @event activate
22876              * Fires when the editor is first receives the focus. Any insertion must wait
22877              * until after this event.
22878              * @param {HtmlEditor} this
22879              */
22880             activate: true,
22881              /**
22882              * @event beforesync
22883              * Fires before the textarea is updated with content from the editor iframe. Return false
22884              * to cancel the sync.
22885              * @param {HtmlEditor} this
22886              * @param {String} html
22887              */
22888             beforesync: true,
22889              /**
22890              * @event beforepush
22891              * Fires before the iframe editor is updated with content from the textarea. Return false
22892              * to cancel the push.
22893              * @param {HtmlEditor} this
22894              * @param {String} html
22895              */
22896             beforepush: true,
22897              /**
22898              * @event sync
22899              * Fires when the textarea is updated with content from the editor iframe.
22900              * @param {HtmlEditor} this
22901              * @param {String} html
22902              */
22903             sync: true,
22904              /**
22905              * @event push
22906              * Fires when the iframe editor is updated with content from the textarea.
22907              * @param {HtmlEditor} this
22908              * @param {String} html
22909              */
22910             push: true,
22911              /**
22912              * @event editmodechange
22913              * Fires when the editor switches edit modes
22914              * @param {HtmlEditor} this
22915              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22916              */
22917             editmodechange: true,
22918             /**
22919              * @event editorevent
22920              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22921              * @param {HtmlEditor} this
22922              */
22923             editorevent: true,
22924             /**
22925              * @event firstfocus
22926              * Fires when on first focus - needed by toolbars..
22927              * @param {HtmlEditor} this
22928              */
22929             firstfocus: true,
22930             /**
22931              * @event autosave
22932              * Auto save the htmlEditor value as a file into Events
22933              * @param {HtmlEditor} this
22934              */
22935             autosave: true,
22936             /**
22937              * @event savedpreview
22938              * preview the saved version of htmlEditor
22939              * @param {HtmlEditor} this
22940              */
22941             savedpreview: true
22942         });
22943 };
22944
22945
22946 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22947     
22948     
22949       /**
22950      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22951      */
22952     toolbars : false,
22953     
22954      /**
22955     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22956     */
22957     btns : [],
22958    
22959      /**
22960      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22961      *                        Roo.resizable.
22962      */
22963     resizable : false,
22964      /**
22965      * @cfg {Number} height (in pixels)
22966      */   
22967     height: 300,
22968    /**
22969      * @cfg {Number} width (in pixels)
22970      */   
22971     width: false,
22972     
22973     /**
22974      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22975      * 
22976      */
22977     stylesheets: false,
22978     
22979     // id of frame..
22980     frameId: false,
22981     
22982     // private properties
22983     validationEvent : false,
22984     deferHeight: true,
22985     initialized : false,
22986     activated : false,
22987     
22988     onFocus : Roo.emptyFn,
22989     iframePad:3,
22990     hideMode:'offsets',
22991     
22992     tbContainer : false,
22993     
22994     toolbarContainer :function() {
22995         return this.wrap.select('.x-html-editor-tb',true).first();
22996     },
22997
22998     /**
22999      * Protected method that will not generally be called directly. It
23000      * is called when the editor creates its toolbar. Override this method if you need to
23001      * add custom toolbar buttons.
23002      * @param {HtmlEditor} editor
23003      */
23004     createToolbar : function(){
23005         Roo.log('renewing');
23006         Roo.log("create toolbars");
23007         
23008         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23009         this.toolbars[0].render(this.toolbarContainer());
23010         
23011         return;
23012         
23013 //        if (!editor.toolbars || !editor.toolbars.length) {
23014 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23015 //        }
23016 //        
23017 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23018 //            editor.toolbars[i] = Roo.factory(
23019 //                    typeof(editor.toolbars[i]) == 'string' ?
23020 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23021 //                Roo.bootstrap.HtmlEditor);
23022 //            editor.toolbars[i].init(editor);
23023 //        }
23024     },
23025
23026      
23027     // private
23028     onRender : function(ct, position)
23029     {
23030        // Roo.log("Call onRender: " + this.xtype);
23031         var _t = this;
23032         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23033       
23034         this.wrap = this.inputEl().wrap({
23035             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23036         });
23037         
23038         this.editorcore.onRender(ct, position);
23039          
23040         if (this.resizable) {
23041             this.resizeEl = new Roo.Resizable(this.wrap, {
23042                 pinned : true,
23043                 wrap: true,
23044                 dynamic : true,
23045                 minHeight : this.height,
23046                 height: this.height,
23047                 handles : this.resizable,
23048                 width: this.width,
23049                 listeners : {
23050                     resize : function(r, w, h) {
23051                         _t.onResize(w,h); // -something
23052                     }
23053                 }
23054             });
23055             
23056         }
23057         this.createToolbar(this);
23058        
23059         
23060         if(!this.width && this.resizable){
23061             this.setSize(this.wrap.getSize());
23062         }
23063         if (this.resizeEl) {
23064             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23065             // should trigger onReize..
23066         }
23067         
23068     },
23069
23070     // private
23071     onResize : function(w, h)
23072     {
23073         Roo.log('resize: ' +w + ',' + h );
23074         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23075         var ew = false;
23076         var eh = false;
23077         
23078         if(this.inputEl() ){
23079             if(typeof w == 'number'){
23080                 var aw = w - this.wrap.getFrameWidth('lr');
23081                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23082                 ew = aw;
23083             }
23084             if(typeof h == 'number'){
23085                  var tbh = -11;  // fixme it needs to tool bar size!
23086                 for (var i =0; i < this.toolbars.length;i++) {
23087                     // fixme - ask toolbars for heights?
23088                     tbh += this.toolbars[i].el.getHeight();
23089                     //if (this.toolbars[i].footer) {
23090                     //    tbh += this.toolbars[i].footer.el.getHeight();
23091                     //}
23092                 }
23093               
23094                 
23095                 
23096                 
23097                 
23098                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23099                 ah -= 5; // knock a few pixes off for look..
23100                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23101                 var eh = ah;
23102             }
23103         }
23104         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23105         this.editorcore.onResize(ew,eh);
23106         
23107     },
23108
23109     /**
23110      * Toggles the editor between standard and source edit mode.
23111      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23112      */
23113     toggleSourceEdit : function(sourceEditMode)
23114     {
23115         this.editorcore.toggleSourceEdit(sourceEditMode);
23116         
23117         if(this.editorcore.sourceEditMode){
23118             Roo.log('editor - showing textarea');
23119             
23120 //            Roo.log('in');
23121 //            Roo.log(this.syncValue());
23122             this.syncValue();
23123             this.inputEl().removeClass(['hide', 'x-hidden']);
23124             this.inputEl().dom.removeAttribute('tabIndex');
23125             this.inputEl().focus();
23126         }else{
23127             Roo.log('editor - hiding textarea');
23128 //            Roo.log('out')
23129 //            Roo.log(this.pushValue()); 
23130             this.pushValue();
23131             
23132             this.inputEl().addClass(['hide', 'x-hidden']);
23133             this.inputEl().dom.setAttribute('tabIndex', -1);
23134             //this.deferFocus();
23135         }
23136          
23137         if(this.resizable){
23138             this.setSize(this.wrap.getSize());
23139         }
23140         
23141         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23142     },
23143  
23144     // private (for BoxComponent)
23145     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23146
23147     // private (for BoxComponent)
23148     getResizeEl : function(){
23149         return this.wrap;
23150     },
23151
23152     // private (for BoxComponent)
23153     getPositionEl : function(){
23154         return this.wrap;
23155     },
23156
23157     // private
23158     initEvents : function(){
23159         this.originalValue = this.getValue();
23160     },
23161
23162 //    /**
23163 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23164 //     * @method
23165 //     */
23166 //    markInvalid : Roo.emptyFn,
23167 //    /**
23168 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23169 //     * @method
23170 //     */
23171 //    clearInvalid : Roo.emptyFn,
23172
23173     setValue : function(v){
23174         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23175         this.editorcore.pushValue();
23176     },
23177
23178      
23179     // private
23180     deferFocus : function(){
23181         this.focus.defer(10, this);
23182     },
23183
23184     // doc'ed in Field
23185     focus : function(){
23186         this.editorcore.focus();
23187         
23188     },
23189       
23190
23191     // private
23192     onDestroy : function(){
23193         
23194         
23195         
23196         if(this.rendered){
23197             
23198             for (var i =0; i < this.toolbars.length;i++) {
23199                 // fixme - ask toolbars for heights?
23200                 this.toolbars[i].onDestroy();
23201             }
23202             
23203             this.wrap.dom.innerHTML = '';
23204             this.wrap.remove();
23205         }
23206     },
23207
23208     // private
23209     onFirstFocus : function(){
23210         //Roo.log("onFirstFocus");
23211         this.editorcore.onFirstFocus();
23212          for (var i =0; i < this.toolbars.length;i++) {
23213             this.toolbars[i].onFirstFocus();
23214         }
23215         
23216     },
23217     
23218     // private
23219     syncValue : function()
23220     {   
23221         this.editorcore.syncValue();
23222     },
23223     
23224     pushValue : function()
23225     {   
23226         this.editorcore.pushValue();
23227     }
23228      
23229     
23230     // hide stuff that is not compatible
23231     /**
23232      * @event blur
23233      * @hide
23234      */
23235     /**
23236      * @event change
23237      * @hide
23238      */
23239     /**
23240      * @event focus
23241      * @hide
23242      */
23243     /**
23244      * @event specialkey
23245      * @hide
23246      */
23247     /**
23248      * @cfg {String} fieldClass @hide
23249      */
23250     /**
23251      * @cfg {String} focusClass @hide
23252      */
23253     /**
23254      * @cfg {String} autoCreate @hide
23255      */
23256     /**
23257      * @cfg {String} inputType @hide
23258      */
23259     /**
23260      * @cfg {String} invalidClass @hide
23261      */
23262     /**
23263      * @cfg {String} invalidText @hide
23264      */
23265     /**
23266      * @cfg {String} msgFx @hide
23267      */
23268     /**
23269      * @cfg {String} validateOnBlur @hide
23270      */
23271 });
23272  
23273     
23274    
23275    
23276    
23277       
23278 Roo.namespace('Roo.bootstrap.htmleditor');
23279 /**
23280  * @class Roo.bootstrap.HtmlEditorToolbar1
23281  * Basic Toolbar
23282  * 
23283  * Usage:
23284  *
23285  new Roo.bootstrap.HtmlEditor({
23286     ....
23287     toolbars : [
23288         new Roo.bootstrap.HtmlEditorToolbar1({
23289             disable : { fonts: 1 , format: 1, ..., ... , ...],
23290             btns : [ .... ]
23291         })
23292     }
23293      
23294  * 
23295  * @cfg {Object} disable List of elements to disable..
23296  * @cfg {Array} btns List of additional buttons.
23297  * 
23298  * 
23299  * NEEDS Extra CSS? 
23300  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23301  */
23302  
23303 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23304 {
23305     
23306     Roo.apply(this, config);
23307     
23308     // default disabled, based on 'good practice'..
23309     this.disable = this.disable || {};
23310     Roo.applyIf(this.disable, {
23311         fontSize : true,
23312         colors : true,
23313         specialElements : true
23314     });
23315     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23316     
23317     this.editor = config.editor;
23318     this.editorcore = config.editor.editorcore;
23319     
23320     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23321     
23322     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23323     // dont call parent... till later.
23324 }
23325 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23326      
23327     bar : true,
23328     
23329     editor : false,
23330     editorcore : false,
23331     
23332     
23333     formats : [
23334         "p" ,  
23335         "h1","h2","h3","h4","h5","h6", 
23336         "pre", "code", 
23337         "abbr", "acronym", "address", "cite", "samp", "var",
23338         'div','span'
23339     ],
23340     
23341     onRender : function(ct, position)
23342     {
23343        // Roo.log("Call onRender: " + this.xtype);
23344         
23345        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23346        Roo.log(this.el);
23347        this.el.dom.style.marginBottom = '0';
23348        var _this = this;
23349        var editorcore = this.editorcore;
23350        var editor= this.editor;
23351        
23352        var children = [];
23353        var btn = function(id,cmd , toggle, handler, html){
23354        
23355             var  event = toggle ? 'toggle' : 'click';
23356        
23357             var a = {
23358                 size : 'sm',
23359                 xtype: 'Button',
23360                 xns: Roo.bootstrap,
23361                 glyphicon : id,
23362                 cmd : id || cmd,
23363                 enableToggle:toggle !== false,
23364                 html : html || '',
23365                 pressed : toggle ? false : null,
23366                 listeners : {}
23367             };
23368             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23369                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23370             };
23371             children.push(a);
23372             return a;
23373        }
23374        
23375     //    var cb_box = function...
23376         
23377         var style = {
23378                 xtype: 'Button',
23379                 size : 'sm',
23380                 xns: Roo.bootstrap,
23381                 glyphicon : 'font',
23382                 //html : 'submit'
23383                 menu : {
23384                     xtype: 'Menu',
23385                     xns: Roo.bootstrap,
23386                     items:  []
23387                 }
23388         };
23389         Roo.each(this.formats, function(f) {
23390             style.menu.items.push({
23391                 xtype :'MenuItem',
23392                 xns: Roo.bootstrap,
23393                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23394                 tagname : f,
23395                 listeners : {
23396                     click : function()
23397                     {
23398                         editorcore.insertTag(this.tagname);
23399                         editor.focus();
23400                     }
23401                 }
23402                 
23403             });
23404         });
23405         children.push(style);   
23406         
23407         btn('bold',false,true);
23408         btn('italic',false,true);
23409         btn('align-left', 'justifyleft',true);
23410         btn('align-center', 'justifycenter',true);
23411         btn('align-right' , 'justifyright',true);
23412         btn('link', false, false, function(btn) {
23413             //Roo.log("create link?");
23414             var url = prompt(this.createLinkText, this.defaultLinkValue);
23415             if(url && url != 'http:/'+'/'){
23416                 this.editorcore.relayCmd('createlink', url);
23417             }
23418         }),
23419         btn('list','insertunorderedlist',true);
23420         btn('pencil', false,true, function(btn){
23421                 Roo.log(this);
23422                 this.toggleSourceEdit(btn.pressed);
23423         });
23424         
23425         if (this.editor.btns.length > 0) {
23426             for (var i = 0; i<this.editor.btns.length; i++) {
23427                 children.push(this.editor.btns[i]);
23428             }
23429         }
23430         
23431         /*
23432         var cog = {
23433                 xtype: 'Button',
23434                 size : 'sm',
23435                 xns: Roo.bootstrap,
23436                 glyphicon : 'cog',
23437                 //html : 'submit'
23438                 menu : {
23439                     xtype: 'Menu',
23440                     xns: Roo.bootstrap,
23441                     items:  []
23442                 }
23443         };
23444         
23445         cog.menu.items.push({
23446             xtype :'MenuItem',
23447             xns: Roo.bootstrap,
23448             html : Clean styles,
23449             tagname : f,
23450             listeners : {
23451                 click : function()
23452                 {
23453                     editorcore.insertTag(this.tagname);
23454                     editor.focus();
23455                 }
23456             }
23457             
23458         });
23459        */
23460         
23461          
23462        this.xtype = 'NavSimplebar';
23463         
23464         for(var i=0;i< children.length;i++) {
23465             
23466             this.buttons.add(this.addxtypeChild(children[i]));
23467             
23468         }
23469         
23470         editor.on('editorevent', this.updateToolbar, this);
23471     },
23472     onBtnClick : function(id)
23473     {
23474        this.editorcore.relayCmd(id);
23475        this.editorcore.focus();
23476     },
23477     
23478     /**
23479      * Protected method that will not generally be called directly. It triggers
23480      * a toolbar update by reading the markup state of the current selection in the editor.
23481      */
23482     updateToolbar: function(){
23483
23484         if(!this.editorcore.activated){
23485             this.editor.onFirstFocus(); // is this neeed?
23486             return;
23487         }
23488
23489         var btns = this.buttons; 
23490         var doc = this.editorcore.doc;
23491         btns.get('bold').setActive(doc.queryCommandState('bold'));
23492         btns.get('italic').setActive(doc.queryCommandState('italic'));
23493         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23494         
23495         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23496         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23497         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23498         
23499         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23500         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23501          /*
23502         
23503         var ans = this.editorcore.getAllAncestors();
23504         if (this.formatCombo) {
23505             
23506             
23507             var store = this.formatCombo.store;
23508             this.formatCombo.setValue("");
23509             for (var i =0; i < ans.length;i++) {
23510                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23511                     // select it..
23512                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23513                     break;
23514                 }
23515             }
23516         }
23517         
23518         
23519         
23520         // hides menus... - so this cant be on a menu...
23521         Roo.bootstrap.MenuMgr.hideAll();
23522         */
23523         Roo.bootstrap.MenuMgr.hideAll();
23524         //this.editorsyncValue();
23525     },
23526     onFirstFocus: function() {
23527         this.buttons.each(function(item){
23528            item.enable();
23529         });
23530     },
23531     toggleSourceEdit : function(sourceEditMode){
23532         
23533           
23534         if(sourceEditMode){
23535             Roo.log("disabling buttons");
23536            this.buttons.each( function(item){
23537                 if(item.cmd != 'pencil'){
23538                     item.disable();
23539                 }
23540             });
23541           
23542         }else{
23543             Roo.log("enabling buttons");
23544             if(this.editorcore.initialized){
23545                 this.buttons.each( function(item){
23546                     item.enable();
23547                 });
23548             }
23549             
23550         }
23551         Roo.log("calling toggole on editor");
23552         // tell the editor that it's been pressed..
23553         this.editor.toggleSourceEdit(sourceEditMode);
23554        
23555     }
23556 });
23557
23558
23559
23560
23561
23562 /**
23563  * @class Roo.bootstrap.Table.AbstractSelectionModel
23564  * @extends Roo.util.Observable
23565  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23566  * implemented by descendant classes.  This class should not be directly instantiated.
23567  * @constructor
23568  */
23569 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23570     this.locked = false;
23571     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23572 };
23573
23574
23575 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23576     /** @ignore Called by the grid automatically. Do not call directly. */
23577     init : function(grid){
23578         this.grid = grid;
23579         this.initEvents();
23580     },
23581
23582     /**
23583      * Locks the selections.
23584      */
23585     lock : function(){
23586         this.locked = true;
23587     },
23588
23589     /**
23590      * Unlocks the selections.
23591      */
23592     unlock : function(){
23593         this.locked = false;
23594     },
23595
23596     /**
23597      * Returns true if the selections are locked.
23598      * @return {Boolean}
23599      */
23600     isLocked : function(){
23601         return this.locked;
23602     }
23603 });
23604 /**
23605  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23606  * @class Roo.bootstrap.Table.RowSelectionModel
23607  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23608  * It supports multiple selections and keyboard selection/navigation. 
23609  * @constructor
23610  * @param {Object} config
23611  */
23612
23613 Roo.bootstrap.Table.RowSelectionModel = function(config){
23614     Roo.apply(this, config);
23615     this.selections = new Roo.util.MixedCollection(false, function(o){
23616         return o.id;
23617     });
23618
23619     this.last = false;
23620     this.lastActive = false;
23621
23622     this.addEvents({
23623         /**
23624              * @event selectionchange
23625              * Fires when the selection changes
23626              * @param {SelectionModel} this
23627              */
23628             "selectionchange" : true,
23629         /**
23630              * @event afterselectionchange
23631              * Fires after the selection changes (eg. by key press or clicking)
23632              * @param {SelectionModel} this
23633              */
23634             "afterselectionchange" : true,
23635         /**
23636              * @event beforerowselect
23637              * Fires when a row is selected being selected, return false to cancel.
23638              * @param {SelectionModel} this
23639              * @param {Number} rowIndex The selected index
23640              * @param {Boolean} keepExisting False if other selections will be cleared
23641              */
23642             "beforerowselect" : true,
23643         /**
23644              * @event rowselect
23645              * Fires when a row is selected.
23646              * @param {SelectionModel} this
23647              * @param {Number} rowIndex The selected index
23648              * @param {Roo.data.Record} r The record
23649              */
23650             "rowselect" : true,
23651         /**
23652              * @event rowdeselect
23653              * Fires when a row is deselected.
23654              * @param {SelectionModel} this
23655              * @param {Number} rowIndex The selected index
23656              */
23657         "rowdeselect" : true
23658     });
23659     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23660     this.locked = false;
23661  };
23662
23663 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23664     /**
23665      * @cfg {Boolean} singleSelect
23666      * True to allow selection of only one row at a time (defaults to false)
23667      */
23668     singleSelect : false,
23669
23670     // private
23671     initEvents : function()
23672     {
23673
23674         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23675         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23676         //}else{ // allow click to work like normal
23677          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23678         //}
23679         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23680         this.grid.on("rowclick", this.handleMouseDown, this);
23681         
23682         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23683             "up" : function(e){
23684                 if(!e.shiftKey){
23685                     this.selectPrevious(e.shiftKey);
23686                 }else if(this.last !== false && this.lastActive !== false){
23687                     var last = this.last;
23688                     this.selectRange(this.last,  this.lastActive-1);
23689                     this.grid.getView().focusRow(this.lastActive);
23690                     if(last !== false){
23691                         this.last = last;
23692                     }
23693                 }else{
23694                     this.selectFirstRow();
23695                 }
23696                 this.fireEvent("afterselectionchange", this);
23697             },
23698             "down" : function(e){
23699                 if(!e.shiftKey){
23700                     this.selectNext(e.shiftKey);
23701                 }else if(this.last !== false && this.lastActive !== false){
23702                     var last = this.last;
23703                     this.selectRange(this.last,  this.lastActive+1);
23704                     this.grid.getView().focusRow(this.lastActive);
23705                     if(last !== false){
23706                         this.last = last;
23707                     }
23708                 }else{
23709                     this.selectFirstRow();
23710                 }
23711                 this.fireEvent("afterselectionchange", this);
23712             },
23713             scope: this
23714         });
23715         this.grid.store.on('load', function(){
23716             this.selections.clear();
23717         },this);
23718         /*
23719         var view = this.grid.view;
23720         view.on("refresh", this.onRefresh, this);
23721         view.on("rowupdated", this.onRowUpdated, this);
23722         view.on("rowremoved", this.onRemove, this);
23723         */
23724     },
23725
23726     // private
23727     onRefresh : function()
23728     {
23729         var ds = this.grid.store, i, v = this.grid.view;
23730         var s = this.selections;
23731         s.each(function(r){
23732             if((i = ds.indexOfId(r.id)) != -1){
23733                 v.onRowSelect(i);
23734             }else{
23735                 s.remove(r);
23736             }
23737         });
23738     },
23739
23740     // private
23741     onRemove : function(v, index, r){
23742         this.selections.remove(r);
23743     },
23744
23745     // private
23746     onRowUpdated : function(v, index, r){
23747         if(this.isSelected(r)){
23748             v.onRowSelect(index);
23749         }
23750     },
23751
23752     /**
23753      * Select records.
23754      * @param {Array} records The records to select
23755      * @param {Boolean} keepExisting (optional) True to keep existing selections
23756      */
23757     selectRecords : function(records, keepExisting)
23758     {
23759         if(!keepExisting){
23760             this.clearSelections();
23761         }
23762             var ds = this.grid.store;
23763         for(var i = 0, len = records.length; i < len; i++){
23764             this.selectRow(ds.indexOf(records[i]), true);
23765         }
23766     },
23767
23768     /**
23769      * Gets the number of selected rows.
23770      * @return {Number}
23771      */
23772     getCount : function(){
23773         return this.selections.length;
23774     },
23775
23776     /**
23777      * Selects the first row in the grid.
23778      */
23779     selectFirstRow : function(){
23780         this.selectRow(0);
23781     },
23782
23783     /**
23784      * Select the last row.
23785      * @param {Boolean} keepExisting (optional) True to keep existing selections
23786      */
23787     selectLastRow : function(keepExisting){
23788         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23789         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23790     },
23791
23792     /**
23793      * Selects the row immediately following the last selected row.
23794      * @param {Boolean} keepExisting (optional) True to keep existing selections
23795      */
23796     selectNext : function(keepExisting)
23797     {
23798             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23799             this.selectRow(this.last+1, keepExisting);
23800             this.grid.getView().focusRow(this.last);
23801         }
23802     },
23803
23804     /**
23805      * Selects the row that precedes the last selected row.
23806      * @param {Boolean} keepExisting (optional) True to keep existing selections
23807      */
23808     selectPrevious : function(keepExisting){
23809         if(this.last){
23810             this.selectRow(this.last-1, keepExisting);
23811             this.grid.getView().focusRow(this.last);
23812         }
23813     },
23814
23815     /**
23816      * Returns the selected records
23817      * @return {Array} Array of selected records
23818      */
23819     getSelections : function(){
23820         return [].concat(this.selections.items);
23821     },
23822
23823     /**
23824      * Returns the first selected record.
23825      * @return {Record}
23826      */
23827     getSelected : function(){
23828         return this.selections.itemAt(0);
23829     },
23830
23831
23832     /**
23833      * Clears all selections.
23834      */
23835     clearSelections : function(fast)
23836     {
23837         if(this.locked) {
23838             return;
23839         }
23840         if(fast !== true){
23841                 var ds = this.grid.store;
23842             var s = this.selections;
23843             s.each(function(r){
23844                 this.deselectRow(ds.indexOfId(r.id));
23845             }, this);
23846             s.clear();
23847         }else{
23848             this.selections.clear();
23849         }
23850         this.last = false;
23851     },
23852
23853
23854     /**
23855      * Selects all rows.
23856      */
23857     selectAll : function(){
23858         if(this.locked) {
23859             return;
23860         }
23861         this.selections.clear();
23862         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23863             this.selectRow(i, true);
23864         }
23865     },
23866
23867     /**
23868      * Returns True if there is a selection.
23869      * @return {Boolean}
23870      */
23871     hasSelection : function(){
23872         return this.selections.length > 0;
23873     },
23874
23875     /**
23876      * Returns True if the specified row is selected.
23877      * @param {Number/Record} record The record or index of the record to check
23878      * @return {Boolean}
23879      */
23880     isSelected : function(index){
23881             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23882         return (r && this.selections.key(r.id) ? true : false);
23883     },
23884
23885     /**
23886      * Returns True if the specified record id is selected.
23887      * @param {String} id The id of record to check
23888      * @return {Boolean}
23889      */
23890     isIdSelected : function(id){
23891         return (this.selections.key(id) ? true : false);
23892     },
23893
23894
23895     // private
23896     handleMouseDBClick : function(e, t){
23897         
23898     },
23899     // private
23900     handleMouseDown : function(e, t)
23901     {
23902             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23903         if(this.isLocked() || rowIndex < 0 ){
23904             return;
23905         };
23906         if(e.shiftKey && this.last !== false){
23907             var last = this.last;
23908             this.selectRange(last, rowIndex, e.ctrlKey);
23909             this.last = last; // reset the last
23910             t.focus();
23911     
23912         }else{
23913             var isSelected = this.isSelected(rowIndex);
23914             //Roo.log("select row:" + rowIndex);
23915             if(isSelected){
23916                 this.deselectRow(rowIndex);
23917             } else {
23918                         this.selectRow(rowIndex, true);
23919             }
23920     
23921             /*
23922                 if(e.button !== 0 && isSelected){
23923                 alert('rowIndex 2: ' + rowIndex);
23924                     view.focusRow(rowIndex);
23925                 }else if(e.ctrlKey && isSelected){
23926                     this.deselectRow(rowIndex);
23927                 }else if(!isSelected){
23928                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23929                     view.focusRow(rowIndex);
23930                 }
23931             */
23932         }
23933         this.fireEvent("afterselectionchange", this);
23934     },
23935     // private
23936     handleDragableRowClick :  function(grid, rowIndex, e) 
23937     {
23938         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23939             this.selectRow(rowIndex, false);
23940             grid.view.focusRow(rowIndex);
23941              this.fireEvent("afterselectionchange", this);
23942         }
23943     },
23944     
23945     /**
23946      * Selects multiple rows.
23947      * @param {Array} rows Array of the indexes of the row to select
23948      * @param {Boolean} keepExisting (optional) True to keep existing selections
23949      */
23950     selectRows : function(rows, keepExisting){
23951         if(!keepExisting){
23952             this.clearSelections();
23953         }
23954         for(var i = 0, len = rows.length; i < len; i++){
23955             this.selectRow(rows[i], true);
23956         }
23957     },
23958
23959     /**
23960      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23961      * @param {Number} startRow The index of the first row in the range
23962      * @param {Number} endRow The index of the last row in the range
23963      * @param {Boolean} keepExisting (optional) True to retain existing selections
23964      */
23965     selectRange : function(startRow, endRow, keepExisting){
23966         if(this.locked) {
23967             return;
23968         }
23969         if(!keepExisting){
23970             this.clearSelections();
23971         }
23972         if(startRow <= endRow){
23973             for(var i = startRow; i <= endRow; i++){
23974                 this.selectRow(i, true);
23975             }
23976         }else{
23977             for(var i = startRow; i >= endRow; i--){
23978                 this.selectRow(i, true);
23979             }
23980         }
23981     },
23982
23983     /**
23984      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23985      * @param {Number} startRow The index of the first row in the range
23986      * @param {Number} endRow The index of the last row in the range
23987      */
23988     deselectRange : function(startRow, endRow, preventViewNotify){
23989         if(this.locked) {
23990             return;
23991         }
23992         for(var i = startRow; i <= endRow; i++){
23993             this.deselectRow(i, preventViewNotify);
23994         }
23995     },
23996
23997     /**
23998      * Selects a row.
23999      * @param {Number} row The index of the row to select
24000      * @param {Boolean} keepExisting (optional) True to keep existing selections
24001      */
24002     selectRow : function(index, keepExisting, preventViewNotify)
24003     {
24004             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24005             return;
24006         }
24007         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24008             if(!keepExisting || this.singleSelect){
24009                 this.clearSelections();
24010             }
24011             
24012             var r = this.grid.store.getAt(index);
24013             //console.log('selectRow - record id :' + r.id);
24014             
24015             this.selections.add(r);
24016             this.last = this.lastActive = index;
24017             if(!preventViewNotify){
24018                 var proxy = new Roo.Element(
24019                                 this.grid.getRowDom(index)
24020                 );
24021                 proxy.addClass('bg-info info');
24022             }
24023             this.fireEvent("rowselect", this, index, r);
24024             this.fireEvent("selectionchange", this);
24025         }
24026     },
24027
24028     /**
24029      * Deselects a row.
24030      * @param {Number} row The index of the row to deselect
24031      */
24032     deselectRow : function(index, preventViewNotify)
24033     {
24034         if(this.locked) {
24035             return;
24036         }
24037         if(this.last == index){
24038             this.last = false;
24039         }
24040         if(this.lastActive == index){
24041             this.lastActive = false;
24042         }
24043         
24044         var r = this.grid.store.getAt(index);
24045         if (!r) {
24046             return;
24047         }
24048         
24049         this.selections.remove(r);
24050         //.console.log('deselectRow - record id :' + r.id);
24051         if(!preventViewNotify){
24052         
24053             var proxy = new Roo.Element(
24054                 this.grid.getRowDom(index)
24055             );
24056             proxy.removeClass('bg-info info');
24057         }
24058         this.fireEvent("rowdeselect", this, index);
24059         this.fireEvent("selectionchange", this);
24060     },
24061
24062     // private
24063     restoreLast : function(){
24064         if(this._last){
24065             this.last = this._last;
24066         }
24067     },
24068
24069     // private
24070     acceptsNav : function(row, col, cm){
24071         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24072     },
24073
24074     // private
24075     onEditorKey : function(field, e){
24076         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24077         if(k == e.TAB){
24078             e.stopEvent();
24079             ed.completeEdit();
24080             if(e.shiftKey){
24081                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24082             }else{
24083                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24084             }
24085         }else if(k == e.ENTER && !e.ctrlKey){
24086             e.stopEvent();
24087             ed.completeEdit();
24088             if(e.shiftKey){
24089                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24090             }else{
24091                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24092             }
24093         }else if(k == e.ESC){
24094             ed.cancelEdit();
24095         }
24096         if(newCell){
24097             g.startEditing(newCell[0], newCell[1]);
24098         }
24099     }
24100 });
24101 /*
24102  * Based on:
24103  * Ext JS Library 1.1.1
24104  * Copyright(c) 2006-2007, Ext JS, LLC.
24105  *
24106  * Originally Released Under LGPL - original licence link has changed is not relivant.
24107  *
24108  * Fork - LGPL
24109  * <script type="text/javascript">
24110  */
24111  
24112 /**
24113  * @class Roo.bootstrap.PagingToolbar
24114  * @extends Roo.bootstrap.NavSimplebar
24115  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24116  * @constructor
24117  * Create a new PagingToolbar
24118  * @param {Object} config The config object
24119  * @param {Roo.data.Store} store
24120  */
24121 Roo.bootstrap.PagingToolbar = function(config)
24122 {
24123     // old args format still supported... - xtype is prefered..
24124         // created from xtype...
24125     
24126     this.ds = config.dataSource;
24127     
24128     if (config.store && !this.ds) {
24129         this.store= Roo.factory(config.store, Roo.data);
24130         this.ds = this.store;
24131         this.ds.xmodule = this.xmodule || false;
24132     }
24133     
24134     this.toolbarItems = [];
24135     if (config.items) {
24136         this.toolbarItems = config.items;
24137     }
24138     
24139     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24140     
24141     this.cursor = 0;
24142     
24143     if (this.ds) { 
24144         this.bind(this.ds);
24145     }
24146     
24147     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24148     
24149 };
24150
24151 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24152     /**
24153      * @cfg {Roo.data.Store} dataSource
24154      * The underlying data store providing the paged data
24155      */
24156     /**
24157      * @cfg {String/HTMLElement/Element} container
24158      * container The id or element that will contain the toolbar
24159      */
24160     /**
24161      * @cfg {Boolean} displayInfo
24162      * True to display the displayMsg (defaults to false)
24163      */
24164     /**
24165      * @cfg {Number} pageSize
24166      * The number of records to display per page (defaults to 20)
24167      */
24168     pageSize: 20,
24169     /**
24170      * @cfg {String} displayMsg
24171      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24172      */
24173     displayMsg : 'Displaying {0} - {1} of {2}',
24174     /**
24175      * @cfg {String} emptyMsg
24176      * The message to display when no records are found (defaults to "No data to display")
24177      */
24178     emptyMsg : 'No data to display',
24179     /**
24180      * Customizable piece of the default paging text (defaults to "Page")
24181      * @type String
24182      */
24183     beforePageText : "Page",
24184     /**
24185      * Customizable piece of the default paging text (defaults to "of %0")
24186      * @type String
24187      */
24188     afterPageText : "of {0}",
24189     /**
24190      * Customizable piece of the default paging text (defaults to "First Page")
24191      * @type String
24192      */
24193     firstText : "First Page",
24194     /**
24195      * Customizable piece of the default paging text (defaults to "Previous Page")
24196      * @type String
24197      */
24198     prevText : "Previous Page",
24199     /**
24200      * Customizable piece of the default paging text (defaults to "Next Page")
24201      * @type String
24202      */
24203     nextText : "Next Page",
24204     /**
24205      * Customizable piece of the default paging text (defaults to "Last Page")
24206      * @type String
24207      */
24208     lastText : "Last Page",
24209     /**
24210      * Customizable piece of the default paging text (defaults to "Refresh")
24211      * @type String
24212      */
24213     refreshText : "Refresh",
24214
24215     buttons : false,
24216     // private
24217     onRender : function(ct, position) 
24218     {
24219         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24220         this.navgroup.parentId = this.id;
24221         this.navgroup.onRender(this.el, null);
24222         // add the buttons to the navgroup
24223         
24224         if(this.displayInfo){
24225             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24226             this.displayEl = this.el.select('.x-paging-info', true).first();
24227 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24228 //            this.displayEl = navel.el.select('span',true).first();
24229         }
24230         
24231         var _this = this;
24232         
24233         if(this.buttons){
24234             Roo.each(_this.buttons, function(e){ // this might need to use render????
24235                Roo.factory(e).onRender(_this.el, null);
24236             });
24237         }
24238             
24239         Roo.each(_this.toolbarItems, function(e) {
24240             _this.navgroup.addItem(e);
24241         });
24242         
24243         
24244         this.first = this.navgroup.addItem({
24245             tooltip: this.firstText,
24246             cls: "prev",
24247             icon : 'fa fa-backward',
24248             disabled: true,
24249             preventDefault: true,
24250             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24251         });
24252         
24253         this.prev =  this.navgroup.addItem({
24254             tooltip: this.prevText,
24255             cls: "prev",
24256             icon : 'fa fa-step-backward',
24257             disabled: true,
24258             preventDefault: true,
24259             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24260         });
24261     //this.addSeparator();
24262         
24263         
24264         var field = this.navgroup.addItem( {
24265             tagtype : 'span',
24266             cls : 'x-paging-position',
24267             
24268             html : this.beforePageText  +
24269                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24270                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24271          } ); //?? escaped?
24272         
24273         this.field = field.el.select('input', true).first();
24274         this.field.on("keydown", this.onPagingKeydown, this);
24275         this.field.on("focus", function(){this.dom.select();});
24276     
24277     
24278         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24279         //this.field.setHeight(18);
24280         //this.addSeparator();
24281         this.next = this.navgroup.addItem({
24282             tooltip: this.nextText,
24283             cls: "next",
24284             html : ' <i class="fa fa-step-forward">',
24285             disabled: true,
24286             preventDefault: true,
24287             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24288         });
24289         this.last = this.navgroup.addItem({
24290             tooltip: this.lastText,
24291             icon : 'fa fa-forward',
24292             cls: "next",
24293             disabled: true,
24294             preventDefault: true,
24295             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24296         });
24297     //this.addSeparator();
24298         this.loading = this.navgroup.addItem({
24299             tooltip: this.refreshText,
24300             icon: 'fa fa-refresh',
24301             preventDefault: true,
24302             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24303         });
24304         
24305     },
24306
24307     // private
24308     updateInfo : function(){
24309         if(this.displayEl){
24310             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24311             var msg = count == 0 ?
24312                 this.emptyMsg :
24313                 String.format(
24314                     this.displayMsg,
24315                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24316                 );
24317             this.displayEl.update(msg);
24318         }
24319     },
24320
24321     // private
24322     onLoad : function(ds, r, o)
24323     {
24324         this.cursor = o.params ? o.params.start : 0;
24325         var d = this.getPageData(),
24326             ap = d.activePage,
24327             ps = d.pages;
24328         
24329         
24330         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24331         this.field.dom.value = ap;
24332         this.first.setDisabled(ap == 1);
24333         this.prev.setDisabled(ap == 1);
24334         this.next.setDisabled(ap == ps);
24335         this.last.setDisabled(ap == ps);
24336         this.loading.enable();
24337         this.updateInfo();
24338     },
24339
24340     // private
24341     getPageData : function(){
24342         var total = this.ds.getTotalCount();
24343         return {
24344             total : total,
24345             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24346             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24347         };
24348     },
24349
24350     // private
24351     onLoadError : function(){
24352         this.loading.enable();
24353     },
24354
24355     // private
24356     onPagingKeydown : function(e){
24357         var k = e.getKey();
24358         var d = this.getPageData();
24359         if(k == e.RETURN){
24360             var v = this.field.dom.value, pageNum;
24361             if(!v || isNaN(pageNum = parseInt(v, 10))){
24362                 this.field.dom.value = d.activePage;
24363                 return;
24364             }
24365             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24366             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24367             e.stopEvent();
24368         }
24369         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))
24370         {
24371           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24372           this.field.dom.value = pageNum;
24373           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24374           e.stopEvent();
24375         }
24376         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24377         {
24378           var v = this.field.dom.value, pageNum; 
24379           var increment = (e.shiftKey) ? 10 : 1;
24380           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24381                 increment *= -1;
24382           }
24383           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24384             this.field.dom.value = d.activePage;
24385             return;
24386           }
24387           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24388           {
24389             this.field.dom.value = parseInt(v, 10) + increment;
24390             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24391             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24392           }
24393           e.stopEvent();
24394         }
24395     },
24396
24397     // private
24398     beforeLoad : function(){
24399         if(this.loading){
24400             this.loading.disable();
24401         }
24402     },
24403
24404     // private
24405     onClick : function(which){
24406         
24407         var ds = this.ds;
24408         if (!ds) {
24409             return;
24410         }
24411         
24412         switch(which){
24413             case "first":
24414                 ds.load({params:{start: 0, limit: this.pageSize}});
24415             break;
24416             case "prev":
24417                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24418             break;
24419             case "next":
24420                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24421             break;
24422             case "last":
24423                 var total = ds.getTotalCount();
24424                 var extra = total % this.pageSize;
24425                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24426                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24427             break;
24428             case "refresh":
24429                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24430             break;
24431         }
24432     },
24433
24434     /**
24435      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24436      * @param {Roo.data.Store} store The data store to unbind
24437      */
24438     unbind : function(ds){
24439         ds.un("beforeload", this.beforeLoad, this);
24440         ds.un("load", this.onLoad, this);
24441         ds.un("loadexception", this.onLoadError, this);
24442         ds.un("remove", this.updateInfo, this);
24443         ds.un("add", this.updateInfo, this);
24444         this.ds = undefined;
24445     },
24446
24447     /**
24448      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24449      * @param {Roo.data.Store} store The data store to bind
24450      */
24451     bind : function(ds){
24452         ds.on("beforeload", this.beforeLoad, this);
24453         ds.on("load", this.onLoad, this);
24454         ds.on("loadexception", this.onLoadError, this);
24455         ds.on("remove", this.updateInfo, this);
24456         ds.on("add", this.updateInfo, this);
24457         this.ds = ds;
24458     }
24459 });/*
24460  * - LGPL
24461  *
24462  * element
24463  * 
24464  */
24465
24466 /**
24467  * @class Roo.bootstrap.MessageBar
24468  * @extends Roo.bootstrap.Component
24469  * Bootstrap MessageBar class
24470  * @cfg {String} html contents of the MessageBar
24471  * @cfg {String} weight (info | success | warning | danger) default info
24472  * @cfg {String} beforeClass insert the bar before the given class
24473  * @cfg {Boolean} closable (true | false) default false
24474  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24475  * 
24476  * @constructor
24477  * Create a new Element
24478  * @param {Object} config The config object
24479  */
24480
24481 Roo.bootstrap.MessageBar = function(config){
24482     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24483 };
24484
24485 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24486     
24487     html: '',
24488     weight: 'info',
24489     closable: false,
24490     fixed: false,
24491     beforeClass: 'bootstrap-sticky-wrap',
24492     
24493     getAutoCreate : function(){
24494         
24495         var cfg = {
24496             tag: 'div',
24497             cls: 'alert alert-dismissable alert-' + this.weight,
24498             cn: [
24499                 {
24500                     tag: 'span',
24501                     cls: 'message',
24502                     html: this.html || ''
24503                 }
24504             ]
24505         };
24506         
24507         if(this.fixed){
24508             cfg.cls += ' alert-messages-fixed';
24509         }
24510         
24511         if(this.closable){
24512             cfg.cn.push({
24513                 tag: 'button',
24514                 cls: 'close',
24515                 html: 'x'
24516             });
24517         }
24518         
24519         return cfg;
24520     },
24521     
24522     onRender : function(ct, position)
24523     {
24524         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24525         
24526         if(!this.el){
24527             var cfg = Roo.apply({},  this.getAutoCreate());
24528             cfg.id = Roo.id();
24529             
24530             if (this.cls) {
24531                 cfg.cls += ' ' + this.cls;
24532             }
24533             if (this.style) {
24534                 cfg.style = this.style;
24535             }
24536             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24537             
24538             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24539         }
24540         
24541         this.el.select('>button.close').on('click', this.hide, this);
24542         
24543     },
24544     
24545     show : function()
24546     {
24547         if (!this.rendered) {
24548             this.render();
24549         }
24550         
24551         this.el.show();
24552         
24553         this.fireEvent('show', this);
24554         
24555     },
24556     
24557     hide : function()
24558     {
24559         if (!this.rendered) {
24560             this.render();
24561         }
24562         
24563         this.el.hide();
24564         
24565         this.fireEvent('hide', this);
24566     },
24567     
24568     update : function()
24569     {
24570 //        var e = this.el.dom.firstChild;
24571 //        
24572 //        if(this.closable){
24573 //            e = e.nextSibling;
24574 //        }
24575 //        
24576 //        e.data = this.html || '';
24577
24578         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24579     }
24580    
24581 });
24582
24583  
24584
24585      /*
24586  * - LGPL
24587  *
24588  * Graph
24589  * 
24590  */
24591
24592
24593 /**
24594  * @class Roo.bootstrap.Graph
24595  * @extends Roo.bootstrap.Component
24596  * Bootstrap Graph class
24597 > Prameters
24598  -sm {number} sm 4
24599  -md {number} md 5
24600  @cfg {String} graphtype  bar | vbar | pie
24601  @cfg {number} g_x coodinator | centre x (pie)
24602  @cfg {number} g_y coodinator | centre y (pie)
24603  @cfg {number} g_r radius (pie)
24604  @cfg {number} g_height height of the chart (respected by all elements in the set)
24605  @cfg {number} g_width width of the chart (respected by all elements in the set)
24606  @cfg {Object} title The title of the chart
24607     
24608  -{Array}  values
24609  -opts (object) options for the chart 
24610      o {
24611      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24612      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24613      o vgutter (number)
24614      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.
24615      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24616      o to
24617      o stretch (boolean)
24618      o }
24619  -opts (object) options for the pie
24620      o{
24621      o cut
24622      o startAngle (number)
24623      o endAngle (number)
24624      } 
24625  *
24626  * @constructor
24627  * Create a new Input
24628  * @param {Object} config The config object
24629  */
24630
24631 Roo.bootstrap.Graph = function(config){
24632     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24633     
24634     this.addEvents({
24635         // img events
24636         /**
24637          * @event click
24638          * The img click event for the img.
24639          * @param {Roo.EventObject} e
24640          */
24641         "click" : true
24642     });
24643 };
24644
24645 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24646     
24647     sm: 4,
24648     md: 5,
24649     graphtype: 'bar',
24650     g_height: 250,
24651     g_width: 400,
24652     g_x: 50,
24653     g_y: 50,
24654     g_r: 30,
24655     opts:{
24656         //g_colors: this.colors,
24657         g_type: 'soft',
24658         g_gutter: '20%'
24659
24660     },
24661     title : false,
24662
24663     getAutoCreate : function(){
24664         
24665         var cfg = {
24666             tag: 'div',
24667             html : null
24668         };
24669         
24670         
24671         return  cfg;
24672     },
24673
24674     onRender : function(ct,position){
24675         
24676         
24677         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24678         
24679         if (typeof(Raphael) == 'undefined') {
24680             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24681             return;
24682         }
24683         
24684         this.raphael = Raphael(this.el.dom);
24685         
24686                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24687                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24688                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24689                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24690                 /*
24691                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24692                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24693                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24694                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24695                 
24696                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24697                 r.barchart(330, 10, 300, 220, data1);
24698                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24699                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24700                 */
24701                 
24702                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24703                 // r.barchart(30, 30, 560, 250,  xdata, {
24704                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24705                 //     axis : "0 0 1 1",
24706                 //     axisxlabels :  xdata
24707                 //     //yvalues : cols,
24708                    
24709                 // });
24710 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24711 //        
24712 //        this.load(null,xdata,{
24713 //                axis : "0 0 1 1",
24714 //                axisxlabels :  xdata
24715 //                });
24716
24717     },
24718
24719     load : function(graphtype,xdata,opts)
24720     {
24721         this.raphael.clear();
24722         if(!graphtype) {
24723             graphtype = this.graphtype;
24724         }
24725         if(!opts){
24726             opts = this.opts;
24727         }
24728         var r = this.raphael,
24729             fin = function () {
24730                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24731             },
24732             fout = function () {
24733                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24734             },
24735             pfin = function() {
24736                 this.sector.stop();
24737                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24738
24739                 if (this.label) {
24740                     this.label[0].stop();
24741                     this.label[0].attr({ r: 7.5 });
24742                     this.label[1].attr({ "font-weight": 800 });
24743                 }
24744             },
24745             pfout = function() {
24746                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24747
24748                 if (this.label) {
24749                     this.label[0].animate({ r: 5 }, 500, "bounce");
24750                     this.label[1].attr({ "font-weight": 400 });
24751                 }
24752             };
24753
24754         switch(graphtype){
24755             case 'bar':
24756                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24757                 break;
24758             case 'hbar':
24759                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24760                 break;
24761             case 'pie':
24762 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24763 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24764 //            
24765                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24766                 
24767                 break;
24768
24769         }
24770         
24771         if(this.title){
24772             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24773         }
24774         
24775     },
24776     
24777     setTitle: function(o)
24778     {
24779         this.title = o;
24780     },
24781     
24782     initEvents: function() {
24783         
24784         if(!this.href){
24785             this.el.on('click', this.onClick, this);
24786         }
24787     },
24788     
24789     onClick : function(e)
24790     {
24791         Roo.log('img onclick');
24792         this.fireEvent('click', this, e);
24793     }
24794    
24795 });
24796
24797  
24798 /*
24799  * - LGPL
24800  *
24801  * numberBox
24802  * 
24803  */
24804 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24805
24806 /**
24807  * @class Roo.bootstrap.dash.NumberBox
24808  * @extends Roo.bootstrap.Component
24809  * Bootstrap NumberBox class
24810  * @cfg {String} headline Box headline
24811  * @cfg {String} content Box content
24812  * @cfg {String} icon Box icon
24813  * @cfg {String} footer Footer text
24814  * @cfg {String} fhref Footer href
24815  * 
24816  * @constructor
24817  * Create a new NumberBox
24818  * @param {Object} config The config object
24819  */
24820
24821
24822 Roo.bootstrap.dash.NumberBox = function(config){
24823     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24824     
24825 };
24826
24827 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24828     
24829     headline : '',
24830     content : '',
24831     icon : '',
24832     footer : '',
24833     fhref : '',
24834     ficon : '',
24835     
24836     getAutoCreate : function(){
24837         
24838         var cfg = {
24839             tag : 'div',
24840             cls : 'small-box ',
24841             cn : [
24842                 {
24843                     tag : 'div',
24844                     cls : 'inner',
24845                     cn :[
24846                         {
24847                             tag : 'h3',
24848                             cls : 'roo-headline',
24849                             html : this.headline
24850                         },
24851                         {
24852                             tag : 'p',
24853                             cls : 'roo-content',
24854                             html : this.content
24855                         }
24856                     ]
24857                 }
24858             ]
24859         };
24860         
24861         if(this.icon){
24862             cfg.cn.push({
24863                 tag : 'div',
24864                 cls : 'icon',
24865                 cn :[
24866                     {
24867                         tag : 'i',
24868                         cls : 'ion ' + this.icon
24869                     }
24870                 ]
24871             });
24872         }
24873         
24874         if(this.footer){
24875             var footer = {
24876                 tag : 'a',
24877                 cls : 'small-box-footer',
24878                 href : this.fhref || '#',
24879                 html : this.footer
24880             };
24881             
24882             cfg.cn.push(footer);
24883             
24884         }
24885         
24886         return  cfg;
24887     },
24888
24889     onRender : function(ct,position){
24890         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24891
24892
24893        
24894                 
24895     },
24896
24897     setHeadline: function (value)
24898     {
24899         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24900     },
24901     
24902     setFooter: function (value, href)
24903     {
24904         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24905         
24906         if(href){
24907             this.el.select('a.small-box-footer',true).first().attr('href', href);
24908         }
24909         
24910     },
24911
24912     setContent: function (value)
24913     {
24914         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24915     },
24916
24917     initEvents: function() 
24918     {   
24919         
24920     }
24921     
24922 });
24923
24924  
24925 /*
24926  * - LGPL
24927  *
24928  * TabBox
24929  * 
24930  */
24931 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24932
24933 /**
24934  * @class Roo.bootstrap.dash.TabBox
24935  * @extends Roo.bootstrap.Component
24936  * Bootstrap TabBox class
24937  * @cfg {String} title Title of the TabBox
24938  * @cfg {String} icon Icon of the TabBox
24939  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24940  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24941  * 
24942  * @constructor
24943  * Create a new TabBox
24944  * @param {Object} config The config object
24945  */
24946
24947
24948 Roo.bootstrap.dash.TabBox = function(config){
24949     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24950     this.addEvents({
24951         // raw events
24952         /**
24953          * @event addpane
24954          * When a pane is added
24955          * @param {Roo.bootstrap.dash.TabPane} pane
24956          */
24957         "addpane" : true,
24958         /**
24959          * @event activatepane
24960          * When a pane is activated
24961          * @param {Roo.bootstrap.dash.TabPane} pane
24962          */
24963         "activatepane" : true
24964         
24965          
24966     });
24967     
24968     this.panes = [];
24969 };
24970
24971 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24972
24973     title : '',
24974     icon : false,
24975     showtabs : true,
24976     tabScrollable : false,
24977     
24978     getChildContainer : function()
24979     {
24980         return this.el.select('.tab-content', true).first();
24981     },
24982     
24983     getAutoCreate : function(){
24984         
24985         var header = {
24986             tag: 'li',
24987             cls: 'pull-left header',
24988             html: this.title,
24989             cn : []
24990         };
24991         
24992         if(this.icon){
24993             header.cn.push({
24994                 tag: 'i',
24995                 cls: 'fa ' + this.icon
24996             });
24997         }
24998         
24999         var h = {
25000             tag: 'ul',
25001             cls: 'nav nav-tabs pull-right',
25002             cn: [
25003                 header
25004             ]
25005         };
25006         
25007         if(this.tabScrollable){
25008             h = {
25009                 tag: 'div',
25010                 cls: 'tab-header',
25011                 cn: [
25012                     {
25013                         tag: 'ul',
25014                         cls: 'nav nav-tabs pull-right',
25015                         cn: [
25016                             header
25017                         ]
25018                     }
25019                 ]
25020             };
25021         }
25022         
25023         var cfg = {
25024             tag: 'div',
25025             cls: 'nav-tabs-custom',
25026             cn: [
25027                 h,
25028                 {
25029                     tag: 'div',
25030                     cls: 'tab-content no-padding',
25031                     cn: []
25032                 }
25033             ]
25034         };
25035
25036         return  cfg;
25037     },
25038     initEvents : function()
25039     {
25040         //Roo.log('add add pane handler');
25041         this.on('addpane', this.onAddPane, this);
25042     },
25043      /**
25044      * Updates the box title
25045      * @param {String} html to set the title to.
25046      */
25047     setTitle : function(value)
25048     {
25049         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25050     },
25051     onAddPane : function(pane)
25052     {
25053         this.panes.push(pane);
25054         //Roo.log('addpane');
25055         //Roo.log(pane);
25056         // tabs are rendere left to right..
25057         if(!this.showtabs){
25058             return;
25059         }
25060         
25061         var ctr = this.el.select('.nav-tabs', true).first();
25062          
25063          
25064         var existing = ctr.select('.nav-tab',true);
25065         var qty = existing.getCount();;
25066         
25067         
25068         var tab = ctr.createChild({
25069             tag : 'li',
25070             cls : 'nav-tab' + (qty ? '' : ' active'),
25071             cn : [
25072                 {
25073                     tag : 'a',
25074                     href:'#',
25075                     html : pane.title
25076                 }
25077             ]
25078         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25079         pane.tab = tab;
25080         
25081         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25082         if (!qty) {
25083             pane.el.addClass('active');
25084         }
25085         
25086                 
25087     },
25088     onTabClick : function(ev,un,ob,pane)
25089     {
25090         //Roo.log('tab - prev default');
25091         ev.preventDefault();
25092         
25093         
25094         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25095         pane.tab.addClass('active');
25096         //Roo.log(pane.title);
25097         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25098         // technically we should have a deactivate event.. but maybe add later.
25099         // and it should not de-activate the selected tab...
25100         this.fireEvent('activatepane', pane);
25101         pane.el.addClass('active');
25102         pane.fireEvent('activate');
25103         
25104         
25105     },
25106     
25107     getActivePane : function()
25108     {
25109         var r = false;
25110         Roo.each(this.panes, function(p) {
25111             if(p.el.hasClass('active')){
25112                 r = p;
25113                 return false;
25114             }
25115             
25116             return;
25117         });
25118         
25119         return r;
25120     }
25121     
25122     
25123 });
25124
25125  
25126 /*
25127  * - LGPL
25128  *
25129  * Tab pane
25130  * 
25131  */
25132 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25133 /**
25134  * @class Roo.bootstrap.TabPane
25135  * @extends Roo.bootstrap.Component
25136  * Bootstrap TabPane class
25137  * @cfg {Boolean} active (false | true) Default false
25138  * @cfg {String} title title of panel
25139
25140  * 
25141  * @constructor
25142  * Create a new TabPane
25143  * @param {Object} config The config object
25144  */
25145
25146 Roo.bootstrap.dash.TabPane = function(config){
25147     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25148     
25149     this.addEvents({
25150         // raw events
25151         /**
25152          * @event activate
25153          * When a pane is activated
25154          * @param {Roo.bootstrap.dash.TabPane} pane
25155          */
25156         "activate" : true
25157          
25158     });
25159 };
25160
25161 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25162     
25163     active : false,
25164     title : '',
25165     
25166     // the tabBox that this is attached to.
25167     tab : false,
25168      
25169     getAutoCreate : function() 
25170     {
25171         var cfg = {
25172             tag: 'div',
25173             cls: 'tab-pane'
25174         };
25175         
25176         if(this.active){
25177             cfg.cls += ' active';
25178         }
25179         
25180         return cfg;
25181     },
25182     initEvents  : function()
25183     {
25184         //Roo.log('trigger add pane handler');
25185         this.parent().fireEvent('addpane', this)
25186     },
25187     
25188      /**
25189      * Updates the tab title 
25190      * @param {String} html to set the title to.
25191      */
25192     setTitle: function(str)
25193     {
25194         if (!this.tab) {
25195             return;
25196         }
25197         this.title = str;
25198         this.tab.select('a', true).first().dom.innerHTML = str;
25199         
25200     }
25201     
25202     
25203     
25204 });
25205
25206  
25207
25208
25209  /*
25210  * - LGPL
25211  *
25212  * menu
25213  * 
25214  */
25215 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25216
25217 /**
25218  * @class Roo.bootstrap.menu.Menu
25219  * @extends Roo.bootstrap.Component
25220  * Bootstrap Menu class - container for Menu
25221  * @cfg {String} html Text of the menu
25222  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25223  * @cfg {String} icon Font awesome icon
25224  * @cfg {String} pos Menu align to (top | bottom) default bottom
25225  * 
25226  * 
25227  * @constructor
25228  * Create a new Menu
25229  * @param {Object} config The config object
25230  */
25231
25232
25233 Roo.bootstrap.menu.Menu = function(config){
25234     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25235     
25236     this.addEvents({
25237         /**
25238          * @event beforeshow
25239          * Fires before this menu is displayed
25240          * @param {Roo.bootstrap.menu.Menu} this
25241          */
25242         beforeshow : true,
25243         /**
25244          * @event beforehide
25245          * Fires before this menu is hidden
25246          * @param {Roo.bootstrap.menu.Menu} this
25247          */
25248         beforehide : true,
25249         /**
25250          * @event show
25251          * Fires after this menu is displayed
25252          * @param {Roo.bootstrap.menu.Menu} this
25253          */
25254         show : true,
25255         /**
25256          * @event hide
25257          * Fires after this menu is hidden
25258          * @param {Roo.bootstrap.menu.Menu} this
25259          */
25260         hide : true,
25261         /**
25262          * @event click
25263          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25264          * @param {Roo.bootstrap.menu.Menu} this
25265          * @param {Roo.EventObject} e
25266          */
25267         click : true
25268     });
25269     
25270 };
25271
25272 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25273     
25274     submenu : false,
25275     html : '',
25276     weight : 'default',
25277     icon : false,
25278     pos : 'bottom',
25279     
25280     
25281     getChildContainer : function() {
25282         if(this.isSubMenu){
25283             return this.el;
25284         }
25285         
25286         return this.el.select('ul.dropdown-menu', true).first();  
25287     },
25288     
25289     getAutoCreate : function()
25290     {
25291         var text = [
25292             {
25293                 tag : 'span',
25294                 cls : 'roo-menu-text',
25295                 html : this.html
25296             }
25297         ];
25298         
25299         if(this.icon){
25300             text.unshift({
25301                 tag : 'i',
25302                 cls : 'fa ' + this.icon
25303             })
25304         }
25305         
25306         
25307         var cfg = {
25308             tag : 'div',
25309             cls : 'btn-group',
25310             cn : [
25311                 {
25312                     tag : 'button',
25313                     cls : 'dropdown-button btn btn-' + this.weight,
25314                     cn : text
25315                 },
25316                 {
25317                     tag : 'button',
25318                     cls : 'dropdown-toggle btn btn-' + this.weight,
25319                     cn : [
25320                         {
25321                             tag : 'span',
25322                             cls : 'caret'
25323                         }
25324                     ]
25325                 },
25326                 {
25327                     tag : 'ul',
25328                     cls : 'dropdown-menu'
25329                 }
25330             ]
25331             
25332         };
25333         
25334         if(this.pos == 'top'){
25335             cfg.cls += ' dropup';
25336         }
25337         
25338         if(this.isSubMenu){
25339             cfg = {
25340                 tag : 'ul',
25341                 cls : 'dropdown-menu'
25342             }
25343         }
25344         
25345         return cfg;
25346     },
25347     
25348     onRender : function(ct, position)
25349     {
25350         this.isSubMenu = ct.hasClass('dropdown-submenu');
25351         
25352         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25353     },
25354     
25355     initEvents : function() 
25356     {
25357         if(this.isSubMenu){
25358             return;
25359         }
25360         
25361         this.hidden = true;
25362         
25363         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25364         this.triggerEl.on('click', this.onTriggerPress, this);
25365         
25366         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25367         this.buttonEl.on('click', this.onClick, this);
25368         
25369     },
25370     
25371     list : function()
25372     {
25373         if(this.isSubMenu){
25374             return this.el;
25375         }
25376         
25377         return this.el.select('ul.dropdown-menu', true).first();
25378     },
25379     
25380     onClick : function(e)
25381     {
25382         this.fireEvent("click", this, e);
25383     },
25384     
25385     onTriggerPress  : function(e)
25386     {   
25387         if (this.isVisible()) {
25388             this.hide();
25389         } else {
25390             this.show();
25391         }
25392     },
25393     
25394     isVisible : function(){
25395         return !this.hidden;
25396     },
25397     
25398     show : function()
25399     {
25400         this.fireEvent("beforeshow", this);
25401         
25402         this.hidden = false;
25403         this.el.addClass('open');
25404         
25405         Roo.get(document).on("mouseup", this.onMouseUp, this);
25406         
25407         this.fireEvent("show", this);
25408         
25409         
25410     },
25411     
25412     hide : function()
25413     {
25414         this.fireEvent("beforehide", this);
25415         
25416         this.hidden = true;
25417         this.el.removeClass('open');
25418         
25419         Roo.get(document).un("mouseup", this.onMouseUp);
25420         
25421         this.fireEvent("hide", this);
25422     },
25423     
25424     onMouseUp : function()
25425     {
25426         this.hide();
25427     }
25428     
25429 });
25430
25431  
25432  /*
25433  * - LGPL
25434  *
25435  * menu item
25436  * 
25437  */
25438 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25439
25440 /**
25441  * @class Roo.bootstrap.menu.Item
25442  * @extends Roo.bootstrap.Component
25443  * Bootstrap MenuItem class
25444  * @cfg {Boolean} submenu (true | false) default false
25445  * @cfg {String} html text of the item
25446  * @cfg {String} href the link
25447  * @cfg {Boolean} disable (true | false) default false
25448  * @cfg {Boolean} preventDefault (true | false) default true
25449  * @cfg {String} icon Font awesome icon
25450  * @cfg {String} pos Submenu align to (left | right) default right 
25451  * 
25452  * 
25453  * @constructor
25454  * Create a new Item
25455  * @param {Object} config The config object
25456  */
25457
25458
25459 Roo.bootstrap.menu.Item = function(config){
25460     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25461     this.addEvents({
25462         /**
25463          * @event mouseover
25464          * Fires when the mouse is hovering over this menu
25465          * @param {Roo.bootstrap.menu.Item} this
25466          * @param {Roo.EventObject} e
25467          */
25468         mouseover : true,
25469         /**
25470          * @event mouseout
25471          * Fires when the mouse exits this menu
25472          * @param {Roo.bootstrap.menu.Item} this
25473          * @param {Roo.EventObject} e
25474          */
25475         mouseout : true,
25476         // raw events
25477         /**
25478          * @event click
25479          * The raw click event for the entire grid.
25480          * @param {Roo.EventObject} e
25481          */
25482         click : true
25483     });
25484 };
25485
25486 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25487     
25488     submenu : false,
25489     href : '',
25490     html : '',
25491     preventDefault: true,
25492     disable : false,
25493     icon : false,
25494     pos : 'right',
25495     
25496     getAutoCreate : function()
25497     {
25498         var text = [
25499             {
25500                 tag : 'span',
25501                 cls : 'roo-menu-item-text',
25502                 html : this.html
25503             }
25504         ];
25505         
25506         if(this.icon){
25507             text.unshift({
25508                 tag : 'i',
25509                 cls : 'fa ' + this.icon
25510             })
25511         }
25512         
25513         var cfg = {
25514             tag : 'li',
25515             cn : [
25516                 {
25517                     tag : 'a',
25518                     href : this.href || '#',
25519                     cn : text
25520                 }
25521             ]
25522         };
25523         
25524         if(this.disable){
25525             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25526         }
25527         
25528         if(this.submenu){
25529             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25530             
25531             if(this.pos == 'left'){
25532                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25533             }
25534         }
25535         
25536         return cfg;
25537     },
25538     
25539     initEvents : function() 
25540     {
25541         this.el.on('mouseover', this.onMouseOver, this);
25542         this.el.on('mouseout', this.onMouseOut, this);
25543         
25544         this.el.select('a', true).first().on('click', this.onClick, this);
25545         
25546     },
25547     
25548     onClick : function(e)
25549     {
25550         if(this.preventDefault){
25551             e.preventDefault();
25552         }
25553         
25554         this.fireEvent("click", this, e);
25555     },
25556     
25557     onMouseOver : function(e)
25558     {
25559         if(this.submenu && this.pos == 'left'){
25560             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25561         }
25562         
25563         this.fireEvent("mouseover", this, e);
25564     },
25565     
25566     onMouseOut : function(e)
25567     {
25568         this.fireEvent("mouseout", this, e);
25569     }
25570 });
25571
25572  
25573
25574  /*
25575  * - LGPL
25576  *
25577  * menu separator
25578  * 
25579  */
25580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25581
25582 /**
25583  * @class Roo.bootstrap.menu.Separator
25584  * @extends Roo.bootstrap.Component
25585  * Bootstrap Separator class
25586  * 
25587  * @constructor
25588  * Create a new Separator
25589  * @param {Object} config The config object
25590  */
25591
25592
25593 Roo.bootstrap.menu.Separator = function(config){
25594     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25595 };
25596
25597 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25598     
25599     getAutoCreate : function(){
25600         var cfg = {
25601             tag : 'li',
25602             cls: 'divider'
25603         };
25604         
25605         return cfg;
25606     }
25607    
25608 });
25609
25610  
25611
25612  /*
25613  * - LGPL
25614  *
25615  * Tooltip
25616  * 
25617  */
25618
25619 /**
25620  * @class Roo.bootstrap.Tooltip
25621  * Bootstrap Tooltip class
25622  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25623  * to determine which dom element triggers the tooltip.
25624  * 
25625  * It needs to add support for additional attributes like tooltip-position
25626  * 
25627  * @constructor
25628  * Create a new Toolti
25629  * @param {Object} config The config object
25630  */
25631
25632 Roo.bootstrap.Tooltip = function(config){
25633     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25634     
25635     this.alignment = Roo.bootstrap.Tooltip.alignment;
25636     
25637     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25638         this.alignment = config.alignment;
25639     }
25640     
25641 };
25642
25643 Roo.apply(Roo.bootstrap.Tooltip, {
25644     /**
25645      * @function init initialize tooltip monitoring.
25646      * @static
25647      */
25648     currentEl : false,
25649     currentTip : false,
25650     currentRegion : false,
25651     
25652     //  init : delay?
25653     
25654     init : function()
25655     {
25656         Roo.get(document).on('mouseover', this.enter ,this);
25657         Roo.get(document).on('mouseout', this.leave, this);
25658          
25659         
25660         this.currentTip = new Roo.bootstrap.Tooltip();
25661     },
25662     
25663     enter : function(ev)
25664     {
25665         var dom = ev.getTarget();
25666         
25667         //Roo.log(['enter',dom]);
25668         var el = Roo.fly(dom);
25669         if (this.currentEl) {
25670             //Roo.log(dom);
25671             //Roo.log(this.currentEl);
25672             //Roo.log(this.currentEl.contains(dom));
25673             if (this.currentEl == el) {
25674                 return;
25675             }
25676             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25677                 return;
25678             }
25679
25680         }
25681         
25682         if (this.currentTip.el) {
25683             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25684         }    
25685         //Roo.log(ev);
25686         
25687         if(!el || el.dom == document){
25688             return;
25689         }
25690         
25691         var bindEl = el;
25692         
25693         // you can not look for children, as if el is the body.. then everythign is the child..
25694         if (!el.attr('tooltip')) { //
25695             if (!el.select("[tooltip]").elements.length) {
25696                 return;
25697             }
25698             // is the mouse over this child...?
25699             bindEl = el.select("[tooltip]").first();
25700             var xy = ev.getXY();
25701             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25702                 //Roo.log("not in region.");
25703                 return;
25704             }
25705             //Roo.log("child element over..");
25706             
25707         }
25708         this.currentEl = bindEl;
25709         this.currentTip.bind(bindEl);
25710         this.currentRegion = Roo.lib.Region.getRegion(dom);
25711         this.currentTip.enter();
25712         
25713     },
25714     leave : function(ev)
25715     {
25716         var dom = ev.getTarget();
25717         //Roo.log(['leave',dom]);
25718         if (!this.currentEl) {
25719             return;
25720         }
25721         
25722         
25723         if (dom != this.currentEl.dom) {
25724             return;
25725         }
25726         var xy = ev.getXY();
25727         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25728             return;
25729         }
25730         // only activate leave if mouse cursor is outside... bounding box..
25731         
25732         
25733         
25734         
25735         if (this.currentTip) {
25736             this.currentTip.leave();
25737         }
25738         //Roo.log('clear currentEl');
25739         this.currentEl = false;
25740         
25741         
25742     },
25743     alignment : {
25744         'left' : ['r-l', [-2,0], 'right'],
25745         'right' : ['l-r', [2,0], 'left'],
25746         'bottom' : ['t-b', [0,2], 'top'],
25747         'top' : [ 'b-t', [0,-2], 'bottom']
25748     }
25749     
25750 });
25751
25752
25753 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25754     
25755     
25756     bindEl : false,
25757     
25758     delay : null, // can be { show : 300 , hide: 500}
25759     
25760     timeout : null,
25761     
25762     hoverState : null, //???
25763     
25764     placement : 'bottom', 
25765     
25766     alignment : false,
25767     
25768     getAutoCreate : function(){
25769     
25770         var cfg = {
25771            cls : 'tooltip',
25772            role : 'tooltip',
25773            cn : [
25774                 {
25775                     cls : 'tooltip-arrow'
25776                 },
25777                 {
25778                     cls : 'tooltip-inner'
25779                 }
25780            ]
25781         };
25782         
25783         return cfg;
25784     },
25785     bind : function(el)
25786     {
25787         this.bindEl = el;
25788     },
25789       
25790     
25791     enter : function () {
25792        
25793         if (this.timeout != null) {
25794             clearTimeout(this.timeout);
25795         }
25796         
25797         this.hoverState = 'in';
25798          //Roo.log("enter - show");
25799         if (!this.delay || !this.delay.show) {
25800             this.show();
25801             return;
25802         }
25803         var _t = this;
25804         this.timeout = setTimeout(function () {
25805             if (_t.hoverState == 'in') {
25806                 _t.show();
25807             }
25808         }, this.delay.show);
25809     },
25810     leave : function()
25811     {
25812         clearTimeout(this.timeout);
25813     
25814         this.hoverState = 'out';
25815          if (!this.delay || !this.delay.hide) {
25816             this.hide();
25817             return;
25818         }
25819        
25820         var _t = this;
25821         this.timeout = setTimeout(function () {
25822             //Roo.log("leave - timeout");
25823             
25824             if (_t.hoverState == 'out') {
25825                 _t.hide();
25826                 Roo.bootstrap.Tooltip.currentEl = false;
25827             }
25828         }, delay);
25829     },
25830     
25831     show : function (msg)
25832     {
25833         if (!this.el) {
25834             this.render(document.body);
25835         }
25836         // set content.
25837         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25838         
25839         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25840         
25841         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25842         
25843         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25844         
25845         var placement = typeof this.placement == 'function' ?
25846             this.placement.call(this, this.el, on_el) :
25847             this.placement;
25848             
25849         var autoToken = /\s?auto?\s?/i;
25850         var autoPlace = autoToken.test(placement);
25851         if (autoPlace) {
25852             placement = placement.replace(autoToken, '') || 'top';
25853         }
25854         
25855         //this.el.detach()
25856         //this.el.setXY([0,0]);
25857         this.el.show();
25858         //this.el.dom.style.display='block';
25859         
25860         //this.el.appendTo(on_el);
25861         
25862         var p = this.getPosition();
25863         var box = this.el.getBox();
25864         
25865         if (autoPlace) {
25866             // fixme..
25867         }
25868         
25869         var align = this.alignment[placement];
25870         
25871         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25872         
25873         if(placement == 'top' || placement == 'bottom'){
25874             if(xy[0] < 0){
25875                 placement = 'right';
25876             }
25877             
25878             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25879                 placement = 'left';
25880             }
25881             
25882             var scroll = Roo.select('body', true).first().getScroll();
25883             
25884             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25885                 placement = 'top';
25886             }
25887             
25888         }
25889         
25890         this.el.alignTo(this.bindEl, align[0],align[1]);
25891         //var arrow = this.el.select('.arrow',true).first();
25892         //arrow.set(align[2], 
25893         
25894         this.el.addClass(placement);
25895         
25896         this.el.addClass('in fade');
25897         
25898         this.hoverState = null;
25899         
25900         if (this.el.hasClass('fade')) {
25901             // fade it?
25902         }
25903         
25904     },
25905     hide : function()
25906     {
25907          
25908         if (!this.el) {
25909             return;
25910         }
25911         //this.el.setXY([0,0]);
25912         this.el.removeClass('in');
25913         //this.el.hide();
25914         
25915     }
25916     
25917 });
25918  
25919
25920  /*
25921  * - LGPL
25922  *
25923  * Location Picker
25924  * 
25925  */
25926
25927 /**
25928  * @class Roo.bootstrap.LocationPicker
25929  * @extends Roo.bootstrap.Component
25930  * Bootstrap LocationPicker class
25931  * @cfg {Number} latitude Position when init default 0
25932  * @cfg {Number} longitude Position when init default 0
25933  * @cfg {Number} zoom default 15
25934  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25935  * @cfg {Boolean} mapTypeControl default false
25936  * @cfg {Boolean} disableDoubleClickZoom default false
25937  * @cfg {Boolean} scrollwheel default true
25938  * @cfg {Boolean} streetViewControl default false
25939  * @cfg {Number} radius default 0
25940  * @cfg {String} locationName
25941  * @cfg {Boolean} draggable default true
25942  * @cfg {Boolean} enableAutocomplete default false
25943  * @cfg {Boolean} enableReverseGeocode default true
25944  * @cfg {String} markerTitle
25945  * 
25946  * @constructor
25947  * Create a new LocationPicker
25948  * @param {Object} config The config object
25949  */
25950
25951
25952 Roo.bootstrap.LocationPicker = function(config){
25953     
25954     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25955     
25956     this.addEvents({
25957         /**
25958          * @event initial
25959          * Fires when the picker initialized.
25960          * @param {Roo.bootstrap.LocationPicker} this
25961          * @param {Google Location} location
25962          */
25963         initial : true,
25964         /**
25965          * @event positionchanged
25966          * Fires when the picker position changed.
25967          * @param {Roo.bootstrap.LocationPicker} this
25968          * @param {Google Location} location
25969          */
25970         positionchanged : true,
25971         /**
25972          * @event resize
25973          * Fires when the map resize.
25974          * @param {Roo.bootstrap.LocationPicker} this
25975          */
25976         resize : true,
25977         /**
25978          * @event show
25979          * Fires when the map show.
25980          * @param {Roo.bootstrap.LocationPicker} this
25981          */
25982         show : true,
25983         /**
25984          * @event hide
25985          * Fires when the map hide.
25986          * @param {Roo.bootstrap.LocationPicker} this
25987          */
25988         hide : true,
25989         /**
25990          * @event mapClick
25991          * Fires when click the map.
25992          * @param {Roo.bootstrap.LocationPicker} this
25993          * @param {Map event} e
25994          */
25995         mapClick : true,
25996         /**
25997          * @event mapRightClick
25998          * Fires when right click the map.
25999          * @param {Roo.bootstrap.LocationPicker} this
26000          * @param {Map event} e
26001          */
26002         mapRightClick : true,
26003         /**
26004          * @event markerClick
26005          * Fires when click the marker.
26006          * @param {Roo.bootstrap.LocationPicker} this
26007          * @param {Map event} e
26008          */
26009         markerClick : true,
26010         /**
26011          * @event markerRightClick
26012          * Fires when right click the marker.
26013          * @param {Roo.bootstrap.LocationPicker} this
26014          * @param {Map event} e
26015          */
26016         markerRightClick : true,
26017         /**
26018          * @event OverlayViewDraw
26019          * Fires when OverlayView Draw
26020          * @param {Roo.bootstrap.LocationPicker} this
26021          */
26022         OverlayViewDraw : true,
26023         /**
26024          * @event OverlayViewOnAdd
26025          * Fires when OverlayView Draw
26026          * @param {Roo.bootstrap.LocationPicker} this
26027          */
26028         OverlayViewOnAdd : true,
26029         /**
26030          * @event OverlayViewOnRemove
26031          * Fires when OverlayView Draw
26032          * @param {Roo.bootstrap.LocationPicker} this
26033          */
26034         OverlayViewOnRemove : true,
26035         /**
26036          * @event OverlayViewShow
26037          * Fires when OverlayView Draw
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          * @param {Pixel} cpx
26040          */
26041         OverlayViewShow : true,
26042         /**
26043          * @event OverlayViewHide
26044          * Fires when OverlayView Draw
26045          * @param {Roo.bootstrap.LocationPicker} this
26046          */
26047         OverlayViewHide : true,
26048         /**
26049          * @event loadexception
26050          * Fires when load google lib failed.
26051          * @param {Roo.bootstrap.LocationPicker} this
26052          */
26053         loadexception : true
26054     });
26055         
26056 };
26057
26058 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26059     
26060     gMapContext: false,
26061     
26062     latitude: 0,
26063     longitude: 0,
26064     zoom: 15,
26065     mapTypeId: false,
26066     mapTypeControl: false,
26067     disableDoubleClickZoom: false,
26068     scrollwheel: true,
26069     streetViewControl: false,
26070     radius: 0,
26071     locationName: '',
26072     draggable: true,
26073     enableAutocomplete: false,
26074     enableReverseGeocode: true,
26075     markerTitle: '',
26076     
26077     getAutoCreate: function()
26078     {
26079
26080         var cfg = {
26081             tag: 'div',
26082             cls: 'roo-location-picker'
26083         };
26084         
26085         return cfg
26086     },
26087     
26088     initEvents: function(ct, position)
26089     {       
26090         if(!this.el.getWidth() || this.isApplied()){
26091             return;
26092         }
26093         
26094         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26095         
26096         this.initial();
26097     },
26098     
26099     initial: function()
26100     {
26101         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26102             this.fireEvent('loadexception', this);
26103             return;
26104         }
26105         
26106         if(!this.mapTypeId){
26107             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26108         }
26109         
26110         this.gMapContext = this.GMapContext();
26111         
26112         this.initOverlayView();
26113         
26114         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26115         
26116         var _this = this;
26117                 
26118         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26119             _this.setPosition(_this.gMapContext.marker.position);
26120         });
26121         
26122         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26123             _this.fireEvent('mapClick', this, event);
26124             
26125         });
26126
26127         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26128             _this.fireEvent('mapRightClick', this, event);
26129             
26130         });
26131         
26132         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26133             _this.fireEvent('markerClick', this, event);
26134             
26135         });
26136
26137         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26138             _this.fireEvent('markerRightClick', this, event);
26139             
26140         });
26141         
26142         this.setPosition(this.gMapContext.location);
26143         
26144         this.fireEvent('initial', this, this.gMapContext.location);
26145     },
26146     
26147     initOverlayView: function()
26148     {
26149         var _this = this;
26150         
26151         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26152             
26153             draw: function()
26154             {
26155                 _this.fireEvent('OverlayViewDraw', _this);
26156             },
26157             
26158             onAdd: function()
26159             {
26160                 _this.fireEvent('OverlayViewOnAdd', _this);
26161             },
26162             
26163             onRemove: function()
26164             {
26165                 _this.fireEvent('OverlayViewOnRemove', _this);
26166             },
26167             
26168             show: function(cpx)
26169             {
26170                 _this.fireEvent('OverlayViewShow', _this, cpx);
26171             },
26172             
26173             hide: function()
26174             {
26175                 _this.fireEvent('OverlayViewHide', _this);
26176             }
26177             
26178         });
26179     },
26180     
26181     fromLatLngToContainerPixel: function(event)
26182     {
26183         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26184     },
26185     
26186     isApplied: function() 
26187     {
26188         return this.getGmapContext() == false ? false : true;
26189     },
26190     
26191     getGmapContext: function() 
26192     {
26193         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26194     },
26195     
26196     GMapContext: function() 
26197     {
26198         var position = new google.maps.LatLng(this.latitude, this.longitude);
26199         
26200         var _map = new google.maps.Map(this.el.dom, {
26201             center: position,
26202             zoom: this.zoom,
26203             mapTypeId: this.mapTypeId,
26204             mapTypeControl: this.mapTypeControl,
26205             disableDoubleClickZoom: this.disableDoubleClickZoom,
26206             scrollwheel: this.scrollwheel,
26207             streetViewControl: this.streetViewControl,
26208             locationName: this.locationName,
26209             draggable: this.draggable,
26210             enableAutocomplete: this.enableAutocomplete,
26211             enableReverseGeocode: this.enableReverseGeocode
26212         });
26213         
26214         var _marker = new google.maps.Marker({
26215             position: position,
26216             map: _map,
26217             title: this.markerTitle,
26218             draggable: this.draggable
26219         });
26220         
26221         return {
26222             map: _map,
26223             marker: _marker,
26224             circle: null,
26225             location: position,
26226             radius: this.radius,
26227             locationName: this.locationName,
26228             addressComponents: {
26229                 formatted_address: null,
26230                 addressLine1: null,
26231                 addressLine2: null,
26232                 streetName: null,
26233                 streetNumber: null,
26234                 city: null,
26235                 district: null,
26236                 state: null,
26237                 stateOrProvince: null
26238             },
26239             settings: this,
26240             domContainer: this.el.dom,
26241             geodecoder: new google.maps.Geocoder()
26242         };
26243     },
26244     
26245     drawCircle: function(center, radius, options) 
26246     {
26247         if (this.gMapContext.circle != null) {
26248             this.gMapContext.circle.setMap(null);
26249         }
26250         if (radius > 0) {
26251             radius *= 1;
26252             options = Roo.apply({}, options, {
26253                 strokeColor: "#0000FF",
26254                 strokeOpacity: .35,
26255                 strokeWeight: 2,
26256                 fillColor: "#0000FF",
26257                 fillOpacity: .2
26258             });
26259             
26260             options.map = this.gMapContext.map;
26261             options.radius = radius;
26262             options.center = center;
26263             this.gMapContext.circle = new google.maps.Circle(options);
26264             return this.gMapContext.circle;
26265         }
26266         
26267         return null;
26268     },
26269     
26270     setPosition: function(location) 
26271     {
26272         this.gMapContext.location = location;
26273         this.gMapContext.marker.setPosition(location);
26274         this.gMapContext.map.panTo(location);
26275         this.drawCircle(location, this.gMapContext.radius, {});
26276         
26277         var _this = this;
26278         
26279         if (this.gMapContext.settings.enableReverseGeocode) {
26280             this.gMapContext.geodecoder.geocode({
26281                 latLng: this.gMapContext.location
26282             }, function(results, status) {
26283                 
26284                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26285                     _this.gMapContext.locationName = results[0].formatted_address;
26286                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26287                     
26288                     _this.fireEvent('positionchanged', this, location);
26289                 }
26290             });
26291             
26292             return;
26293         }
26294         
26295         this.fireEvent('positionchanged', this, location);
26296     },
26297     
26298     resize: function()
26299     {
26300         google.maps.event.trigger(this.gMapContext.map, "resize");
26301         
26302         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26303         
26304         this.fireEvent('resize', this);
26305     },
26306     
26307     setPositionByLatLng: function(latitude, longitude)
26308     {
26309         this.setPosition(new google.maps.LatLng(latitude, longitude));
26310     },
26311     
26312     getCurrentPosition: function() 
26313     {
26314         return {
26315             latitude: this.gMapContext.location.lat(),
26316             longitude: this.gMapContext.location.lng()
26317         };
26318     },
26319     
26320     getAddressName: function() 
26321     {
26322         return this.gMapContext.locationName;
26323     },
26324     
26325     getAddressComponents: function() 
26326     {
26327         return this.gMapContext.addressComponents;
26328     },
26329     
26330     address_component_from_google_geocode: function(address_components) 
26331     {
26332         var result = {};
26333         
26334         for (var i = 0; i < address_components.length; i++) {
26335             var component = address_components[i];
26336             if (component.types.indexOf("postal_code") >= 0) {
26337                 result.postalCode = component.short_name;
26338             } else if (component.types.indexOf("street_number") >= 0) {
26339                 result.streetNumber = component.short_name;
26340             } else if (component.types.indexOf("route") >= 0) {
26341                 result.streetName = component.short_name;
26342             } else if (component.types.indexOf("neighborhood") >= 0) {
26343                 result.city = component.short_name;
26344             } else if (component.types.indexOf("locality") >= 0) {
26345                 result.city = component.short_name;
26346             } else if (component.types.indexOf("sublocality") >= 0) {
26347                 result.district = component.short_name;
26348             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26349                 result.stateOrProvince = component.short_name;
26350             } else if (component.types.indexOf("country") >= 0) {
26351                 result.country = component.short_name;
26352             }
26353         }
26354         
26355         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26356         result.addressLine2 = "";
26357         return result;
26358     },
26359     
26360     setZoomLevel: function(zoom)
26361     {
26362         this.gMapContext.map.setZoom(zoom);
26363     },
26364     
26365     show: function()
26366     {
26367         if(!this.el){
26368             return;
26369         }
26370         
26371         this.el.show();
26372         
26373         this.resize();
26374         
26375         this.fireEvent('show', this);
26376     },
26377     
26378     hide: function()
26379     {
26380         if(!this.el){
26381             return;
26382         }
26383         
26384         this.el.hide();
26385         
26386         this.fireEvent('hide', this);
26387     }
26388     
26389 });
26390
26391 Roo.apply(Roo.bootstrap.LocationPicker, {
26392     
26393     OverlayView : function(map, options)
26394     {
26395         options = options || {};
26396         
26397         this.setMap(map);
26398     }
26399     
26400     
26401 });/*
26402  * - LGPL
26403  *
26404  * Alert
26405  * 
26406  */
26407
26408 /**
26409  * @class Roo.bootstrap.Alert
26410  * @extends Roo.bootstrap.Component
26411  * Bootstrap Alert class
26412  * @cfg {String} title The title of alert
26413  * @cfg {String} html The content of alert
26414  * @cfg {String} weight (  success | info | warning | danger )
26415  * @cfg {String} faicon font-awesomeicon
26416  * 
26417  * @constructor
26418  * Create a new alert
26419  * @param {Object} config The config object
26420  */
26421
26422
26423 Roo.bootstrap.Alert = function(config){
26424     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26425     
26426 };
26427
26428 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26429     
26430     title: '',
26431     html: '',
26432     weight: false,
26433     faicon: false,
26434     
26435     getAutoCreate : function()
26436     {
26437         
26438         var cfg = {
26439             tag : 'div',
26440             cls : 'alert',
26441             cn : [
26442                 {
26443                     tag : 'i',
26444                     cls : 'roo-alert-icon'
26445                     
26446                 },
26447                 {
26448                     tag : 'b',
26449                     cls : 'roo-alert-title',
26450                     html : this.title
26451                 },
26452                 {
26453                     tag : 'span',
26454                     cls : 'roo-alert-text',
26455                     html : this.html
26456                 }
26457             ]
26458         };
26459         
26460         if(this.faicon){
26461             cfg.cn[0].cls += ' fa ' + this.faicon;
26462         }
26463         
26464         if(this.weight){
26465             cfg.cls += ' alert-' + this.weight;
26466         }
26467         
26468         return cfg;
26469     },
26470     
26471     initEvents: function() 
26472     {
26473         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26474     },
26475     
26476     setTitle : function(str)
26477     {
26478         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26479     },
26480     
26481     setText : function(str)
26482     {
26483         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26484     },
26485     
26486     setWeight : function(weight)
26487     {
26488         if(this.weight){
26489             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26490         }
26491         
26492         this.weight = weight;
26493         
26494         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26495     },
26496     
26497     setIcon : function(icon)
26498     {
26499         if(this.faicon){
26500             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26501         }
26502         
26503         this.faicon = icon;
26504         
26505         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26506     },
26507     
26508     hide: function() 
26509     {
26510         this.el.hide();   
26511     },
26512     
26513     show: function() 
26514     {  
26515         this.el.show();   
26516     }
26517     
26518 });
26519
26520  
26521 /*
26522 * Licence: LGPL
26523 */
26524
26525 /**
26526  * @class Roo.bootstrap.UploadCropbox
26527  * @extends Roo.bootstrap.Component
26528  * Bootstrap UploadCropbox class
26529  * @cfg {String} emptyText show when image has been loaded
26530  * @cfg {String} rotateNotify show when image too small to rotate
26531  * @cfg {Number} errorTimeout default 3000
26532  * @cfg {Number} minWidth default 300
26533  * @cfg {Number} minHeight default 300
26534  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26535  * @cfg {Boolean} isDocument (true|false) default false
26536  * @cfg {String} url action url
26537  * @cfg {String} paramName default 'imageUpload'
26538  * @cfg {String} method default POST
26539  * @cfg {Boolean} loadMask (true|false) default true
26540  * @cfg {Boolean} loadingText default 'Loading...'
26541  * 
26542  * @constructor
26543  * Create a new UploadCropbox
26544  * @param {Object} config The config object
26545  */
26546
26547 Roo.bootstrap.UploadCropbox = function(config){
26548     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26549     
26550     this.addEvents({
26551         /**
26552          * @event beforeselectfile
26553          * Fire before select file
26554          * @param {Roo.bootstrap.UploadCropbox} this
26555          */
26556         "beforeselectfile" : true,
26557         /**
26558          * @event initial
26559          * Fire after initEvent
26560          * @param {Roo.bootstrap.UploadCropbox} this
26561          */
26562         "initial" : true,
26563         /**
26564          * @event crop
26565          * Fire after initEvent
26566          * @param {Roo.bootstrap.UploadCropbox} this
26567          * @param {String} data
26568          */
26569         "crop" : true,
26570         /**
26571          * @event prepare
26572          * Fire when preparing the file data
26573          * @param {Roo.bootstrap.UploadCropbox} this
26574          * @param {Object} file
26575          */
26576         "prepare" : true,
26577         /**
26578          * @event exception
26579          * Fire when get exception
26580          * @param {Roo.bootstrap.UploadCropbox} this
26581          * @param {XMLHttpRequest} xhr
26582          */
26583         "exception" : true,
26584         /**
26585          * @event beforeloadcanvas
26586          * Fire before load the canvas
26587          * @param {Roo.bootstrap.UploadCropbox} this
26588          * @param {String} src
26589          */
26590         "beforeloadcanvas" : true,
26591         /**
26592          * @event trash
26593          * Fire when trash image
26594          * @param {Roo.bootstrap.UploadCropbox} this
26595          */
26596         "trash" : true,
26597         /**
26598          * @event download
26599          * Fire when download the image
26600          * @param {Roo.bootstrap.UploadCropbox} this
26601          */
26602         "download" : true,
26603         /**
26604          * @event footerbuttonclick
26605          * Fire when footerbuttonclick
26606          * @param {Roo.bootstrap.UploadCropbox} this
26607          * @param {String} type
26608          */
26609         "footerbuttonclick" : true,
26610         /**
26611          * @event resize
26612          * Fire when resize
26613          * @param {Roo.bootstrap.UploadCropbox} this
26614          */
26615         "resize" : true,
26616         /**
26617          * @event rotate
26618          * Fire when rotate the image
26619          * @param {Roo.bootstrap.UploadCropbox} this
26620          * @param {String} pos
26621          */
26622         "rotate" : true,
26623         /**
26624          * @event inspect
26625          * Fire when inspect the file
26626          * @param {Roo.bootstrap.UploadCropbox} this
26627          * @param {Object} file
26628          */
26629         "inspect" : true,
26630         /**
26631          * @event upload
26632          * Fire when xhr upload the file
26633          * @param {Roo.bootstrap.UploadCropbox} this
26634          * @param {Object} data
26635          */
26636         "upload" : true,
26637         /**
26638          * @event arrange
26639          * Fire when arrange the file data
26640          * @param {Roo.bootstrap.UploadCropbox} this
26641          * @param {Object} formData
26642          */
26643         "arrange" : true
26644     });
26645     
26646     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26647 };
26648
26649 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26650     
26651     emptyText : 'Click to upload image',
26652     rotateNotify : 'Image is too small to rotate',
26653     errorTimeout : 3000,
26654     scale : 0,
26655     baseScale : 1,
26656     rotate : 0,
26657     dragable : false,
26658     pinching : false,
26659     mouseX : 0,
26660     mouseY : 0,
26661     cropData : false,
26662     minWidth : 300,
26663     minHeight : 300,
26664     file : false,
26665     exif : {},
26666     baseRotate : 1,
26667     cropType : 'image/jpeg',
26668     buttons : false,
26669     canvasLoaded : false,
26670     isDocument : false,
26671     method : 'POST',
26672     paramName : 'imageUpload',
26673     loadMask : true,
26674     loadingText : 'Loading...',
26675     maskEl : false,
26676     
26677     getAutoCreate : function()
26678     {
26679         var cfg = {
26680             tag : 'div',
26681             cls : 'roo-upload-cropbox',
26682             cn : [
26683                 {
26684                     tag : 'input',
26685                     cls : 'roo-upload-cropbox-selector',
26686                     type : 'file'
26687                 },
26688                 {
26689                     tag : 'div',
26690                     cls : 'roo-upload-cropbox-body',
26691                     style : 'cursor:pointer',
26692                     cn : [
26693                         {
26694                             tag : 'div',
26695                             cls : 'roo-upload-cropbox-preview'
26696                         },
26697                         {
26698                             tag : 'div',
26699                             cls : 'roo-upload-cropbox-thumb'
26700                         },
26701                         {
26702                             tag : 'div',
26703                             cls : 'roo-upload-cropbox-empty-notify',
26704                             html : this.emptyText
26705                         },
26706                         {
26707                             tag : 'div',
26708                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26709                             html : this.rotateNotify
26710                         }
26711                     ]
26712                 },
26713                 {
26714                     tag : 'div',
26715                     cls : 'roo-upload-cropbox-footer',
26716                     cn : {
26717                         tag : 'div',
26718                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26719                         cn : []
26720                     }
26721                 }
26722             ]
26723         };
26724         
26725         return cfg;
26726     },
26727     
26728     onRender : function(ct, position)
26729     {
26730         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26731         
26732         if (this.buttons.length) {
26733             
26734             Roo.each(this.buttons, function(bb) {
26735                 
26736                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26737                 
26738                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26739                 
26740             }, this);
26741         }
26742         
26743         if(this.loadMask){
26744             this.maskEl = this.el;
26745         }
26746     },
26747     
26748     initEvents : function()
26749     {
26750         this.urlAPI = (window.createObjectURL && window) || 
26751                                 (window.URL && URL.revokeObjectURL && URL) || 
26752                                 (window.webkitURL && webkitURL);
26753                         
26754         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26755         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26756         
26757         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26758         this.selectorEl.hide();
26759         
26760         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26761         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26762         
26763         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26764         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26765         this.thumbEl.hide();
26766         
26767         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26768         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26769         
26770         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26771         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26772         this.errorEl.hide();
26773         
26774         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26775         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26776         this.footerEl.hide();
26777         
26778         this.setThumbBoxSize();
26779         
26780         this.bind();
26781         
26782         this.resize();
26783         
26784         this.fireEvent('initial', this);
26785     },
26786
26787     bind : function()
26788     {
26789         var _this = this;
26790         
26791         window.addEventListener("resize", function() { _this.resize(); } );
26792         
26793         this.bodyEl.on('click', this.beforeSelectFile, this);
26794         
26795         if(Roo.isTouch){
26796             this.bodyEl.on('touchstart', this.onTouchStart, this);
26797             this.bodyEl.on('touchmove', this.onTouchMove, this);
26798             this.bodyEl.on('touchend', this.onTouchEnd, this);
26799         }
26800         
26801         if(!Roo.isTouch){
26802             this.bodyEl.on('mousedown', this.onMouseDown, this);
26803             this.bodyEl.on('mousemove', this.onMouseMove, this);
26804             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26805             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26806             Roo.get(document).on('mouseup', this.onMouseUp, this);
26807         }
26808         
26809         this.selectorEl.on('change', this.onFileSelected, this);
26810     },
26811     
26812     reset : function()
26813     {    
26814         this.scale = 0;
26815         this.baseScale = 1;
26816         this.rotate = 0;
26817         this.baseRotate = 1;
26818         this.dragable = false;
26819         this.pinching = false;
26820         this.mouseX = 0;
26821         this.mouseY = 0;
26822         this.cropData = false;
26823         this.notifyEl.dom.innerHTML = this.emptyText;
26824         
26825         this.selectorEl.dom.value = '';
26826         
26827     },
26828     
26829     resize : function()
26830     {
26831         if(this.fireEvent('resize', this) != false){
26832             this.setThumbBoxPosition();
26833             this.setCanvasPosition();
26834         }
26835     },
26836     
26837     onFooterButtonClick : function(e, el, o, type)
26838     {
26839         switch (type) {
26840             case 'rotate-left' :
26841                 this.onRotateLeft(e);
26842                 break;
26843             case 'rotate-right' :
26844                 this.onRotateRight(e);
26845                 break;
26846             case 'picture' :
26847                 this.beforeSelectFile(e);
26848                 break;
26849             case 'trash' :
26850                 this.trash(e);
26851                 break;
26852             case 'crop' :
26853                 this.crop(e);
26854                 break;
26855             case 'download' :
26856                 this.download(e);
26857                 break;
26858             default :
26859                 break;
26860         }
26861         
26862         this.fireEvent('footerbuttonclick', this, type);
26863     },
26864     
26865     beforeSelectFile : function(e)
26866     {
26867         e.preventDefault();
26868         
26869         if(this.fireEvent('beforeselectfile', this) != false){
26870             this.selectorEl.dom.click();
26871         }
26872     },
26873     
26874     onFileSelected : function(e)
26875     {
26876         e.preventDefault();
26877         
26878         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26879             return;
26880         }
26881         
26882         var file = this.selectorEl.dom.files[0];
26883         
26884         if(this.fireEvent('inspect', this, file) != false){
26885             this.prepare(file);
26886         }
26887         
26888     },
26889     
26890     trash : function(e)
26891     {
26892         this.fireEvent('trash', this);
26893     },
26894     
26895     download : function(e)
26896     {
26897         this.fireEvent('download', this);
26898     },
26899     
26900     loadCanvas : function(src)
26901     {   
26902         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26903             
26904             this.reset();
26905             
26906             this.imageEl = document.createElement('img');
26907             
26908             var _this = this;
26909             
26910             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26911             
26912             this.imageEl.src = src;
26913         }
26914     },
26915     
26916     onLoadCanvas : function()
26917     {   
26918         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26919         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26920         
26921         this.bodyEl.un('click', this.beforeSelectFile, this);
26922         
26923         this.notifyEl.hide();
26924         this.thumbEl.show();
26925         this.footerEl.show();
26926         
26927         this.baseRotateLevel();
26928         
26929         if(this.isDocument){
26930             this.setThumbBoxSize();
26931         }
26932         
26933         this.setThumbBoxPosition();
26934         
26935         this.baseScaleLevel();
26936         
26937         this.draw();
26938         
26939         this.resize();
26940         
26941         this.canvasLoaded = true;
26942         
26943         if(this.loadMask){
26944             this.maskEl.unmask();
26945         }
26946         
26947     },
26948     
26949     setCanvasPosition : function()
26950     {   
26951         if(!this.canvasEl){
26952             return;
26953         }
26954         
26955         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26956         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26957         
26958         this.previewEl.setLeft(pw);
26959         this.previewEl.setTop(ph);
26960         
26961     },
26962     
26963     onMouseDown : function(e)
26964     {   
26965         e.stopEvent();
26966         
26967         this.dragable = true;
26968         this.pinching = false;
26969         
26970         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26971             this.dragable = false;
26972             return;
26973         }
26974         
26975         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26976         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26977         
26978     },
26979     
26980     onMouseMove : function(e)
26981     {   
26982         e.stopEvent();
26983         
26984         if(!this.canvasLoaded){
26985             return;
26986         }
26987         
26988         if (!this.dragable){
26989             return;
26990         }
26991         
26992         var minX = Math.ceil(this.thumbEl.getLeft(true));
26993         var minY = Math.ceil(this.thumbEl.getTop(true));
26994         
26995         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26996         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26997         
26998         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26999         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27000         
27001         x = x - this.mouseX;
27002         y = y - this.mouseY;
27003         
27004         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27005         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27006         
27007         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27008         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27009         
27010         this.previewEl.setLeft(bgX);
27011         this.previewEl.setTop(bgY);
27012         
27013         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27014         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27015     },
27016     
27017     onMouseUp : function(e)
27018     {   
27019         e.stopEvent();
27020         
27021         this.dragable = false;
27022     },
27023     
27024     onMouseWheel : function(e)
27025     {   
27026         e.stopEvent();
27027         
27028         this.startScale = this.scale;
27029         
27030         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27031         
27032         if(!this.zoomable()){
27033             this.scale = this.startScale;
27034             return;
27035         }
27036         
27037         this.draw();
27038         
27039         return;
27040     },
27041     
27042     zoomable : function()
27043     {
27044         var minScale = this.thumbEl.getWidth() / this.minWidth;
27045         
27046         if(this.minWidth < this.minHeight){
27047             minScale = this.thumbEl.getHeight() / this.minHeight;
27048         }
27049         
27050         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27051         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27052         
27053         if(
27054                 this.isDocument &&
27055                 (this.rotate == 0 || this.rotate == 180) && 
27056                 (
27057                     width > this.imageEl.OriginWidth || 
27058                     height > this.imageEl.OriginHeight ||
27059                     (width < this.minWidth && height < this.minHeight)
27060                 )
27061         ){
27062             return false;
27063         }
27064         
27065         if(
27066                 this.isDocument &&
27067                 (this.rotate == 90 || this.rotate == 270) && 
27068                 (
27069                     width > this.imageEl.OriginWidth || 
27070                     height > this.imageEl.OriginHeight ||
27071                     (width < this.minHeight && height < this.minWidth)
27072                 )
27073         ){
27074             return false;
27075         }
27076         
27077         if(
27078                 !this.isDocument &&
27079                 (this.rotate == 0 || this.rotate == 180) && 
27080                 (
27081                     width < this.minWidth || 
27082                     width > this.imageEl.OriginWidth || 
27083                     height < this.minHeight || 
27084                     height > this.imageEl.OriginHeight
27085                 )
27086         ){
27087             return false;
27088         }
27089         
27090         if(
27091                 !this.isDocument &&
27092                 (this.rotate == 90 || this.rotate == 270) && 
27093                 (
27094                     width < this.minHeight || 
27095                     width > this.imageEl.OriginWidth || 
27096                     height < this.minWidth || 
27097                     height > this.imageEl.OriginHeight
27098                 )
27099         ){
27100             return false;
27101         }
27102         
27103         return true;
27104         
27105     },
27106     
27107     onRotateLeft : function(e)
27108     {   
27109         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27110             
27111             var minScale = this.thumbEl.getWidth() / this.minWidth;
27112             
27113             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27114             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27115             
27116             this.startScale = this.scale;
27117             
27118             while (this.getScaleLevel() < minScale){
27119             
27120                 this.scale = this.scale + 1;
27121                 
27122                 if(!this.zoomable()){
27123                     break;
27124                 }
27125                 
27126                 if(
27127                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27128                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27129                 ){
27130                     continue;
27131                 }
27132                 
27133                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27134
27135                 this.draw();
27136                 
27137                 return;
27138             }
27139             
27140             this.scale = this.startScale;
27141             
27142             this.onRotateFail();
27143             
27144             return false;
27145         }
27146         
27147         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27148
27149         if(this.isDocument){
27150             this.setThumbBoxSize();
27151             this.setThumbBoxPosition();
27152             this.setCanvasPosition();
27153         }
27154         
27155         this.draw();
27156         
27157         this.fireEvent('rotate', this, 'left');
27158         
27159     },
27160     
27161     onRotateRight : function(e)
27162     {
27163         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27164             
27165             var minScale = this.thumbEl.getWidth() / this.minWidth;
27166         
27167             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27168             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27169             
27170             this.startScale = this.scale;
27171             
27172             while (this.getScaleLevel() < minScale){
27173             
27174                 this.scale = this.scale + 1;
27175                 
27176                 if(!this.zoomable()){
27177                     break;
27178                 }
27179                 
27180                 if(
27181                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27182                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27183                 ){
27184                     continue;
27185                 }
27186                 
27187                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27188
27189                 this.draw();
27190                 
27191                 return;
27192             }
27193             
27194             this.scale = this.startScale;
27195             
27196             this.onRotateFail();
27197             
27198             return false;
27199         }
27200         
27201         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27202
27203         if(this.isDocument){
27204             this.setThumbBoxSize();
27205             this.setThumbBoxPosition();
27206             this.setCanvasPosition();
27207         }
27208         
27209         this.draw();
27210         
27211         this.fireEvent('rotate', this, 'right');
27212     },
27213     
27214     onRotateFail : function()
27215     {
27216         this.errorEl.show(true);
27217         
27218         var _this = this;
27219         
27220         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27221     },
27222     
27223     draw : function()
27224     {
27225         this.previewEl.dom.innerHTML = '';
27226         
27227         var canvasEl = document.createElement("canvas");
27228         
27229         var contextEl = canvasEl.getContext("2d");
27230         
27231         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27232         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27233         var center = this.imageEl.OriginWidth / 2;
27234         
27235         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27236             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27237             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27238             center = this.imageEl.OriginHeight / 2;
27239         }
27240         
27241         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27242         
27243         contextEl.translate(center, center);
27244         contextEl.rotate(this.rotate * Math.PI / 180);
27245
27246         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27247         
27248         this.canvasEl = document.createElement("canvas");
27249         
27250         this.contextEl = this.canvasEl.getContext("2d");
27251         
27252         switch (this.rotate) {
27253             case 0 :
27254                 
27255                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27256                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27257                 
27258                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27259                 
27260                 break;
27261             case 90 : 
27262                 
27263                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27264                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27265                 
27266                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27267                     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);
27268                     break;
27269                 }
27270                 
27271                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27272                 
27273                 break;
27274             case 180 :
27275                 
27276                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27277                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27278                 
27279                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27280                     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);
27281                     break;
27282                 }
27283                 
27284                 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);
27285                 
27286                 break;
27287             case 270 :
27288                 
27289                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27290                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27291         
27292                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27293                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27294                     break;
27295                 }
27296                 
27297                 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);
27298                 
27299                 break;
27300             default : 
27301                 break;
27302         }
27303         
27304         this.previewEl.appendChild(this.canvasEl);
27305         
27306         this.setCanvasPosition();
27307     },
27308     
27309     crop : function()
27310     {
27311         if(!this.canvasLoaded){
27312             return;
27313         }
27314         
27315         var imageCanvas = document.createElement("canvas");
27316         
27317         var imageContext = imageCanvas.getContext("2d");
27318         
27319         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27320         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27321         
27322         var center = imageCanvas.width / 2;
27323         
27324         imageContext.translate(center, center);
27325         
27326         imageContext.rotate(this.rotate * Math.PI / 180);
27327         
27328         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27329         
27330         var canvas = document.createElement("canvas");
27331         
27332         var context = canvas.getContext("2d");
27333                 
27334         canvas.width = this.minWidth;
27335         canvas.height = this.minHeight;
27336
27337         switch (this.rotate) {
27338             case 0 :
27339                 
27340                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27341                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27342                 
27343                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27344                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27345                 
27346                 var targetWidth = this.minWidth - 2 * x;
27347                 var targetHeight = this.minHeight - 2 * y;
27348                 
27349                 var scale = 1;
27350                 
27351                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27352                     scale = targetWidth / width;
27353                 }
27354                 
27355                 if(x > 0 && y == 0){
27356                     scale = targetHeight / height;
27357                 }
27358                 
27359                 if(x > 0 && y > 0){
27360                     scale = targetWidth / width;
27361                     
27362                     if(width < height){
27363                         scale = targetHeight / height;
27364                     }
27365                 }
27366                 
27367                 context.scale(scale, scale);
27368                 
27369                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27370                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27371
27372                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27373                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27374
27375                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27376                 
27377                 break;
27378             case 90 : 
27379                 
27380                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27381                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27382                 
27383                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27384                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27385                 
27386                 var targetWidth = this.minWidth - 2 * x;
27387                 var targetHeight = this.minHeight - 2 * y;
27388                 
27389                 var scale = 1;
27390                 
27391                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27392                     scale = targetWidth / width;
27393                 }
27394                 
27395                 if(x > 0 && y == 0){
27396                     scale = targetHeight / height;
27397                 }
27398                 
27399                 if(x > 0 && y > 0){
27400                     scale = targetWidth / width;
27401                     
27402                     if(width < height){
27403                         scale = targetHeight / height;
27404                     }
27405                 }
27406                 
27407                 context.scale(scale, scale);
27408                 
27409                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27410                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27411
27412                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27413                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27414                 
27415                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27416                 
27417                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27418                 
27419                 break;
27420             case 180 :
27421                 
27422                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27423                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27424                 
27425                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27426                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27427                 
27428                 var targetWidth = this.minWidth - 2 * x;
27429                 var targetHeight = this.minHeight - 2 * y;
27430                 
27431                 var scale = 1;
27432                 
27433                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27434                     scale = targetWidth / width;
27435                 }
27436                 
27437                 if(x > 0 && y == 0){
27438                     scale = targetHeight / height;
27439                 }
27440                 
27441                 if(x > 0 && y > 0){
27442                     scale = targetWidth / width;
27443                     
27444                     if(width < height){
27445                         scale = targetHeight / height;
27446                     }
27447                 }
27448                 
27449                 context.scale(scale, scale);
27450                 
27451                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27452                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27453
27454                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27455                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27456
27457                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27458                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27459                 
27460                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27461                 
27462                 break;
27463             case 270 :
27464                 
27465                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27466                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27467                 
27468                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27469                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27470                 
27471                 var targetWidth = this.minWidth - 2 * x;
27472                 var targetHeight = this.minHeight - 2 * y;
27473                 
27474                 var scale = 1;
27475                 
27476                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27477                     scale = targetWidth / width;
27478                 }
27479                 
27480                 if(x > 0 && y == 0){
27481                     scale = targetHeight / height;
27482                 }
27483                 
27484                 if(x > 0 && y > 0){
27485                     scale = targetWidth / width;
27486                     
27487                     if(width < height){
27488                         scale = targetHeight / height;
27489                     }
27490                 }
27491                 
27492                 context.scale(scale, scale);
27493                 
27494                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27495                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27496
27497                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27498                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27499                 
27500                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27501                 
27502                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27503                 
27504                 break;
27505             default : 
27506                 break;
27507         }
27508         
27509         this.cropData = canvas.toDataURL(this.cropType);
27510         
27511         if(this.fireEvent('crop', this, this.cropData) !== false){
27512             this.process(this.file, this.cropData);
27513         }
27514         
27515         return;
27516         
27517     },
27518     
27519     setThumbBoxSize : function()
27520     {
27521         var width, height;
27522         
27523         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27524             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27525             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27526             
27527             this.minWidth = width;
27528             this.minHeight = height;
27529             
27530             if(this.rotate == 90 || this.rotate == 270){
27531                 this.minWidth = height;
27532                 this.minHeight = width;
27533             }
27534         }
27535         
27536         height = 300;
27537         width = Math.ceil(this.minWidth * height / this.minHeight);
27538         
27539         if(this.minWidth > this.minHeight){
27540             width = 300;
27541             height = Math.ceil(this.minHeight * width / this.minWidth);
27542         }
27543         
27544         this.thumbEl.setStyle({
27545             width : width + 'px',
27546             height : height + 'px'
27547         });
27548
27549         return;
27550             
27551     },
27552     
27553     setThumbBoxPosition : function()
27554     {
27555         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27556         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27557         
27558         this.thumbEl.setLeft(x);
27559         this.thumbEl.setTop(y);
27560         
27561     },
27562     
27563     baseRotateLevel : function()
27564     {
27565         this.baseRotate = 1;
27566         
27567         if(
27568                 typeof(this.exif) != 'undefined' &&
27569                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27570                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27571         ){
27572             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27573         }
27574         
27575         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27576         
27577     },
27578     
27579     baseScaleLevel : function()
27580     {
27581         var width, height;
27582         
27583         if(this.isDocument){
27584             
27585             if(this.baseRotate == 6 || this.baseRotate == 8){
27586             
27587                 height = this.thumbEl.getHeight();
27588                 this.baseScale = height / this.imageEl.OriginWidth;
27589
27590                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27591                     width = this.thumbEl.getWidth();
27592                     this.baseScale = width / this.imageEl.OriginHeight;
27593                 }
27594
27595                 return;
27596             }
27597
27598             height = this.thumbEl.getHeight();
27599             this.baseScale = height / this.imageEl.OriginHeight;
27600
27601             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27602                 width = this.thumbEl.getWidth();
27603                 this.baseScale = width / this.imageEl.OriginWidth;
27604             }
27605
27606             return;
27607         }
27608         
27609         if(this.baseRotate == 6 || this.baseRotate == 8){
27610             
27611             width = this.thumbEl.getHeight();
27612             this.baseScale = width / this.imageEl.OriginHeight;
27613             
27614             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27615                 height = this.thumbEl.getWidth();
27616                 this.baseScale = height / this.imageEl.OriginHeight;
27617             }
27618             
27619             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27620                 height = this.thumbEl.getWidth();
27621                 this.baseScale = height / this.imageEl.OriginHeight;
27622                 
27623                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27624                     width = this.thumbEl.getHeight();
27625                     this.baseScale = width / this.imageEl.OriginWidth;
27626                 }
27627             }
27628             
27629             return;
27630         }
27631         
27632         width = this.thumbEl.getWidth();
27633         this.baseScale = width / this.imageEl.OriginWidth;
27634         
27635         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27636             height = this.thumbEl.getHeight();
27637             this.baseScale = height / this.imageEl.OriginHeight;
27638         }
27639         
27640         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27641             
27642             height = this.thumbEl.getHeight();
27643             this.baseScale = height / this.imageEl.OriginHeight;
27644             
27645             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27646                 width = this.thumbEl.getWidth();
27647                 this.baseScale = width / this.imageEl.OriginWidth;
27648             }
27649             
27650         }
27651         
27652         return;
27653     },
27654     
27655     getScaleLevel : function()
27656     {
27657         return this.baseScale * Math.pow(1.1, this.scale);
27658     },
27659     
27660     onTouchStart : function(e)
27661     {
27662         if(!this.canvasLoaded){
27663             this.beforeSelectFile(e);
27664             return;
27665         }
27666         
27667         var touches = e.browserEvent.touches;
27668         
27669         if(!touches){
27670             return;
27671         }
27672         
27673         if(touches.length == 1){
27674             this.onMouseDown(e);
27675             return;
27676         }
27677         
27678         if(touches.length != 2){
27679             return;
27680         }
27681         
27682         var coords = [];
27683         
27684         for(var i = 0, finger; finger = touches[i]; i++){
27685             coords.push(finger.pageX, finger.pageY);
27686         }
27687         
27688         var x = Math.pow(coords[0] - coords[2], 2);
27689         var y = Math.pow(coords[1] - coords[3], 2);
27690         
27691         this.startDistance = Math.sqrt(x + y);
27692         
27693         this.startScale = this.scale;
27694         
27695         this.pinching = true;
27696         this.dragable = false;
27697         
27698     },
27699     
27700     onTouchMove : function(e)
27701     {
27702         if(!this.pinching && !this.dragable){
27703             return;
27704         }
27705         
27706         var touches = e.browserEvent.touches;
27707         
27708         if(!touches){
27709             return;
27710         }
27711         
27712         if(this.dragable){
27713             this.onMouseMove(e);
27714             return;
27715         }
27716         
27717         var coords = [];
27718         
27719         for(var i = 0, finger; finger = touches[i]; i++){
27720             coords.push(finger.pageX, finger.pageY);
27721         }
27722         
27723         var x = Math.pow(coords[0] - coords[2], 2);
27724         var y = Math.pow(coords[1] - coords[3], 2);
27725         
27726         this.endDistance = Math.sqrt(x + y);
27727         
27728         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27729         
27730         if(!this.zoomable()){
27731             this.scale = this.startScale;
27732             return;
27733         }
27734         
27735         this.draw();
27736         
27737     },
27738     
27739     onTouchEnd : function(e)
27740     {
27741         this.pinching = false;
27742         this.dragable = false;
27743         
27744     },
27745     
27746     process : function(file, crop)
27747     {
27748         if(this.loadMask){
27749             this.maskEl.mask(this.loadingText);
27750         }
27751         
27752         this.xhr = new XMLHttpRequest();
27753         
27754         file.xhr = this.xhr;
27755
27756         this.xhr.open(this.method, this.url, true);
27757         
27758         var headers = {
27759             "Accept": "application/json",
27760             "Cache-Control": "no-cache",
27761             "X-Requested-With": "XMLHttpRequest"
27762         };
27763         
27764         for (var headerName in headers) {
27765             var headerValue = headers[headerName];
27766             if (headerValue) {
27767                 this.xhr.setRequestHeader(headerName, headerValue);
27768             }
27769         }
27770         
27771         var _this = this;
27772         
27773         this.xhr.onload = function()
27774         {
27775             _this.xhrOnLoad(_this.xhr);
27776         }
27777         
27778         this.xhr.onerror = function()
27779         {
27780             _this.xhrOnError(_this.xhr);
27781         }
27782         
27783         var formData = new FormData();
27784
27785         formData.append('returnHTML', 'NO');
27786         
27787         if(crop){
27788             formData.append('crop', crop);
27789         }
27790         
27791         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27792             formData.append(this.paramName, file, file.name);
27793         }
27794         
27795         if(typeof(file.filename) != 'undefined'){
27796             formData.append('filename', file.filename);
27797         }
27798         
27799         if(typeof(file.mimetype) != 'undefined'){
27800             formData.append('mimetype', file.mimetype);
27801         }
27802         
27803         if(this.fireEvent('arrange', this, formData) != false){
27804             this.xhr.send(formData);
27805         };
27806     },
27807     
27808     xhrOnLoad : function(xhr)
27809     {
27810         if(this.loadMask){
27811             this.maskEl.unmask();
27812         }
27813         
27814         if (xhr.readyState !== 4) {
27815             this.fireEvent('exception', this, xhr);
27816             return;
27817         }
27818
27819         var response = Roo.decode(xhr.responseText);
27820         
27821         if(!response.success){
27822             this.fireEvent('exception', this, xhr);
27823             return;
27824         }
27825         
27826         var response = Roo.decode(xhr.responseText);
27827         
27828         this.fireEvent('upload', this, response);
27829         
27830     },
27831     
27832     xhrOnError : function()
27833     {
27834         if(this.loadMask){
27835             this.maskEl.unmask();
27836         }
27837         
27838         Roo.log('xhr on error');
27839         
27840         var response = Roo.decode(xhr.responseText);
27841           
27842         Roo.log(response);
27843         
27844     },
27845     
27846     prepare : function(file)
27847     {   
27848         if(this.loadMask){
27849             this.maskEl.mask(this.loadingText);
27850         }
27851         
27852         this.file = false;
27853         this.exif = {};
27854         
27855         if(typeof(file) === 'string'){
27856             this.loadCanvas(file);
27857             return;
27858         }
27859         
27860         if(!file || !this.urlAPI){
27861             return;
27862         }
27863         
27864         this.file = file;
27865         this.cropType = file.type;
27866         
27867         var _this = this;
27868         
27869         if(this.fireEvent('prepare', this, this.file) != false){
27870             
27871             var reader = new FileReader();
27872             
27873             reader.onload = function (e) {
27874                 if (e.target.error) {
27875                     Roo.log(e.target.error);
27876                     return;
27877                 }
27878                 
27879                 var buffer = e.target.result,
27880                     dataView = new DataView(buffer),
27881                     offset = 2,
27882                     maxOffset = dataView.byteLength - 4,
27883                     markerBytes,
27884                     markerLength;
27885                 
27886                 if (dataView.getUint16(0) === 0xffd8) {
27887                     while (offset < maxOffset) {
27888                         markerBytes = dataView.getUint16(offset);
27889                         
27890                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27891                             markerLength = dataView.getUint16(offset + 2) + 2;
27892                             if (offset + markerLength > dataView.byteLength) {
27893                                 Roo.log('Invalid meta data: Invalid segment size.');
27894                                 break;
27895                             }
27896                             
27897                             if(markerBytes == 0xffe1){
27898                                 _this.parseExifData(
27899                                     dataView,
27900                                     offset,
27901                                     markerLength
27902                                 );
27903                             }
27904                             
27905                             offset += markerLength;
27906                             
27907                             continue;
27908                         }
27909                         
27910                         break;
27911                     }
27912                     
27913                 }
27914                 
27915                 var url = _this.urlAPI.createObjectURL(_this.file);
27916                 
27917                 _this.loadCanvas(url);
27918                 
27919                 return;
27920             }
27921             
27922             reader.readAsArrayBuffer(this.file);
27923             
27924         }
27925         
27926     },
27927     
27928     parseExifData : function(dataView, offset, length)
27929     {
27930         var tiffOffset = offset + 10,
27931             littleEndian,
27932             dirOffset;
27933     
27934         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27935             // No Exif data, might be XMP data instead
27936             return;
27937         }
27938         
27939         // Check for the ASCII code for "Exif" (0x45786966):
27940         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27941             // No Exif data, might be XMP data instead
27942             return;
27943         }
27944         if (tiffOffset + 8 > dataView.byteLength) {
27945             Roo.log('Invalid Exif data: Invalid segment size.');
27946             return;
27947         }
27948         // Check for the two null bytes:
27949         if (dataView.getUint16(offset + 8) !== 0x0000) {
27950             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27951             return;
27952         }
27953         // Check the byte alignment:
27954         switch (dataView.getUint16(tiffOffset)) {
27955         case 0x4949:
27956             littleEndian = true;
27957             break;
27958         case 0x4D4D:
27959             littleEndian = false;
27960             break;
27961         default:
27962             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27963             return;
27964         }
27965         // Check for the TIFF tag marker (0x002A):
27966         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27967             Roo.log('Invalid Exif data: Missing TIFF marker.');
27968             return;
27969         }
27970         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27971         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27972         
27973         this.parseExifTags(
27974             dataView,
27975             tiffOffset,
27976             tiffOffset + dirOffset,
27977             littleEndian
27978         );
27979     },
27980     
27981     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27982     {
27983         var tagsNumber,
27984             dirEndOffset,
27985             i;
27986         if (dirOffset + 6 > dataView.byteLength) {
27987             Roo.log('Invalid Exif data: Invalid directory offset.');
27988             return;
27989         }
27990         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27991         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27992         if (dirEndOffset + 4 > dataView.byteLength) {
27993             Roo.log('Invalid Exif data: Invalid directory size.');
27994             return;
27995         }
27996         for (i = 0; i < tagsNumber; i += 1) {
27997             this.parseExifTag(
27998                 dataView,
27999                 tiffOffset,
28000                 dirOffset + 2 + 12 * i, // tag offset
28001                 littleEndian
28002             );
28003         }
28004         // Return the offset to the next directory:
28005         return dataView.getUint32(dirEndOffset, littleEndian);
28006     },
28007     
28008     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28009     {
28010         var tag = dataView.getUint16(offset, littleEndian);
28011         
28012         this.exif[tag] = this.getExifValue(
28013             dataView,
28014             tiffOffset,
28015             offset,
28016             dataView.getUint16(offset + 2, littleEndian), // tag type
28017             dataView.getUint32(offset + 4, littleEndian), // tag length
28018             littleEndian
28019         );
28020     },
28021     
28022     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28023     {
28024         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28025             tagSize,
28026             dataOffset,
28027             values,
28028             i,
28029             str,
28030             c;
28031     
28032         if (!tagType) {
28033             Roo.log('Invalid Exif data: Invalid tag type.');
28034             return;
28035         }
28036         
28037         tagSize = tagType.size * length;
28038         // Determine if the value is contained in the dataOffset bytes,
28039         // or if the value at the dataOffset is a pointer to the actual data:
28040         dataOffset = tagSize > 4 ?
28041                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28042         if (dataOffset + tagSize > dataView.byteLength) {
28043             Roo.log('Invalid Exif data: Invalid data offset.');
28044             return;
28045         }
28046         if (length === 1) {
28047             return tagType.getValue(dataView, dataOffset, littleEndian);
28048         }
28049         values = [];
28050         for (i = 0; i < length; i += 1) {
28051             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28052         }
28053         
28054         if (tagType.ascii) {
28055             str = '';
28056             // Concatenate the chars:
28057             for (i = 0; i < values.length; i += 1) {
28058                 c = values[i];
28059                 // Ignore the terminating NULL byte(s):
28060                 if (c === '\u0000') {
28061                     break;
28062                 }
28063                 str += c;
28064             }
28065             return str;
28066         }
28067         return values;
28068     }
28069     
28070 });
28071
28072 Roo.apply(Roo.bootstrap.UploadCropbox, {
28073     tags : {
28074         'Orientation': 0x0112
28075     },
28076     
28077     Orientation: {
28078             1: 0, //'top-left',
28079 //            2: 'top-right',
28080             3: 180, //'bottom-right',
28081 //            4: 'bottom-left',
28082 //            5: 'left-top',
28083             6: 90, //'right-top',
28084 //            7: 'right-bottom',
28085             8: 270 //'left-bottom'
28086     },
28087     
28088     exifTagTypes : {
28089         // byte, 8-bit unsigned int:
28090         1: {
28091             getValue: function (dataView, dataOffset) {
28092                 return dataView.getUint8(dataOffset);
28093             },
28094             size: 1
28095         },
28096         // ascii, 8-bit byte:
28097         2: {
28098             getValue: function (dataView, dataOffset) {
28099                 return String.fromCharCode(dataView.getUint8(dataOffset));
28100             },
28101             size: 1,
28102             ascii: true
28103         },
28104         // short, 16 bit int:
28105         3: {
28106             getValue: function (dataView, dataOffset, littleEndian) {
28107                 return dataView.getUint16(dataOffset, littleEndian);
28108             },
28109             size: 2
28110         },
28111         // long, 32 bit int:
28112         4: {
28113             getValue: function (dataView, dataOffset, littleEndian) {
28114                 return dataView.getUint32(dataOffset, littleEndian);
28115             },
28116             size: 4
28117         },
28118         // rational = two long values, first is numerator, second is denominator:
28119         5: {
28120             getValue: function (dataView, dataOffset, littleEndian) {
28121                 return dataView.getUint32(dataOffset, littleEndian) /
28122                     dataView.getUint32(dataOffset + 4, littleEndian);
28123             },
28124             size: 8
28125         },
28126         // slong, 32 bit signed int:
28127         9: {
28128             getValue: function (dataView, dataOffset, littleEndian) {
28129                 return dataView.getInt32(dataOffset, littleEndian);
28130             },
28131             size: 4
28132         },
28133         // srational, two slongs, first is numerator, second is denominator:
28134         10: {
28135             getValue: function (dataView, dataOffset, littleEndian) {
28136                 return dataView.getInt32(dataOffset, littleEndian) /
28137                     dataView.getInt32(dataOffset + 4, littleEndian);
28138             },
28139             size: 8
28140         }
28141     },
28142     
28143     footer : {
28144         STANDARD : [
28145             {
28146                 tag : 'div',
28147                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28148                 action : 'rotate-left',
28149                 cn : [
28150                     {
28151                         tag : 'button',
28152                         cls : 'btn btn-default',
28153                         html : '<i class="fa fa-undo"></i>'
28154                     }
28155                 ]
28156             },
28157             {
28158                 tag : 'div',
28159                 cls : 'btn-group roo-upload-cropbox-picture',
28160                 action : 'picture',
28161                 cn : [
28162                     {
28163                         tag : 'button',
28164                         cls : 'btn btn-default',
28165                         html : '<i class="fa fa-picture-o"></i>'
28166                     }
28167                 ]
28168             },
28169             {
28170                 tag : 'div',
28171                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28172                 action : 'rotate-right',
28173                 cn : [
28174                     {
28175                         tag : 'button',
28176                         cls : 'btn btn-default',
28177                         html : '<i class="fa fa-repeat"></i>'
28178                     }
28179                 ]
28180             }
28181         ],
28182         DOCUMENT : [
28183             {
28184                 tag : 'div',
28185                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28186                 action : 'rotate-left',
28187                 cn : [
28188                     {
28189                         tag : 'button',
28190                         cls : 'btn btn-default',
28191                         html : '<i class="fa fa-undo"></i>'
28192                     }
28193                 ]
28194             },
28195             {
28196                 tag : 'div',
28197                 cls : 'btn-group roo-upload-cropbox-download',
28198                 action : 'download',
28199                 cn : [
28200                     {
28201                         tag : 'button',
28202                         cls : 'btn btn-default',
28203                         html : '<i class="fa fa-download"></i>'
28204                     }
28205                 ]
28206             },
28207             {
28208                 tag : 'div',
28209                 cls : 'btn-group roo-upload-cropbox-crop',
28210                 action : 'crop',
28211                 cn : [
28212                     {
28213                         tag : 'button',
28214                         cls : 'btn btn-default',
28215                         html : '<i class="fa fa-crop"></i>'
28216                     }
28217                 ]
28218             },
28219             {
28220                 tag : 'div',
28221                 cls : 'btn-group roo-upload-cropbox-trash',
28222                 action : 'trash',
28223                 cn : [
28224                     {
28225                         tag : 'button',
28226                         cls : 'btn btn-default',
28227                         html : '<i class="fa fa-trash"></i>'
28228                     }
28229                 ]
28230             },
28231             {
28232                 tag : 'div',
28233                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28234                 action : 'rotate-right',
28235                 cn : [
28236                     {
28237                         tag : 'button',
28238                         cls : 'btn btn-default',
28239                         html : '<i class="fa fa-repeat"></i>'
28240                     }
28241                 ]
28242             }
28243         ],
28244         ROTATOR : [
28245             {
28246                 tag : 'div',
28247                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28248                 action : 'rotate-left',
28249                 cn : [
28250                     {
28251                         tag : 'button',
28252                         cls : 'btn btn-default',
28253                         html : '<i class="fa fa-undo"></i>'
28254                     }
28255                 ]
28256             },
28257             {
28258                 tag : 'div',
28259                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28260                 action : 'rotate-right',
28261                 cn : [
28262                     {
28263                         tag : 'button',
28264                         cls : 'btn btn-default',
28265                         html : '<i class="fa fa-repeat"></i>'
28266                     }
28267                 ]
28268             }
28269         ]
28270     }
28271 });
28272
28273 /*
28274 * Licence: LGPL
28275 */
28276
28277 /**
28278  * @class Roo.bootstrap.DocumentManager
28279  * @extends Roo.bootstrap.Component
28280  * Bootstrap DocumentManager class
28281  * @cfg {String} paramName default 'imageUpload'
28282  * @cfg {String} toolTipName default 'filename'
28283  * @cfg {String} method default POST
28284  * @cfg {String} url action url
28285  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28286  * @cfg {Boolean} multiple multiple upload default true
28287  * @cfg {Number} thumbSize default 300
28288  * @cfg {String} fieldLabel
28289  * @cfg {Number} labelWidth default 4
28290  * @cfg {String} labelAlign (left|top) default left
28291  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28292 * @cfg {Number} labellg set the width of label (1-12)
28293  * @cfg {Number} labelmd set the width of label (1-12)
28294  * @cfg {Number} labelsm set the width of label (1-12)
28295  * @cfg {Number} labelxs set the width of label (1-12)
28296  * 
28297  * @constructor
28298  * Create a new DocumentManager
28299  * @param {Object} config The config object
28300  */
28301
28302 Roo.bootstrap.DocumentManager = function(config){
28303     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28304     
28305     this.files = [];
28306     this.delegates = [];
28307     
28308     this.addEvents({
28309         /**
28310          * @event initial
28311          * Fire when initial the DocumentManager
28312          * @param {Roo.bootstrap.DocumentManager} this
28313          */
28314         "initial" : true,
28315         /**
28316          * @event inspect
28317          * inspect selected file
28318          * @param {Roo.bootstrap.DocumentManager} this
28319          * @param {File} file
28320          */
28321         "inspect" : true,
28322         /**
28323          * @event exception
28324          * Fire when xhr load exception
28325          * @param {Roo.bootstrap.DocumentManager} this
28326          * @param {XMLHttpRequest} xhr
28327          */
28328         "exception" : true,
28329         /**
28330          * @event afterupload
28331          * Fire when xhr load exception
28332          * @param {Roo.bootstrap.DocumentManager} this
28333          * @param {XMLHttpRequest} xhr
28334          */
28335         "afterupload" : true,
28336         /**
28337          * @event prepare
28338          * prepare the form data
28339          * @param {Roo.bootstrap.DocumentManager} this
28340          * @param {Object} formData
28341          */
28342         "prepare" : true,
28343         /**
28344          * @event remove
28345          * Fire when remove the file
28346          * @param {Roo.bootstrap.DocumentManager} this
28347          * @param {Object} file
28348          */
28349         "remove" : true,
28350         /**
28351          * @event refresh
28352          * Fire after refresh the file
28353          * @param {Roo.bootstrap.DocumentManager} this
28354          */
28355         "refresh" : true,
28356         /**
28357          * @event click
28358          * Fire after click the image
28359          * @param {Roo.bootstrap.DocumentManager} this
28360          * @param {Object} file
28361          */
28362         "click" : true,
28363         /**
28364          * @event edit
28365          * Fire when upload a image and editable set to true
28366          * @param {Roo.bootstrap.DocumentManager} this
28367          * @param {Object} file
28368          */
28369         "edit" : true,
28370         /**
28371          * @event beforeselectfile
28372          * Fire before select file
28373          * @param {Roo.bootstrap.DocumentManager} this
28374          */
28375         "beforeselectfile" : true,
28376         /**
28377          * @event process
28378          * Fire before process file
28379          * @param {Roo.bootstrap.DocumentManager} this
28380          * @param {Object} file
28381          */
28382         "process" : true
28383         
28384     });
28385 };
28386
28387 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28388     
28389     boxes : 0,
28390     inputName : '',
28391     thumbSize : 300,
28392     multiple : true,
28393     files : false,
28394     method : 'POST',
28395     url : '',
28396     paramName : 'imageUpload',
28397     toolTipName : 'filename',
28398     fieldLabel : '',
28399     labelWidth : 4,
28400     labelAlign : 'left',
28401     editable : true,
28402     delegates : false,
28403     xhr : false, 
28404     
28405     labellg : 0,
28406     labelmd : 0,
28407     labelsm : 0,
28408     labelxs : 0,
28409     
28410     getAutoCreate : function()
28411     {   
28412         var managerWidget = {
28413             tag : 'div',
28414             cls : 'roo-document-manager',
28415             cn : [
28416                 {
28417                     tag : 'input',
28418                     cls : 'roo-document-manager-selector',
28419                     type : 'file'
28420                 },
28421                 {
28422                     tag : 'div',
28423                     cls : 'roo-document-manager-uploader',
28424                     cn : [
28425                         {
28426                             tag : 'div',
28427                             cls : 'roo-document-manager-upload-btn',
28428                             html : '<i class="fa fa-plus"></i>'
28429                         }
28430                     ]
28431                     
28432                 }
28433             ]
28434         };
28435         
28436         var content = [
28437             {
28438                 tag : 'div',
28439                 cls : 'column col-md-12',
28440                 cn : managerWidget
28441             }
28442         ];
28443         
28444         if(this.fieldLabel.length){
28445             
28446             content = [
28447                 {
28448                     tag : 'div',
28449                     cls : 'column col-md-12',
28450                     html : this.fieldLabel
28451                 },
28452                 {
28453                     tag : 'div',
28454                     cls : 'column col-md-12',
28455                     cn : managerWidget
28456                 }
28457             ];
28458
28459             if(this.labelAlign == 'left'){
28460                 content = [
28461                     {
28462                         tag : 'div',
28463                         cls : 'column',
28464                         html : this.fieldLabel
28465                     },
28466                     {
28467                         tag : 'div',
28468                         cls : 'column',
28469                         cn : managerWidget
28470                     }
28471                 ];
28472                 
28473                 if(this.labelWidth > 12){
28474                     content[0].style = "width: " + this.labelWidth + 'px';
28475                 }
28476
28477                 if(this.labelWidth < 13 && this.labelmd == 0){
28478                     this.labelmd = this.labelWidth;
28479                 }
28480
28481                 if(this.labellg > 0){
28482                     content[0].cls += ' col-lg-' + this.labellg;
28483                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28484                 }
28485
28486                 if(this.labelmd > 0){
28487                     content[0].cls += ' col-md-' + this.labelmd;
28488                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28489                 }
28490
28491                 if(this.labelsm > 0){
28492                     content[0].cls += ' col-sm-' + this.labelsm;
28493                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28494                 }
28495
28496                 if(this.labelxs > 0){
28497                     content[0].cls += ' col-xs-' + this.labelxs;
28498                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28499                 }
28500                 
28501             }
28502         }
28503         
28504         var cfg = {
28505             tag : 'div',
28506             cls : 'row clearfix',
28507             cn : content
28508         };
28509         
28510         return cfg;
28511         
28512     },
28513     
28514     initEvents : function()
28515     {
28516         this.managerEl = this.el.select('.roo-document-manager', true).first();
28517         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28518         
28519         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28520         this.selectorEl.hide();
28521         
28522         if(this.multiple){
28523             this.selectorEl.attr('multiple', 'multiple');
28524         }
28525         
28526         this.selectorEl.on('change', this.onFileSelected, this);
28527         
28528         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28529         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28530         
28531         this.uploader.on('click', this.onUploaderClick, this);
28532         
28533         this.renderProgressDialog();
28534         
28535         var _this = this;
28536         
28537         window.addEventListener("resize", function() { _this.refresh(); } );
28538         
28539         this.fireEvent('initial', this);
28540     },
28541     
28542     renderProgressDialog : function()
28543     {
28544         var _this = this;
28545         
28546         this.progressDialog = new Roo.bootstrap.Modal({
28547             cls : 'roo-document-manager-progress-dialog',
28548             allow_close : false,
28549             title : '',
28550             buttons : [
28551                 {
28552                     name  :'cancel',
28553                     weight : 'danger',
28554                     html : 'Cancel'
28555                 }
28556             ], 
28557             listeners : { 
28558                 btnclick : function() {
28559                     _this.uploadCancel();
28560                     this.hide();
28561                 }
28562             }
28563         });
28564          
28565         this.progressDialog.render(Roo.get(document.body));
28566          
28567         this.progress = new Roo.bootstrap.Progress({
28568             cls : 'roo-document-manager-progress',
28569             active : true,
28570             striped : true
28571         });
28572         
28573         this.progress.render(this.progressDialog.getChildContainer());
28574         
28575         this.progressBar = new Roo.bootstrap.ProgressBar({
28576             cls : 'roo-document-manager-progress-bar',
28577             aria_valuenow : 0,
28578             aria_valuemin : 0,
28579             aria_valuemax : 12,
28580             panel : 'success'
28581         });
28582         
28583         this.progressBar.render(this.progress.getChildContainer());
28584     },
28585     
28586     onUploaderClick : function(e)
28587     {
28588         e.preventDefault();
28589      
28590         if(this.fireEvent('beforeselectfile', this) != false){
28591             this.selectorEl.dom.click();
28592         }
28593         
28594     },
28595     
28596     onFileSelected : function(e)
28597     {
28598         e.preventDefault();
28599         
28600         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28601             return;
28602         }
28603         
28604         Roo.each(this.selectorEl.dom.files, function(file){
28605             if(this.fireEvent('inspect', this, file) != false){
28606                 this.files.push(file);
28607             }
28608         }, this);
28609         
28610         this.queue();
28611         
28612     },
28613     
28614     queue : function()
28615     {
28616         this.selectorEl.dom.value = '';
28617         
28618         if(!this.files.length){
28619             return;
28620         }
28621         
28622         if(this.boxes > 0 && this.files.length > this.boxes){
28623             this.files = this.files.slice(0, this.boxes);
28624         }
28625         
28626         this.uploader.show();
28627         
28628         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28629             this.uploader.hide();
28630         }
28631         
28632         var _this = this;
28633         
28634         var files = [];
28635         
28636         var docs = [];
28637         
28638         Roo.each(this.files, function(file){
28639             
28640             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28641                 var f = this.renderPreview(file);
28642                 files.push(f);
28643                 return;
28644             }
28645             
28646             if(file.type.indexOf('image') != -1){
28647                 this.delegates.push(
28648                     (function(){
28649                         _this.process(file);
28650                     }).createDelegate(this)
28651                 );
28652         
28653                 return;
28654             }
28655             
28656             docs.push(
28657                 (function(){
28658                     _this.process(file);
28659                 }).createDelegate(this)
28660             );
28661             
28662         }, this);
28663         
28664         this.files = files;
28665         
28666         this.delegates = this.delegates.concat(docs);
28667         
28668         if(!this.delegates.length){
28669             this.refresh();
28670             return;
28671         }
28672         
28673         this.progressBar.aria_valuemax = this.delegates.length;
28674         
28675         this.arrange();
28676         
28677         return;
28678     },
28679     
28680     arrange : function()
28681     {
28682         if(!this.delegates.length){
28683             this.progressDialog.hide();
28684             this.refresh();
28685             return;
28686         }
28687         
28688         var delegate = this.delegates.shift();
28689         
28690         this.progressDialog.show();
28691         
28692         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28693         
28694         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28695         
28696         delegate();
28697     },
28698     
28699     refresh : function()
28700     {
28701         this.uploader.show();
28702         
28703         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28704             this.uploader.hide();
28705         }
28706         
28707         Roo.isTouch ? this.closable(false) : this.closable(true);
28708         
28709         this.fireEvent('refresh', this);
28710     },
28711     
28712     onRemove : function(e, el, o)
28713     {
28714         e.preventDefault();
28715         
28716         this.fireEvent('remove', this, o);
28717         
28718     },
28719     
28720     remove : function(o)
28721     {
28722         var files = [];
28723         
28724         Roo.each(this.files, function(file){
28725             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28726                 files.push(file);
28727                 return;
28728             }
28729
28730             o.target.remove();
28731
28732         }, this);
28733         
28734         this.files = files;
28735         
28736         this.refresh();
28737     },
28738     
28739     clear : function()
28740     {
28741         Roo.each(this.files, function(file){
28742             if(!file.target){
28743                 return;
28744             }
28745             
28746             file.target.remove();
28747
28748         }, this);
28749         
28750         this.files = [];
28751         
28752         this.refresh();
28753     },
28754     
28755     onClick : function(e, el, o)
28756     {
28757         e.preventDefault();
28758         
28759         this.fireEvent('click', this, o);
28760         
28761     },
28762     
28763     closable : function(closable)
28764     {
28765         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28766             
28767             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28768             
28769             if(closable){
28770                 el.show();
28771                 return;
28772             }
28773             
28774             el.hide();
28775             
28776         }, this);
28777     },
28778     
28779     xhrOnLoad : function(xhr)
28780     {
28781         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28782             el.remove();
28783         }, this);
28784         
28785         if (xhr.readyState !== 4) {
28786             this.arrange();
28787             this.fireEvent('exception', this, xhr);
28788             return;
28789         }
28790
28791         var response = Roo.decode(xhr.responseText);
28792         
28793         if(!response.success){
28794             this.arrange();
28795             this.fireEvent('exception', this, xhr);
28796             return;
28797         }
28798         
28799         var file = this.renderPreview(response.data);
28800         
28801         this.files.push(file);
28802         
28803         this.arrange();
28804         
28805         this.fireEvent('afterupload', this, xhr);
28806         
28807     },
28808     
28809     xhrOnError : function(xhr)
28810     {
28811         Roo.log('xhr on error');
28812         
28813         var response = Roo.decode(xhr.responseText);
28814           
28815         Roo.log(response);
28816         
28817         this.arrange();
28818     },
28819     
28820     process : function(file)
28821     {
28822         if(this.fireEvent('process', this, file) !== false){
28823             if(this.editable && file.type.indexOf('image') != -1){
28824                 this.fireEvent('edit', this, file);
28825                 return;
28826             }
28827
28828             this.uploadStart(file, false);
28829
28830             return;
28831         }
28832         
28833     },
28834     
28835     uploadStart : function(file, crop)
28836     {
28837         this.xhr = new XMLHttpRequest();
28838         
28839         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28840             this.arrange();
28841             return;
28842         }
28843         
28844         file.xhr = this.xhr;
28845             
28846         this.managerEl.createChild({
28847             tag : 'div',
28848             cls : 'roo-document-manager-loading',
28849             cn : [
28850                 {
28851                     tag : 'div',
28852                     tooltip : file.name,
28853                     cls : 'roo-document-manager-thumb',
28854                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28855                 }
28856             ]
28857
28858         });
28859
28860         this.xhr.open(this.method, this.url, true);
28861         
28862         var headers = {
28863             "Accept": "application/json",
28864             "Cache-Control": "no-cache",
28865             "X-Requested-With": "XMLHttpRequest"
28866         };
28867         
28868         for (var headerName in headers) {
28869             var headerValue = headers[headerName];
28870             if (headerValue) {
28871                 this.xhr.setRequestHeader(headerName, headerValue);
28872             }
28873         }
28874         
28875         var _this = this;
28876         
28877         this.xhr.onload = function()
28878         {
28879             _this.xhrOnLoad(_this.xhr);
28880         }
28881         
28882         this.xhr.onerror = function()
28883         {
28884             _this.xhrOnError(_this.xhr);
28885         }
28886         
28887         var formData = new FormData();
28888
28889         formData.append('returnHTML', 'NO');
28890         
28891         if(crop){
28892             formData.append('crop', crop);
28893         }
28894         
28895         formData.append(this.paramName, file, file.name);
28896         
28897         var options = {
28898             file : file, 
28899             manually : false
28900         };
28901         
28902         if(this.fireEvent('prepare', this, formData, options) != false){
28903             
28904             if(options.manually){
28905                 return;
28906             }
28907             
28908             this.xhr.send(formData);
28909             return;
28910         };
28911         
28912         this.uploadCancel();
28913     },
28914     
28915     uploadCancel : function()
28916     {
28917         if (this.xhr) {
28918             this.xhr.abort();
28919         }
28920         
28921         this.delegates = [];
28922         
28923         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28924             el.remove();
28925         }, this);
28926         
28927         this.arrange();
28928     },
28929     
28930     renderPreview : function(file)
28931     {
28932         if(typeof(file.target) != 'undefined' && file.target){
28933             return file;
28934         }
28935         
28936         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28937         
28938         var previewEl = this.managerEl.createChild({
28939             tag : 'div',
28940             cls : 'roo-document-manager-preview',
28941             cn : [
28942                 {
28943                     tag : 'div',
28944                     tooltip : file[this.toolTipName],
28945                     cls : 'roo-document-manager-thumb',
28946                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28947                 },
28948                 {
28949                     tag : 'button',
28950                     cls : 'close',
28951                     html : '<i class="fa fa-times-circle"></i>'
28952                 }
28953             ]
28954         });
28955
28956         var close = previewEl.select('button.close', true).first();
28957
28958         close.on('click', this.onRemove, this, file);
28959
28960         file.target = previewEl;
28961
28962         var image = previewEl.select('img', true).first();
28963         
28964         var _this = this;
28965         
28966         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28967         
28968         image.on('click', this.onClick, this, file);
28969         
28970         return file;
28971         
28972     },
28973     
28974     onPreviewLoad : function(file, image)
28975     {
28976         if(typeof(file.target) == 'undefined' || !file.target){
28977             return;
28978         }
28979         
28980         var width = image.dom.naturalWidth || image.dom.width;
28981         var height = image.dom.naturalHeight || image.dom.height;
28982         
28983         if(width > height){
28984             file.target.addClass('wide');
28985             return;
28986         }
28987         
28988         file.target.addClass('tall');
28989         return;
28990         
28991     },
28992     
28993     uploadFromSource : function(file, crop)
28994     {
28995         this.xhr = new XMLHttpRequest();
28996         
28997         this.managerEl.createChild({
28998             tag : 'div',
28999             cls : 'roo-document-manager-loading',
29000             cn : [
29001                 {
29002                     tag : 'div',
29003                     tooltip : file.name,
29004                     cls : 'roo-document-manager-thumb',
29005                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29006                 }
29007             ]
29008
29009         });
29010
29011         this.xhr.open(this.method, this.url, true);
29012         
29013         var headers = {
29014             "Accept": "application/json",
29015             "Cache-Control": "no-cache",
29016             "X-Requested-With": "XMLHttpRequest"
29017         };
29018         
29019         for (var headerName in headers) {
29020             var headerValue = headers[headerName];
29021             if (headerValue) {
29022                 this.xhr.setRequestHeader(headerName, headerValue);
29023             }
29024         }
29025         
29026         var _this = this;
29027         
29028         this.xhr.onload = function()
29029         {
29030             _this.xhrOnLoad(_this.xhr);
29031         }
29032         
29033         this.xhr.onerror = function()
29034         {
29035             _this.xhrOnError(_this.xhr);
29036         }
29037         
29038         var formData = new FormData();
29039
29040         formData.append('returnHTML', 'NO');
29041         
29042         formData.append('crop', crop);
29043         
29044         if(typeof(file.filename) != 'undefined'){
29045             formData.append('filename', file.filename);
29046         }
29047         
29048         if(typeof(file.mimetype) != 'undefined'){
29049             formData.append('mimetype', file.mimetype);
29050         }
29051         
29052         Roo.log(formData);
29053         
29054         if(this.fireEvent('prepare', this, formData) != false){
29055             this.xhr.send(formData);
29056         };
29057     }
29058 });
29059
29060 /*
29061 * Licence: LGPL
29062 */
29063
29064 /**
29065  * @class Roo.bootstrap.DocumentViewer
29066  * @extends Roo.bootstrap.Component
29067  * Bootstrap DocumentViewer class
29068  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29069  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29070  * 
29071  * @constructor
29072  * Create a new DocumentViewer
29073  * @param {Object} config The config object
29074  */
29075
29076 Roo.bootstrap.DocumentViewer = function(config){
29077     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29078     
29079     this.addEvents({
29080         /**
29081          * @event initial
29082          * Fire after initEvent
29083          * @param {Roo.bootstrap.DocumentViewer} this
29084          */
29085         "initial" : true,
29086         /**
29087          * @event click
29088          * Fire after click
29089          * @param {Roo.bootstrap.DocumentViewer} this
29090          */
29091         "click" : true,
29092         /**
29093          * @event download
29094          * Fire after download button
29095          * @param {Roo.bootstrap.DocumentViewer} this
29096          */
29097         "download" : true,
29098         /**
29099          * @event trash
29100          * Fire after trash button
29101          * @param {Roo.bootstrap.DocumentViewer} this
29102          */
29103         "trash" : true
29104         
29105     });
29106 };
29107
29108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29109     
29110     showDownload : true,
29111     
29112     showTrash : true,
29113     
29114     getAutoCreate : function()
29115     {
29116         var cfg = {
29117             tag : 'div',
29118             cls : 'roo-document-viewer',
29119             cn : [
29120                 {
29121                     tag : 'div',
29122                     cls : 'roo-document-viewer-body',
29123                     cn : [
29124                         {
29125                             tag : 'div',
29126                             cls : 'roo-document-viewer-thumb',
29127                             cn : [
29128                                 {
29129                                     tag : 'img',
29130                                     cls : 'roo-document-viewer-image'
29131                                 }
29132                             ]
29133                         }
29134                     ]
29135                 },
29136                 {
29137                     tag : 'div',
29138                     cls : 'roo-document-viewer-footer',
29139                     cn : {
29140                         tag : 'div',
29141                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29142                         cn : [
29143                             {
29144                                 tag : 'div',
29145                                 cls : 'btn-group roo-document-viewer-download',
29146                                 cn : [
29147                                     {
29148                                         tag : 'button',
29149                                         cls : 'btn btn-default',
29150                                         html : '<i class="fa fa-download"></i>'
29151                                     }
29152                                 ]
29153                             },
29154                             {
29155                                 tag : 'div',
29156                                 cls : 'btn-group roo-document-viewer-trash',
29157                                 cn : [
29158                                     {
29159                                         tag : 'button',
29160                                         cls : 'btn btn-default',
29161                                         html : '<i class="fa fa-trash"></i>'
29162                                     }
29163                                 ]
29164                             }
29165                         ]
29166                     }
29167                 }
29168             ]
29169         };
29170         
29171         return cfg;
29172     },
29173     
29174     initEvents : function()
29175     {
29176         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29178         
29179         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29181         
29182         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29184         
29185         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29186         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29187         
29188         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29189         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29190         
29191         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29192         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29193         
29194         this.bodyEl.on('click', this.onClick, this);
29195         this.downloadBtn.on('click', this.onDownload, this);
29196         this.trashBtn.on('click', this.onTrash, this);
29197         
29198         this.downloadBtn.hide();
29199         this.trashBtn.hide();
29200         
29201         if(this.showDownload){
29202             this.downloadBtn.show();
29203         }
29204         
29205         if(this.showTrash){
29206             this.trashBtn.show();
29207         }
29208         
29209         if(!this.showDownload && !this.showTrash) {
29210             this.footerEl.hide();
29211         }
29212         
29213     },
29214     
29215     initial : function()
29216     {
29217         this.fireEvent('initial', this);
29218         
29219     },
29220     
29221     onClick : function(e)
29222     {
29223         e.preventDefault();
29224         
29225         this.fireEvent('click', this);
29226     },
29227     
29228     onDownload : function(e)
29229     {
29230         e.preventDefault();
29231         
29232         this.fireEvent('download', this);
29233     },
29234     
29235     onTrash : function(e)
29236     {
29237         e.preventDefault();
29238         
29239         this.fireEvent('trash', this);
29240     }
29241     
29242 });
29243 /*
29244  * - LGPL
29245  *
29246  * nav progress bar
29247  * 
29248  */
29249
29250 /**
29251  * @class Roo.bootstrap.NavProgressBar
29252  * @extends Roo.bootstrap.Component
29253  * Bootstrap NavProgressBar class
29254  * 
29255  * @constructor
29256  * Create a new nav progress bar
29257  * @param {Object} config The config object
29258  */
29259
29260 Roo.bootstrap.NavProgressBar = function(config){
29261     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29262
29263     this.bullets = this.bullets || [];
29264    
29265 //    Roo.bootstrap.NavProgressBar.register(this);
29266      this.addEvents({
29267         /**
29268              * @event changed
29269              * Fires when the active item changes
29270              * @param {Roo.bootstrap.NavProgressBar} this
29271              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29272              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29273          */
29274         'changed': true
29275      });
29276     
29277 };
29278
29279 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29280     
29281     bullets : [],
29282     barItems : [],
29283     
29284     getAutoCreate : function()
29285     {
29286         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29287         
29288         cfg = {
29289             tag : 'div',
29290             cls : 'roo-navigation-bar-group',
29291             cn : [
29292                 {
29293                     tag : 'div',
29294                     cls : 'roo-navigation-top-bar'
29295                 },
29296                 {
29297                     tag : 'div',
29298                     cls : 'roo-navigation-bullets-bar',
29299                     cn : [
29300                         {
29301                             tag : 'ul',
29302                             cls : 'roo-navigation-bar'
29303                         }
29304                     ]
29305                 },
29306                 
29307                 {
29308                     tag : 'div',
29309                     cls : 'roo-navigation-bottom-bar'
29310                 }
29311             ]
29312             
29313         };
29314         
29315         return cfg;
29316         
29317     },
29318     
29319     initEvents: function() 
29320     {
29321         
29322     },
29323     
29324     onRender : function(ct, position) 
29325     {
29326         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29327         
29328         if(this.bullets.length){
29329             Roo.each(this.bullets, function(b){
29330                this.addItem(b);
29331             }, this);
29332         }
29333         
29334         this.format();
29335         
29336     },
29337     
29338     addItem : function(cfg)
29339     {
29340         var item = new Roo.bootstrap.NavProgressItem(cfg);
29341         
29342         item.parentId = this.id;
29343         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29344         
29345         if(cfg.html){
29346             var top = new Roo.bootstrap.Element({
29347                 tag : 'div',
29348                 cls : 'roo-navigation-bar-text'
29349             });
29350             
29351             var bottom = new Roo.bootstrap.Element({
29352                 tag : 'div',
29353                 cls : 'roo-navigation-bar-text'
29354             });
29355             
29356             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29357             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29358             
29359             var topText = new Roo.bootstrap.Element({
29360                 tag : 'span',
29361                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29362             });
29363             
29364             var bottomText = new Roo.bootstrap.Element({
29365                 tag : 'span',
29366                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29367             });
29368             
29369             topText.onRender(top.el, null);
29370             bottomText.onRender(bottom.el, null);
29371             
29372             item.topEl = top;
29373             item.bottomEl = bottom;
29374         }
29375         
29376         this.barItems.push(item);
29377         
29378         return item;
29379     },
29380     
29381     getActive : function()
29382     {
29383         var active = false;
29384         
29385         Roo.each(this.barItems, function(v){
29386             
29387             if (!v.isActive()) {
29388                 return;
29389             }
29390             
29391             active = v;
29392             return false;
29393             
29394         });
29395         
29396         return active;
29397     },
29398     
29399     setActiveItem : function(item)
29400     {
29401         var prev = false;
29402         
29403         Roo.each(this.barItems, function(v){
29404             if (v.rid == item.rid) {
29405                 return ;
29406             }
29407             
29408             if (v.isActive()) {
29409                 v.setActive(false);
29410                 prev = v;
29411             }
29412         });
29413
29414         item.setActive(true);
29415         
29416         this.fireEvent('changed', this, item, prev);
29417     },
29418     
29419     getBarItem: function(rid)
29420     {
29421         var ret = false;
29422         
29423         Roo.each(this.barItems, function(e) {
29424             if (e.rid != rid) {
29425                 return;
29426             }
29427             
29428             ret =  e;
29429             return false;
29430         });
29431         
29432         return ret;
29433     },
29434     
29435     indexOfItem : function(item)
29436     {
29437         var index = false;
29438         
29439         Roo.each(this.barItems, function(v, i){
29440             
29441             if (v.rid != item.rid) {
29442                 return;
29443             }
29444             
29445             index = i;
29446             return false
29447         });
29448         
29449         return index;
29450     },
29451     
29452     setActiveNext : function()
29453     {
29454         var i = this.indexOfItem(this.getActive());
29455         
29456         if (i > this.barItems.length) {
29457             return;
29458         }
29459         
29460         this.setActiveItem(this.barItems[i+1]);
29461     },
29462     
29463     setActivePrev : function()
29464     {
29465         var i = this.indexOfItem(this.getActive());
29466         
29467         if (i  < 1) {
29468             return;
29469         }
29470         
29471         this.setActiveItem(this.barItems[i-1]);
29472     },
29473     
29474     format : function()
29475     {
29476         if(!this.barItems.length){
29477             return;
29478         }
29479      
29480         var width = 100 / this.barItems.length;
29481         
29482         Roo.each(this.barItems, function(i){
29483             i.el.setStyle('width', width + '%');
29484             i.topEl.el.setStyle('width', width + '%');
29485             i.bottomEl.el.setStyle('width', width + '%');
29486         }, this);
29487         
29488     }
29489     
29490 });
29491 /*
29492  * - LGPL
29493  *
29494  * Nav Progress Item
29495  * 
29496  */
29497
29498 /**
29499  * @class Roo.bootstrap.NavProgressItem
29500  * @extends Roo.bootstrap.Component
29501  * Bootstrap NavProgressItem class
29502  * @cfg {String} rid the reference id
29503  * @cfg {Boolean} active (true|false) Is item active default false
29504  * @cfg {Boolean} disabled (true|false) Is item active default false
29505  * @cfg {String} html
29506  * @cfg {String} position (top|bottom) text position default bottom
29507  * @cfg {String} icon show icon instead of number
29508  * 
29509  * @constructor
29510  * Create a new NavProgressItem
29511  * @param {Object} config The config object
29512  */
29513 Roo.bootstrap.NavProgressItem = function(config){
29514     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29515     this.addEvents({
29516         // raw events
29517         /**
29518          * @event click
29519          * The raw click event for the entire grid.
29520          * @param {Roo.bootstrap.NavProgressItem} this
29521          * @param {Roo.EventObject} e
29522          */
29523         "click" : true
29524     });
29525    
29526 };
29527
29528 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29529     
29530     rid : '',
29531     active : false,
29532     disabled : false,
29533     html : '',
29534     position : 'bottom',
29535     icon : false,
29536     
29537     getAutoCreate : function()
29538     {
29539         var iconCls = 'roo-navigation-bar-item-icon';
29540         
29541         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29542         
29543         var cfg = {
29544             tag: 'li',
29545             cls: 'roo-navigation-bar-item',
29546             cn : [
29547                 {
29548                     tag : 'i',
29549                     cls : iconCls
29550                 }
29551             ]
29552         };
29553         
29554         if(this.active){
29555             cfg.cls += ' active';
29556         }
29557         if(this.disabled){
29558             cfg.cls += ' disabled';
29559         }
29560         
29561         return cfg;
29562     },
29563     
29564     disable : function()
29565     {
29566         this.setDisabled(true);
29567     },
29568     
29569     enable : function()
29570     {
29571         this.setDisabled(false);
29572     },
29573     
29574     initEvents: function() 
29575     {
29576         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29577         
29578         this.iconEl.on('click', this.onClick, this);
29579     },
29580     
29581     onClick : function(e)
29582     {
29583         e.preventDefault();
29584         
29585         if(this.disabled){
29586             return;
29587         }
29588         
29589         if(this.fireEvent('click', this, e) === false){
29590             return;
29591         };
29592         
29593         this.parent().setActiveItem(this);
29594     },
29595     
29596     isActive: function () 
29597     {
29598         return this.active;
29599     },
29600     
29601     setActive : function(state)
29602     {
29603         if(this.active == state){
29604             return;
29605         }
29606         
29607         this.active = state;
29608         
29609         if (state) {
29610             this.el.addClass('active');
29611             return;
29612         }
29613         
29614         this.el.removeClass('active');
29615         
29616         return;
29617     },
29618     
29619     setDisabled : function(state)
29620     {
29621         if(this.disabled == state){
29622             return;
29623         }
29624         
29625         this.disabled = state;
29626         
29627         if (state) {
29628             this.el.addClass('disabled');
29629             return;
29630         }
29631         
29632         this.el.removeClass('disabled');
29633     },
29634     
29635     tooltipEl : function()
29636     {
29637         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29638     }
29639 });
29640  
29641
29642  /*
29643  * - LGPL
29644  *
29645  * FieldLabel
29646  * 
29647  */
29648
29649 /**
29650  * @class Roo.bootstrap.FieldLabel
29651  * @extends Roo.bootstrap.Component
29652  * Bootstrap FieldLabel class
29653  * @cfg {String} html contents of the element
29654  * @cfg {String} tag tag of the element default label
29655  * @cfg {String} cls class of the element
29656  * @cfg {String} target label target 
29657  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29658  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29659  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29660  * @cfg {String} iconTooltip default "This field is required"
29661  * 
29662  * @constructor
29663  * Create a new FieldLabel
29664  * @param {Object} config The config object
29665  */
29666
29667 Roo.bootstrap.FieldLabel = function(config){
29668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29669     
29670     this.addEvents({
29671             /**
29672              * @event invalid
29673              * Fires after the field has been marked as invalid.
29674              * @param {Roo.form.FieldLabel} this
29675              * @param {String} msg The validation message
29676              */
29677             invalid : true,
29678             /**
29679              * @event valid
29680              * Fires after the field has been validated with no errors.
29681              * @param {Roo.form.FieldLabel} this
29682              */
29683             valid : true
29684         });
29685 };
29686
29687 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29688     
29689     tag: 'label',
29690     cls: '',
29691     html: '',
29692     target: '',
29693     allowBlank : true,
29694     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29695     validClass : 'text-success fa fa-lg fa-check',
29696     iconTooltip : 'This field is required',
29697     
29698     getAutoCreate : function(){
29699         
29700         var cfg = {
29701             tag : this.tag,
29702             cls : 'roo-bootstrap-field-label ' + this.cls,
29703             for : this.target,
29704             cn : [
29705                 {
29706                     tag : 'i',
29707                     cls : '',
29708                     tooltip : this.iconTooltip
29709                 },
29710                 {
29711                     tag : 'span',
29712                     html : this.html
29713                 }
29714             ] 
29715         };
29716         
29717         return cfg;
29718     },
29719     
29720     initEvents: function() 
29721     {
29722         Roo.bootstrap.Element.superclass.initEvents.call(this);
29723         
29724         this.iconEl = this.el.select('i', true).first();
29725         
29726         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29727         
29728         Roo.bootstrap.FieldLabel.register(this);
29729     },
29730     
29731     /**
29732      * Mark this field as valid
29733      */
29734     markValid : function()
29735     {
29736         this.iconEl.show();
29737         
29738         this.iconEl.removeClass(this.invalidClass);
29739         
29740         this.iconEl.addClass(this.validClass);
29741         
29742         this.fireEvent('valid', this);
29743     },
29744     
29745     /**
29746      * Mark this field as invalid
29747      * @param {String} msg The validation message
29748      */
29749     markInvalid : function(msg)
29750     {
29751         this.iconEl.show();
29752         
29753         this.iconEl.removeClass(this.validClass);
29754         
29755         this.iconEl.addClass(this.invalidClass);
29756         
29757         this.fireEvent('invalid', this, msg);
29758     }
29759     
29760    
29761 });
29762
29763 Roo.apply(Roo.bootstrap.FieldLabel, {
29764     
29765     groups: {},
29766     
29767      /**
29768     * register a FieldLabel Group
29769     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29770     */
29771     register : function(label)
29772     {
29773         if(this.groups.hasOwnProperty(label.target)){
29774             return;
29775         }
29776      
29777         this.groups[label.target] = label;
29778         
29779     },
29780     /**
29781     * fetch a FieldLabel Group based on the target
29782     * @param {string} target
29783     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29784     */
29785     get: function(target) {
29786         if (typeof(this.groups[target]) == 'undefined') {
29787             return false;
29788         }
29789         
29790         return this.groups[target] ;
29791     }
29792 });
29793
29794  
29795
29796  /*
29797  * - LGPL
29798  *
29799  * page DateSplitField.
29800  * 
29801  */
29802
29803
29804 /**
29805  * @class Roo.bootstrap.DateSplitField
29806  * @extends Roo.bootstrap.Component
29807  * Bootstrap DateSplitField class
29808  * @cfg {string} fieldLabel - the label associated
29809  * @cfg {Number} labelWidth set the width of label (0-12)
29810  * @cfg {String} labelAlign (top|left)
29811  * @cfg {Boolean} dayAllowBlank (true|false) default false
29812  * @cfg {Boolean} monthAllowBlank (true|false) default false
29813  * @cfg {Boolean} yearAllowBlank (true|false) default false
29814  * @cfg {string} dayPlaceholder 
29815  * @cfg {string} monthPlaceholder
29816  * @cfg {string} yearPlaceholder
29817  * @cfg {string} dayFormat default 'd'
29818  * @cfg {string} monthFormat default 'm'
29819  * @cfg {string} yearFormat default 'Y'
29820  * @cfg {Number} labellg set the width of label (1-12)
29821  * @cfg {Number} labelmd set the width of label (1-12)
29822  * @cfg {Number} labelsm set the width of label (1-12)
29823  * @cfg {Number} labelxs set the width of label (1-12)
29824
29825  *     
29826  * @constructor
29827  * Create a new DateSplitField
29828  * @param {Object} config The config object
29829  */
29830
29831 Roo.bootstrap.DateSplitField = function(config){
29832     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29833     
29834     this.addEvents({
29835         // raw events
29836          /**
29837          * @event years
29838          * getting the data of years
29839          * @param {Roo.bootstrap.DateSplitField} this
29840          * @param {Object} years
29841          */
29842         "years" : true,
29843         /**
29844          * @event days
29845          * getting the data of days
29846          * @param {Roo.bootstrap.DateSplitField} this
29847          * @param {Object} days
29848          */
29849         "days" : true,
29850         /**
29851          * @event invalid
29852          * Fires after the field has been marked as invalid.
29853          * @param {Roo.form.Field} this
29854          * @param {String} msg The validation message
29855          */
29856         invalid : true,
29857        /**
29858          * @event valid
29859          * Fires after the field has been validated with no errors.
29860          * @param {Roo.form.Field} this
29861          */
29862         valid : true
29863     });
29864 };
29865
29866 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29867     
29868     fieldLabel : '',
29869     labelAlign : 'top',
29870     labelWidth : 3,
29871     dayAllowBlank : false,
29872     monthAllowBlank : false,
29873     yearAllowBlank : false,
29874     dayPlaceholder : '',
29875     monthPlaceholder : '',
29876     yearPlaceholder : '',
29877     dayFormat : 'd',
29878     monthFormat : 'm',
29879     yearFormat : 'Y',
29880     isFormField : true,
29881     labellg : 0,
29882     labelmd : 0,
29883     labelsm : 0,
29884     labelxs : 0,
29885     
29886     getAutoCreate : function()
29887     {
29888         var cfg = {
29889             tag : 'div',
29890             cls : 'row roo-date-split-field-group',
29891             cn : [
29892                 {
29893                     tag : 'input',
29894                     type : 'hidden',
29895                     cls : 'form-hidden-field roo-date-split-field-group-value',
29896                     name : this.name
29897                 }
29898             ]
29899         };
29900         
29901         var labelCls = 'col-md-12';
29902         var contentCls = 'col-md-4';
29903         
29904         if(this.fieldLabel){
29905             
29906             var label = {
29907                 tag : 'div',
29908                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29909                 cn : [
29910                     {
29911                         tag : 'label',
29912                         html : this.fieldLabel
29913                     }
29914                 ]
29915             };
29916             
29917             if(this.labelAlign == 'left'){
29918             
29919                 if(this.labelWidth > 12){
29920                     label.style = "width: " + this.labelWidth + 'px';
29921                 }
29922
29923                 if(this.labelWidth < 13 && this.labelmd == 0){
29924                     this.labelmd = this.labelWidth;
29925                 }
29926
29927                 if(this.labellg > 0){
29928                     labelCls = ' col-lg-' + this.labellg;
29929                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29930                 }
29931
29932                 if(this.labelmd > 0){
29933                     labelCls = ' col-md-' + this.labelmd;
29934                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29935                 }
29936
29937                 if(this.labelsm > 0){
29938                     labelCls = ' col-sm-' + this.labelsm;
29939                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29940                 }
29941
29942                 if(this.labelxs > 0){
29943                     labelCls = ' col-xs-' + this.labelxs;
29944                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29945                 }
29946             }
29947             
29948             label.cls += ' ' + labelCls;
29949             
29950             cfg.cn.push(label);
29951         }
29952         
29953         Roo.each(['day', 'month', 'year'], function(t){
29954             cfg.cn.push({
29955                 tag : 'div',
29956                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29957             });
29958         }, this);
29959         
29960         return cfg;
29961     },
29962     
29963     inputEl: function ()
29964     {
29965         return this.el.select('.roo-date-split-field-group-value', true).first();
29966     },
29967     
29968     onRender : function(ct, position) 
29969     {
29970         var _this = this;
29971         
29972         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29973         
29974         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29975         
29976         this.dayField = new Roo.bootstrap.ComboBox({
29977             allowBlank : this.dayAllowBlank,
29978             alwaysQuery : true,
29979             displayField : 'value',
29980             editable : false,
29981             fieldLabel : '',
29982             forceSelection : true,
29983             mode : 'local',
29984             placeholder : this.dayPlaceholder,
29985             selectOnFocus : true,
29986             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29987             triggerAction : 'all',
29988             typeAhead : true,
29989             valueField : 'value',
29990             store : new Roo.data.SimpleStore({
29991                 data : (function() {    
29992                     var days = [];
29993                     _this.fireEvent('days', _this, days);
29994                     return days;
29995                 })(),
29996                 fields : [ 'value' ]
29997             }),
29998             listeners : {
29999                 select : function (_self, record, index)
30000                 {
30001                     _this.setValue(_this.getValue());
30002                 }
30003             }
30004         });
30005
30006         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30007         
30008         this.monthField = new Roo.bootstrap.MonthField({
30009             after : '<i class=\"fa fa-calendar\"></i>',
30010             allowBlank : this.monthAllowBlank,
30011             placeholder : this.monthPlaceholder,
30012             readOnly : true,
30013             listeners : {
30014                 render : function (_self)
30015                 {
30016                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30017                         e.preventDefault();
30018                         _self.focus();
30019                     });
30020                 },
30021                 select : function (_self, oldvalue, newvalue)
30022                 {
30023                     _this.setValue(_this.getValue());
30024                 }
30025             }
30026         });
30027         
30028         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30029         
30030         this.yearField = new Roo.bootstrap.ComboBox({
30031             allowBlank : this.yearAllowBlank,
30032             alwaysQuery : true,
30033             displayField : 'value',
30034             editable : false,
30035             fieldLabel : '',
30036             forceSelection : true,
30037             mode : 'local',
30038             placeholder : this.yearPlaceholder,
30039             selectOnFocus : true,
30040             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30041             triggerAction : 'all',
30042             typeAhead : true,
30043             valueField : 'value',
30044             store : new Roo.data.SimpleStore({
30045                 data : (function() {
30046                     var years = [];
30047                     _this.fireEvent('years', _this, years);
30048                     return years;
30049                 })(),
30050                 fields : [ 'value' ]
30051             }),
30052             listeners : {
30053                 select : function (_self, record, index)
30054                 {
30055                     _this.setValue(_this.getValue());
30056                 }
30057             }
30058         });
30059
30060         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30061     },
30062     
30063     setValue : function(v, format)
30064     {
30065         this.inputEl.dom.value = v;
30066         
30067         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30068         
30069         var d = Date.parseDate(v, f);
30070         
30071         if(!d){
30072             this.validate();
30073             return;
30074         }
30075         
30076         this.setDay(d.format(this.dayFormat));
30077         this.setMonth(d.format(this.monthFormat));
30078         this.setYear(d.format(this.yearFormat));
30079         
30080         this.validate();
30081         
30082         return;
30083     },
30084     
30085     setDay : function(v)
30086     {
30087         this.dayField.setValue(v);
30088         this.inputEl.dom.value = this.getValue();
30089         this.validate();
30090         return;
30091     },
30092     
30093     setMonth : function(v)
30094     {
30095         this.monthField.setValue(v, true);
30096         this.inputEl.dom.value = this.getValue();
30097         this.validate();
30098         return;
30099     },
30100     
30101     setYear : function(v)
30102     {
30103         this.yearField.setValue(v);
30104         this.inputEl.dom.value = this.getValue();
30105         this.validate();
30106         return;
30107     },
30108     
30109     getDay : function()
30110     {
30111         return this.dayField.getValue();
30112     },
30113     
30114     getMonth : function()
30115     {
30116         return this.monthField.getValue();
30117     },
30118     
30119     getYear : function()
30120     {
30121         return this.yearField.getValue();
30122     },
30123     
30124     getValue : function()
30125     {
30126         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30127         
30128         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30129         
30130         return date;
30131     },
30132     
30133     reset : function()
30134     {
30135         this.setDay('');
30136         this.setMonth('');
30137         this.setYear('');
30138         this.inputEl.dom.value = '';
30139         this.validate();
30140         return;
30141     },
30142     
30143     validate : function()
30144     {
30145         var d = this.dayField.validate();
30146         var m = this.monthField.validate();
30147         var y = this.yearField.validate();
30148         
30149         var valid = true;
30150         
30151         if(
30152                 (!this.dayAllowBlank && !d) ||
30153                 (!this.monthAllowBlank && !m) ||
30154                 (!this.yearAllowBlank && !y)
30155         ){
30156             valid = false;
30157         }
30158         
30159         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30160             return valid;
30161         }
30162         
30163         if(valid){
30164             this.markValid();
30165             return valid;
30166         }
30167         
30168         this.markInvalid();
30169         
30170         return valid;
30171     },
30172     
30173     markValid : function()
30174     {
30175         
30176         var label = this.el.select('label', true).first();
30177         var icon = this.el.select('i.fa-star', true).first();
30178
30179         if(label && icon){
30180             icon.remove();
30181         }
30182         
30183         this.fireEvent('valid', this);
30184     },
30185     
30186      /**
30187      * Mark this field as invalid
30188      * @param {String} msg The validation message
30189      */
30190     markInvalid : function(msg)
30191     {
30192         
30193         var label = this.el.select('label', true).first();
30194         var icon = this.el.select('i.fa-star', true).first();
30195
30196         if(label && !icon){
30197             this.el.select('.roo-date-split-field-label', true).createChild({
30198                 tag : 'i',
30199                 cls : 'text-danger fa fa-lg fa-star',
30200                 tooltip : 'This field is required',
30201                 style : 'margin-right:5px;'
30202             }, label, true);
30203         }
30204         
30205         this.fireEvent('invalid', this, msg);
30206     },
30207     
30208     clearInvalid : function()
30209     {
30210         var label = this.el.select('label', true).first();
30211         var icon = this.el.select('i.fa-star', true).first();
30212
30213         if(label && icon){
30214             icon.remove();
30215         }
30216         
30217         this.fireEvent('valid', this);
30218     },
30219     
30220     getName: function()
30221     {
30222         return this.name;
30223     }
30224     
30225 });
30226
30227  /**
30228  *
30229  * This is based on 
30230  * http://masonry.desandro.com
30231  *
30232  * The idea is to render all the bricks based on vertical width...
30233  *
30234  * The original code extends 'outlayer' - we might need to use that....
30235  * 
30236  */
30237
30238
30239 /**
30240  * @class Roo.bootstrap.LayoutMasonry
30241  * @extends Roo.bootstrap.Component
30242  * Bootstrap Layout Masonry class
30243  * 
30244  * @constructor
30245  * Create a new Element
30246  * @param {Object} config The config object
30247  */
30248
30249 Roo.bootstrap.LayoutMasonry = function(config){
30250     
30251     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30252     
30253     this.bricks = [];
30254     
30255     Roo.bootstrap.LayoutMasonry.register(this);
30256     
30257     this.addEvents({
30258         // raw events
30259         /**
30260          * @event layout
30261          * Fire after layout the items
30262          * @param {Roo.bootstrap.LayoutMasonry} this
30263          * @param {Roo.EventObject} e
30264          */
30265         "layout" : true
30266     });
30267     
30268 };
30269
30270 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30271     
30272     /**
30273      * @cfg {Boolean} isLayoutInstant = no animation?
30274      */   
30275     isLayoutInstant : false, // needed?
30276    
30277     /**
30278      * @cfg {Number} boxWidth  width of the columns
30279      */   
30280     boxWidth : 450,
30281     
30282       /**
30283      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30284      */   
30285     boxHeight : 0,
30286     
30287     /**
30288      * @cfg {Number} padWidth padding below box..
30289      */   
30290     padWidth : 10, 
30291     
30292     /**
30293      * @cfg {Number} gutter gutter width..
30294      */   
30295     gutter : 10,
30296     
30297      /**
30298      * @cfg {Number} maxCols maximum number of columns
30299      */   
30300     
30301     maxCols: 0,
30302     
30303     /**
30304      * @cfg {Boolean} isAutoInitial defalut true
30305      */   
30306     isAutoInitial : true, 
30307     
30308     containerWidth: 0,
30309     
30310     /**
30311      * @cfg {Boolean} isHorizontal defalut false
30312      */   
30313     isHorizontal : false, 
30314
30315     currentSize : null,
30316     
30317     tag: 'div',
30318     
30319     cls: '',
30320     
30321     bricks: null, //CompositeElement
30322     
30323     cols : 1,
30324     
30325     _isLayoutInited : false,
30326     
30327 //    isAlternative : false, // only use for vertical layout...
30328     
30329     /**
30330      * @cfg {Number} alternativePadWidth padding below box..
30331      */   
30332     alternativePadWidth : 50,
30333     
30334     selectedBrick : [],
30335     
30336     getAutoCreate : function(){
30337         
30338         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30339         
30340         var cfg = {
30341             tag: this.tag,
30342             cls: 'blog-masonary-wrapper ' + this.cls,
30343             cn : {
30344                 cls : 'mas-boxes masonary'
30345             }
30346         };
30347         
30348         return cfg;
30349     },
30350     
30351     getChildContainer: function( )
30352     {
30353         if (this.boxesEl) {
30354             return this.boxesEl;
30355         }
30356         
30357         this.boxesEl = this.el.select('.mas-boxes').first();
30358         
30359         return this.boxesEl;
30360     },
30361     
30362     
30363     initEvents : function()
30364     {
30365         var _this = this;
30366         
30367         if(this.isAutoInitial){
30368             Roo.log('hook children rendered');
30369             this.on('childrenrendered', function() {
30370                 Roo.log('children rendered');
30371                 _this.initial();
30372             } ,this);
30373         }
30374     },
30375     
30376     initial : function()
30377     {
30378         this.selectedBrick = [];
30379         
30380         this.currentSize = this.el.getBox(true);
30381         
30382         Roo.EventManager.onWindowResize(this.resize, this); 
30383
30384         if(!this.isAutoInitial){
30385             this.layout();
30386             return;
30387         }
30388         
30389         this.layout();
30390         
30391         return;
30392         //this.layout.defer(500,this);
30393         
30394     },
30395     
30396     resize : function()
30397     {
30398         var cs = this.el.getBox(true);
30399         
30400         if (
30401                 this.currentSize.width == cs.width && 
30402                 this.currentSize.x == cs.x && 
30403                 this.currentSize.height == cs.height && 
30404                 this.currentSize.y == cs.y 
30405         ) {
30406             Roo.log("no change in with or X or Y");
30407             return;
30408         }
30409         
30410         this.currentSize = cs;
30411         
30412         this.layout();
30413         
30414     },
30415     
30416     layout : function()
30417     {   
30418         this._resetLayout();
30419         
30420         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30421         
30422         this.layoutItems( isInstant );
30423       
30424         this._isLayoutInited = true;
30425         
30426         this.fireEvent('layout', this);
30427         
30428     },
30429     
30430     _resetLayout : function()
30431     {
30432         if(this.isHorizontal){
30433             this.horizontalMeasureColumns();
30434             return;
30435         }
30436         
30437         this.verticalMeasureColumns();
30438         
30439     },
30440     
30441     verticalMeasureColumns : function()
30442     {
30443         this.getContainerWidth();
30444         
30445 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30446 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30447 //            return;
30448 //        }
30449         
30450         var boxWidth = this.boxWidth + this.padWidth;
30451         
30452         if(this.containerWidth < this.boxWidth){
30453             boxWidth = this.containerWidth
30454         }
30455         
30456         var containerWidth = this.containerWidth;
30457         
30458         var cols = Math.floor(containerWidth / boxWidth);
30459         
30460         this.cols = Math.max( cols, 1 );
30461         
30462         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30463         
30464         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30465         
30466         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30467         
30468         this.colWidth = boxWidth + avail - this.padWidth;
30469         
30470         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30471         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30472     },
30473     
30474     horizontalMeasureColumns : function()
30475     {
30476         this.getContainerWidth();
30477         
30478         var boxWidth = this.boxWidth;
30479         
30480         if(this.containerWidth < boxWidth){
30481             boxWidth = this.containerWidth;
30482         }
30483         
30484         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30485         
30486         this.el.setHeight(boxWidth);
30487         
30488     },
30489     
30490     getContainerWidth : function()
30491     {
30492         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30493     },
30494     
30495     layoutItems : function( isInstant )
30496     {
30497         Roo.log(this.bricks);
30498         
30499         var items = Roo.apply([], this.bricks);
30500         
30501         if(this.isHorizontal){
30502             this._horizontalLayoutItems( items , isInstant );
30503             return;
30504         }
30505         
30506 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30507 //            this._verticalAlternativeLayoutItems( items , isInstant );
30508 //            return;
30509 //        }
30510         
30511         this._verticalLayoutItems( items , isInstant );
30512         
30513     },
30514     
30515     _verticalLayoutItems : function ( items , isInstant)
30516     {
30517         if ( !items || !items.length ) {
30518             return;
30519         }
30520         
30521         var standard = [
30522             ['xs', 'xs', 'xs', 'tall'],
30523             ['xs', 'xs', 'tall'],
30524             ['xs', 'xs', 'sm'],
30525             ['xs', 'xs', 'xs'],
30526             ['xs', 'tall'],
30527             ['xs', 'sm'],
30528             ['xs', 'xs'],
30529             ['xs'],
30530             
30531             ['sm', 'xs', 'xs'],
30532             ['sm', 'xs'],
30533             ['sm'],
30534             
30535             ['tall', 'xs', 'xs', 'xs'],
30536             ['tall', 'xs', 'xs'],
30537             ['tall', 'xs'],
30538             ['tall']
30539             
30540         ];
30541         
30542         var queue = [];
30543         
30544         var boxes = [];
30545         
30546         var box = [];
30547         
30548         Roo.each(items, function(item, k){
30549             
30550             switch (item.size) {
30551                 // these layouts take up a full box,
30552                 case 'md' :
30553                 case 'md-left' :
30554                 case 'md-right' :
30555                 case 'wide' :
30556                     
30557                     if(box.length){
30558                         boxes.push(box);
30559                         box = [];
30560                     }
30561                     
30562                     boxes.push([item]);
30563                     
30564                     break;
30565                     
30566                 case 'xs' :
30567                 case 'sm' :
30568                 case 'tall' :
30569                     
30570                     box.push(item);
30571                     
30572                     break;
30573                 default :
30574                     break;
30575                     
30576             }
30577             
30578         }, this);
30579         
30580         if(box.length){
30581             boxes.push(box);
30582             box = [];
30583         }
30584         
30585         var filterPattern = function(box, length)
30586         {
30587             if(!box.length){
30588                 return;
30589             }
30590             
30591             var match = false;
30592             
30593             var pattern = box.slice(0, length);
30594             
30595             var format = [];
30596             
30597             Roo.each(pattern, function(i){
30598                 format.push(i.size);
30599             }, this);
30600             
30601             Roo.each(standard, function(s){
30602                 
30603                 if(String(s) != String(format)){
30604                     return;
30605                 }
30606                 
30607                 match = true;
30608                 return false;
30609                 
30610             }, this);
30611             
30612             if(!match && length == 1){
30613                 return;
30614             }
30615             
30616             if(!match){
30617                 filterPattern(box, length - 1);
30618                 return;
30619             }
30620                 
30621             queue.push(pattern);
30622
30623             box = box.slice(length, box.length);
30624
30625             filterPattern(box, 4);
30626
30627             return;
30628             
30629         }
30630         
30631         Roo.each(boxes, function(box, k){
30632             
30633             if(!box.length){
30634                 return;
30635             }
30636             
30637             if(box.length == 1){
30638                 queue.push(box);
30639                 return;
30640             }
30641             
30642             filterPattern(box, 4);
30643             
30644         }, this);
30645         
30646         this._processVerticalLayoutQueue( queue, isInstant );
30647         
30648     },
30649     
30650 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30651 //    {
30652 //        if ( !items || !items.length ) {
30653 //            return;
30654 //        }
30655 //
30656 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30657 //        
30658 //    },
30659     
30660     _horizontalLayoutItems : function ( items , isInstant)
30661     {
30662         if ( !items || !items.length || items.length < 3) {
30663             return;
30664         }
30665         
30666         items.reverse();
30667         
30668         var eItems = items.slice(0, 3);
30669         
30670         items = items.slice(3, items.length);
30671         
30672         var standard = [
30673             ['xs', 'xs', 'xs', 'wide'],
30674             ['xs', 'xs', 'wide'],
30675             ['xs', 'xs', 'sm'],
30676             ['xs', 'xs', 'xs'],
30677             ['xs', 'wide'],
30678             ['xs', 'sm'],
30679             ['xs', 'xs'],
30680             ['xs'],
30681             
30682             ['sm', 'xs', 'xs'],
30683             ['sm', 'xs'],
30684             ['sm'],
30685             
30686             ['wide', 'xs', 'xs', 'xs'],
30687             ['wide', 'xs', 'xs'],
30688             ['wide', 'xs'],
30689             ['wide'],
30690             
30691             ['wide-thin']
30692         ];
30693         
30694         var queue = [];
30695         
30696         var boxes = [];
30697         
30698         var box = [];
30699         
30700         Roo.each(items, function(item, k){
30701             
30702             switch (item.size) {
30703                 case 'md' :
30704                 case 'md-left' :
30705                 case 'md-right' :
30706                 case 'tall' :
30707                     
30708                     if(box.length){
30709                         boxes.push(box);
30710                         box = [];
30711                     }
30712                     
30713                     boxes.push([item]);
30714                     
30715                     break;
30716                     
30717                 case 'xs' :
30718                 case 'sm' :
30719                 case 'wide' :
30720                 case 'wide-thin' :
30721                     
30722                     box.push(item);
30723                     
30724                     break;
30725                 default :
30726                     break;
30727                     
30728             }
30729             
30730         }, this);
30731         
30732         if(box.length){
30733             boxes.push(box);
30734             box = [];
30735         }
30736         
30737         var filterPattern = function(box, length)
30738         {
30739             if(!box.length){
30740                 return;
30741             }
30742             
30743             var match = false;
30744             
30745             var pattern = box.slice(0, length);
30746             
30747             var format = [];
30748             
30749             Roo.each(pattern, function(i){
30750                 format.push(i.size);
30751             }, this);
30752             
30753             Roo.each(standard, function(s){
30754                 
30755                 if(String(s) != String(format)){
30756                     return;
30757                 }
30758                 
30759                 match = true;
30760                 return false;
30761                 
30762             }, this);
30763             
30764             if(!match && length == 1){
30765                 return;
30766             }
30767             
30768             if(!match){
30769                 filterPattern(box, length - 1);
30770                 return;
30771             }
30772                 
30773             queue.push(pattern);
30774
30775             box = box.slice(length, box.length);
30776
30777             filterPattern(box, 4);
30778
30779             return;
30780             
30781         }
30782         
30783         Roo.each(boxes, function(box, k){
30784             
30785             if(!box.length){
30786                 return;
30787             }
30788             
30789             if(box.length == 1){
30790                 queue.push(box);
30791                 return;
30792             }
30793             
30794             filterPattern(box, 4);
30795             
30796         }, this);
30797         
30798         
30799         var prune = [];
30800         
30801         var pos = this.el.getBox(true);
30802         
30803         var minX = pos.x;
30804         
30805         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30806         
30807         var hit_end = false;
30808         
30809         Roo.each(queue, function(box){
30810             
30811             if(hit_end){
30812                 
30813                 Roo.each(box, function(b){
30814                 
30815                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30816                     b.el.hide();
30817
30818                 }, this);
30819
30820                 return;
30821             }
30822             
30823             var mx = 0;
30824             
30825             Roo.each(box, function(b){
30826                 
30827                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30828                 b.el.show();
30829
30830                 mx = Math.max(mx, b.x);
30831                 
30832             }, this);
30833             
30834             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30835             
30836             if(maxX < minX){
30837                 
30838                 Roo.each(box, function(b){
30839                 
30840                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30841                     b.el.hide();
30842                     
30843                 }, this);
30844                 
30845                 hit_end = true;
30846                 
30847                 return;
30848             }
30849             
30850             prune.push(box);
30851             
30852         }, this);
30853         
30854         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30855     },
30856     
30857     /** Sets position of item in DOM
30858     * @param {Element} item
30859     * @param {Number} x - horizontal position
30860     * @param {Number} y - vertical position
30861     * @param {Boolean} isInstant - disables transitions
30862     */
30863     _processVerticalLayoutQueue : function( queue, isInstant )
30864     {
30865         var pos = this.el.getBox(true);
30866         var x = pos.x;
30867         var y = pos.y;
30868         var maxY = [];
30869         
30870         for (var i = 0; i < this.cols; i++){
30871             maxY[i] = pos.y;
30872         }
30873         
30874         Roo.each(queue, function(box, k){
30875             
30876             var col = k % this.cols;
30877             
30878             Roo.each(box, function(b,kk){
30879                 
30880                 b.el.position('absolute');
30881                 
30882                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30883                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30884                 
30885                 if(b.size == 'md-left' || b.size == 'md-right'){
30886                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30887                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30888                 }
30889                 
30890                 b.el.setWidth(width);
30891                 b.el.setHeight(height);
30892                 // iframe?
30893                 b.el.select('iframe',true).setSize(width,height);
30894                 
30895             }, this);
30896             
30897             for (var i = 0; i < this.cols; i++){
30898                 
30899                 if(maxY[i] < maxY[col]){
30900                     col = i;
30901                     continue;
30902                 }
30903                 
30904                 col = Math.min(col, i);
30905                 
30906             }
30907             
30908             x = pos.x + col * (this.colWidth + this.padWidth);
30909             
30910             y = maxY[col];
30911             
30912             var positions = [];
30913             
30914             switch (box.length){
30915                 case 1 :
30916                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30917                     break;
30918                 case 2 :
30919                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30920                     break;
30921                 case 3 :
30922                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30923                     break;
30924                 case 4 :
30925                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30926                     break;
30927                 default :
30928                     break;
30929             }
30930             
30931             Roo.each(box, function(b,kk){
30932                 
30933                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30934                 
30935                 var sz = b.el.getSize();
30936                 
30937                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30938                 
30939             }, this);
30940             
30941         }, this);
30942         
30943         var mY = 0;
30944         
30945         for (var i = 0; i < this.cols; i++){
30946             mY = Math.max(mY, maxY[i]);
30947         }
30948         
30949         this.el.setHeight(mY - pos.y);
30950         
30951     },
30952     
30953 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30954 //    {
30955 //        var pos = this.el.getBox(true);
30956 //        var x = pos.x;
30957 //        var y = pos.y;
30958 //        var maxX = pos.right;
30959 //        
30960 //        var maxHeight = 0;
30961 //        
30962 //        Roo.each(items, function(item, k){
30963 //            
30964 //            var c = k % 2;
30965 //            
30966 //            item.el.position('absolute');
30967 //                
30968 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30969 //
30970 //            item.el.setWidth(width);
30971 //
30972 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30973 //
30974 //            item.el.setHeight(height);
30975 //            
30976 //            if(c == 0){
30977 //                item.el.setXY([x, y], isInstant ? false : true);
30978 //            } else {
30979 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30980 //            }
30981 //            
30982 //            y = y + height + this.alternativePadWidth;
30983 //            
30984 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30985 //            
30986 //        }, this);
30987 //        
30988 //        this.el.setHeight(maxHeight);
30989 //        
30990 //    },
30991     
30992     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30993     {
30994         var pos = this.el.getBox(true);
30995         
30996         var minX = pos.x;
30997         var minY = pos.y;
30998         
30999         var maxX = pos.right;
31000         
31001         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31002         
31003         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31004         
31005         Roo.each(queue, function(box, k){
31006             
31007             Roo.each(box, function(b, kk){
31008                 
31009                 b.el.position('absolute');
31010                 
31011                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31012                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31013                 
31014                 if(b.size == 'md-left' || b.size == 'md-right'){
31015                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31016                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31017                 }
31018                 
31019                 b.el.setWidth(width);
31020                 b.el.setHeight(height);
31021                 
31022             }, this);
31023             
31024             if(!box.length){
31025                 return;
31026             }
31027             
31028             var positions = [];
31029             
31030             switch (box.length){
31031                 case 1 :
31032                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31033                     break;
31034                 case 2 :
31035                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31036                     break;
31037                 case 3 :
31038                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31039                     break;
31040                 case 4 :
31041                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31042                     break;
31043                 default :
31044                     break;
31045             }
31046             
31047             Roo.each(box, function(b,kk){
31048                 
31049                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31050                 
31051                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31052                 
31053             }, this);
31054             
31055         }, this);
31056         
31057     },
31058     
31059     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31060     {
31061         Roo.each(eItems, function(b,k){
31062             
31063             b.size = (k == 0) ? 'sm' : 'xs';
31064             b.x = (k == 0) ? 2 : 1;
31065             b.y = (k == 0) ? 2 : 1;
31066             
31067             b.el.position('absolute');
31068             
31069             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31070                 
31071             b.el.setWidth(width);
31072             
31073             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31074             
31075             b.el.setHeight(height);
31076             
31077         }, this);
31078
31079         var positions = [];
31080         
31081         positions.push({
31082             x : maxX - this.unitWidth * 2 - this.gutter,
31083             y : minY
31084         });
31085         
31086         positions.push({
31087             x : maxX - this.unitWidth,
31088             y : minY + (this.unitWidth + this.gutter) * 2
31089         });
31090         
31091         positions.push({
31092             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31093             y : minY
31094         });
31095         
31096         Roo.each(eItems, function(b,k){
31097             
31098             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31099
31100         }, this);
31101         
31102     },
31103     
31104     getVerticalOneBoxColPositions : function(x, y, box)
31105     {
31106         var pos = [];
31107         
31108         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31109         
31110         if(box[0].size == 'md-left'){
31111             rand = 0;
31112         }
31113         
31114         if(box[0].size == 'md-right'){
31115             rand = 1;
31116         }
31117         
31118         pos.push({
31119             x : x + (this.unitWidth + this.gutter) * rand,
31120             y : y
31121         });
31122         
31123         return pos;
31124     },
31125     
31126     getVerticalTwoBoxColPositions : function(x, y, box)
31127     {
31128         var pos = [];
31129         
31130         if(box[0].size == 'xs'){
31131             
31132             pos.push({
31133                 x : x,
31134                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31135             });
31136
31137             pos.push({
31138                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31139                 y : y
31140             });
31141             
31142             return pos;
31143             
31144         }
31145         
31146         pos.push({
31147             x : x,
31148             y : y
31149         });
31150
31151         pos.push({
31152             x : x + (this.unitWidth + this.gutter) * 2,
31153             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31154         });
31155         
31156         return pos;
31157         
31158     },
31159     
31160     getVerticalThreeBoxColPositions : function(x, y, box)
31161     {
31162         var pos = [];
31163         
31164         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31165             
31166             pos.push({
31167                 x : x,
31168                 y : y
31169             });
31170
31171             pos.push({
31172                 x : x + (this.unitWidth + this.gutter) * 1,
31173                 y : y
31174             });
31175             
31176             pos.push({
31177                 x : x + (this.unitWidth + this.gutter) * 2,
31178                 y : y
31179             });
31180             
31181             return pos;
31182             
31183         }
31184         
31185         if(box[0].size == 'xs' && box[1].size == 'xs'){
31186             
31187             pos.push({
31188                 x : x,
31189                 y : y
31190             });
31191
31192             pos.push({
31193                 x : x,
31194                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31195             });
31196             
31197             pos.push({
31198                 x : x + (this.unitWidth + this.gutter) * 1,
31199                 y : y
31200             });
31201             
31202             return pos;
31203             
31204         }
31205         
31206         pos.push({
31207             x : x,
31208             y : y
31209         });
31210
31211         pos.push({
31212             x : x + (this.unitWidth + this.gutter) * 2,
31213             y : y
31214         });
31215
31216         pos.push({
31217             x : x + (this.unitWidth + this.gutter) * 2,
31218             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31219         });
31220             
31221         return pos;
31222         
31223     },
31224     
31225     getVerticalFourBoxColPositions : function(x, y, box)
31226     {
31227         var pos = [];
31228         
31229         if(box[0].size == 'xs'){
31230             
31231             pos.push({
31232                 x : x,
31233                 y : y
31234             });
31235
31236             pos.push({
31237                 x : x,
31238                 y : y + (this.unitHeight + this.gutter) * 1
31239             });
31240             
31241             pos.push({
31242                 x : x,
31243                 y : y + (this.unitHeight + this.gutter) * 2
31244             });
31245             
31246             pos.push({
31247                 x : x + (this.unitWidth + this.gutter) * 1,
31248                 y : y
31249             });
31250             
31251             return pos;
31252             
31253         }
31254         
31255         pos.push({
31256             x : x,
31257             y : y
31258         });
31259
31260         pos.push({
31261             x : x + (this.unitWidth + this.gutter) * 2,
31262             y : y
31263         });
31264
31265         pos.push({
31266             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31267             y : y + (this.unitHeight + this.gutter) * 1
31268         });
31269
31270         pos.push({
31271             x : x + (this.unitWidth + this.gutter) * 2,
31272             y : y + (this.unitWidth + this.gutter) * 2
31273         });
31274
31275         return pos;
31276         
31277     },
31278     
31279     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31280     {
31281         var pos = [];
31282         
31283         if(box[0].size == 'md-left'){
31284             pos.push({
31285                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31286                 y : minY
31287             });
31288             
31289             return pos;
31290         }
31291         
31292         if(box[0].size == 'md-right'){
31293             pos.push({
31294                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31295                 y : minY + (this.unitWidth + this.gutter) * 1
31296             });
31297             
31298             return pos;
31299         }
31300         
31301         var rand = Math.floor(Math.random() * (4 - box[0].y));
31302         
31303         pos.push({
31304             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31305             y : minY + (this.unitWidth + this.gutter) * rand
31306         });
31307         
31308         return pos;
31309         
31310     },
31311     
31312     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31313     {
31314         var pos = [];
31315         
31316         if(box[0].size == 'xs'){
31317             
31318             pos.push({
31319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31320                 y : minY
31321             });
31322
31323             pos.push({
31324                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31325                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31326             });
31327             
31328             return pos;
31329             
31330         }
31331         
31332         pos.push({
31333             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31334             y : minY
31335         });
31336
31337         pos.push({
31338             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31339             y : minY + (this.unitWidth + this.gutter) * 2
31340         });
31341         
31342         return pos;
31343         
31344     },
31345     
31346     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31347     {
31348         var pos = [];
31349         
31350         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31351             
31352             pos.push({
31353                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31354                 y : minY
31355             });
31356
31357             pos.push({
31358                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31359                 y : minY + (this.unitWidth + this.gutter) * 1
31360             });
31361             
31362             pos.push({
31363                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31364                 y : minY + (this.unitWidth + this.gutter) * 2
31365             });
31366             
31367             return pos;
31368             
31369         }
31370         
31371         if(box[0].size == 'xs' && box[1].size == 'xs'){
31372             
31373             pos.push({
31374                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31375                 y : minY
31376             });
31377
31378             pos.push({
31379                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31380                 y : minY
31381             });
31382             
31383             pos.push({
31384                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31385                 y : minY + (this.unitWidth + this.gutter) * 1
31386             });
31387             
31388             return pos;
31389             
31390         }
31391         
31392         pos.push({
31393             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31394             y : minY
31395         });
31396
31397         pos.push({
31398             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31399             y : minY + (this.unitWidth + this.gutter) * 2
31400         });
31401
31402         pos.push({
31403             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31404             y : minY + (this.unitWidth + this.gutter) * 2
31405         });
31406             
31407         return pos;
31408         
31409     },
31410     
31411     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31412     {
31413         var pos = [];
31414         
31415         if(box[0].size == 'xs'){
31416             
31417             pos.push({
31418                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31419                 y : minY
31420             });
31421
31422             pos.push({
31423                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31424                 y : minY
31425             });
31426             
31427             pos.push({
31428                 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),
31429                 y : minY
31430             });
31431             
31432             pos.push({
31433                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31434                 y : minY + (this.unitWidth + this.gutter) * 1
31435             });
31436             
31437             return pos;
31438             
31439         }
31440         
31441         pos.push({
31442             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31443             y : minY
31444         });
31445         
31446         pos.push({
31447             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31448             y : minY + (this.unitWidth + this.gutter) * 2
31449         });
31450         
31451         pos.push({
31452             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31453             y : minY + (this.unitWidth + this.gutter) * 2
31454         });
31455         
31456         pos.push({
31457             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),
31458             y : minY + (this.unitWidth + this.gutter) * 2
31459         });
31460
31461         return pos;
31462         
31463     },
31464     
31465     /**
31466     * remove a Masonry Brick
31467     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31468     */
31469     removeBrick : function(brick_id)
31470     {
31471         if (!brick_id) {
31472             return;
31473         }
31474         
31475         for (var i = 0; i<this.bricks.length; i++) {
31476             if (this.bricks[i].id == brick_id) {
31477                 this.bricks.splice(i,1);
31478                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31479                 this.initial();
31480             }
31481         }
31482     },
31483     
31484     /**
31485     * adds a Masonry Brick
31486     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31487     */
31488     addBrick : function(cfg)
31489     {
31490         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31491         //this.register(cn);
31492         cn.parentId = this.id;
31493         cn.onRender(this.el, null);
31494         return cn;
31495     },
31496     
31497     /**
31498     * register a Masonry Brick
31499     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31500     */
31501     
31502     register : function(brick)
31503     {
31504         this.bricks.push(brick);
31505         brick.masonryId = this.id;
31506     },
31507     
31508     /**
31509     * clear all the Masonry Brick
31510     */
31511     clearAll : function()
31512     {
31513         this.bricks = [];
31514         //this.getChildContainer().dom.innerHTML = "";
31515         this.el.dom.innerHTML = '';
31516     },
31517     
31518     getSelected : function()
31519     {
31520         if (!this.selectedBrick) {
31521             return false;
31522         }
31523         
31524         return this.selectedBrick;
31525     }
31526 });
31527
31528 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31529     
31530     groups: {},
31531      /**
31532     * register a Masonry Layout
31533     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31534     */
31535     
31536     register : function(layout)
31537     {
31538         this.groups[layout.id] = layout;
31539     },
31540     /**
31541     * fetch a  Masonry Layout based on the masonry layout ID
31542     * @param {string} the masonry layout to add
31543     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31544     */
31545     
31546     get: function(layout_id) {
31547         if (typeof(this.groups[layout_id]) == 'undefined') {
31548             return false;
31549         }
31550         return this.groups[layout_id] ;
31551     }
31552     
31553     
31554     
31555 });
31556
31557  
31558
31559  /**
31560  *
31561  * This is based on 
31562  * http://masonry.desandro.com
31563  *
31564  * The idea is to render all the bricks based on vertical width...
31565  *
31566  * The original code extends 'outlayer' - we might need to use that....
31567  * 
31568  */
31569
31570
31571 /**
31572  * @class Roo.bootstrap.LayoutMasonryAuto
31573  * @extends Roo.bootstrap.Component
31574  * Bootstrap Layout Masonry class
31575  * 
31576  * @constructor
31577  * Create a new Element
31578  * @param {Object} config The config object
31579  */
31580
31581 Roo.bootstrap.LayoutMasonryAuto = function(config){
31582     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31583 };
31584
31585 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31586     
31587       /**
31588      * @cfg {Boolean} isFitWidth  - resize the width..
31589      */   
31590     isFitWidth : false,  // options..
31591     /**
31592      * @cfg {Boolean} isOriginLeft = left align?
31593      */   
31594     isOriginLeft : true,
31595     /**
31596      * @cfg {Boolean} isOriginTop = top align?
31597      */   
31598     isOriginTop : false,
31599     /**
31600      * @cfg {Boolean} isLayoutInstant = no animation?
31601      */   
31602     isLayoutInstant : false, // needed?
31603     /**
31604      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31605      */   
31606     isResizingContainer : true,
31607     /**
31608      * @cfg {Number} columnWidth  width of the columns 
31609      */   
31610     
31611     columnWidth : 0,
31612     
31613     /**
31614      * @cfg {Number} maxCols maximum number of columns
31615      */   
31616     
31617     maxCols: 0,
31618     /**
31619      * @cfg {Number} padHeight padding below box..
31620      */   
31621     
31622     padHeight : 10, 
31623     
31624     /**
31625      * @cfg {Boolean} isAutoInitial defalut true
31626      */   
31627     
31628     isAutoInitial : true, 
31629     
31630     // private?
31631     gutter : 0,
31632     
31633     containerWidth: 0,
31634     initialColumnWidth : 0,
31635     currentSize : null,
31636     
31637     colYs : null, // array.
31638     maxY : 0,
31639     padWidth: 10,
31640     
31641     
31642     tag: 'div',
31643     cls: '',
31644     bricks: null, //CompositeElement
31645     cols : 0, // array?
31646     // element : null, // wrapped now this.el
31647     _isLayoutInited : null, 
31648     
31649     
31650     getAutoCreate : function(){
31651         
31652         var cfg = {
31653             tag: this.tag,
31654             cls: 'blog-masonary-wrapper ' + this.cls,
31655             cn : {
31656                 cls : 'mas-boxes masonary'
31657             }
31658         };
31659         
31660         return cfg;
31661     },
31662     
31663     getChildContainer: function( )
31664     {
31665         if (this.boxesEl) {
31666             return this.boxesEl;
31667         }
31668         
31669         this.boxesEl = this.el.select('.mas-boxes').first();
31670         
31671         return this.boxesEl;
31672     },
31673     
31674     
31675     initEvents : function()
31676     {
31677         var _this = this;
31678         
31679         if(this.isAutoInitial){
31680             Roo.log('hook children rendered');
31681             this.on('childrenrendered', function() {
31682                 Roo.log('children rendered');
31683                 _this.initial();
31684             } ,this);
31685         }
31686         
31687     },
31688     
31689     initial : function()
31690     {
31691         this.reloadItems();
31692
31693         this.currentSize = this.el.getBox(true);
31694
31695         /// was window resize... - let's see if this works..
31696         Roo.EventManager.onWindowResize(this.resize, this); 
31697
31698         if(!this.isAutoInitial){
31699             this.layout();
31700             return;
31701         }
31702         
31703         this.layout.defer(500,this);
31704     },
31705     
31706     reloadItems: function()
31707     {
31708         this.bricks = this.el.select('.masonry-brick', true);
31709         
31710         this.bricks.each(function(b) {
31711             //Roo.log(b.getSize());
31712             if (!b.attr('originalwidth')) {
31713                 b.attr('originalwidth',  b.getSize().width);
31714             }
31715             
31716         });
31717         
31718         Roo.log(this.bricks.elements.length);
31719     },
31720     
31721     resize : function()
31722     {
31723         Roo.log('resize');
31724         var cs = this.el.getBox(true);
31725         
31726         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31727             Roo.log("no change in with or X");
31728             return;
31729         }
31730         this.currentSize = cs;
31731         this.layout();
31732     },
31733     
31734     layout : function()
31735     {
31736          Roo.log('layout');
31737         this._resetLayout();
31738         //this._manageStamps();
31739       
31740         // don't animate first layout
31741         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31742         this.layoutItems( isInstant );
31743       
31744         // flag for initalized
31745         this._isLayoutInited = true;
31746     },
31747     
31748     layoutItems : function( isInstant )
31749     {
31750         //var items = this._getItemsForLayout( this.items );
31751         // original code supports filtering layout items.. we just ignore it..
31752         
31753         this._layoutItems( this.bricks , isInstant );
31754       
31755         this._postLayout();
31756     },
31757     _layoutItems : function ( items , isInstant)
31758     {
31759        //this.fireEvent( 'layout', this, items );
31760     
31761
31762         if ( !items || !items.elements.length ) {
31763           // no items, emit event with empty array
31764             return;
31765         }
31766
31767         var queue = [];
31768         items.each(function(item) {
31769             Roo.log("layout item");
31770             Roo.log(item);
31771             // get x/y object from method
31772             var position = this._getItemLayoutPosition( item );
31773             // enqueue
31774             position.item = item;
31775             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31776             queue.push( position );
31777         }, this);
31778       
31779         this._processLayoutQueue( queue );
31780     },
31781     /** Sets position of item in DOM
31782     * @param {Element} item
31783     * @param {Number} x - horizontal position
31784     * @param {Number} y - vertical position
31785     * @param {Boolean} isInstant - disables transitions
31786     */
31787     _processLayoutQueue : function( queue )
31788     {
31789         for ( var i=0, len = queue.length; i < len; i++ ) {
31790             var obj = queue[i];
31791             obj.item.position('absolute');
31792             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31793         }
31794     },
31795       
31796     
31797     /**
31798     * Any logic you want to do after each layout,
31799     * i.e. size the container
31800     */
31801     _postLayout : function()
31802     {
31803         this.resizeContainer();
31804     },
31805     
31806     resizeContainer : function()
31807     {
31808         if ( !this.isResizingContainer ) {
31809             return;
31810         }
31811         var size = this._getContainerSize();
31812         if ( size ) {
31813             this.el.setSize(size.width,size.height);
31814             this.boxesEl.setSize(size.width,size.height);
31815         }
31816     },
31817     
31818     
31819     
31820     _resetLayout : function()
31821     {
31822         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31823         this.colWidth = this.el.getWidth();
31824         //this.gutter = this.el.getWidth(); 
31825         
31826         this.measureColumns();
31827
31828         // reset column Y
31829         var i = this.cols;
31830         this.colYs = [];
31831         while (i--) {
31832             this.colYs.push( 0 );
31833         }
31834     
31835         this.maxY = 0;
31836     },
31837
31838     measureColumns : function()
31839     {
31840         this.getContainerWidth();
31841       // if columnWidth is 0, default to outerWidth of first item
31842         if ( !this.columnWidth ) {
31843             var firstItem = this.bricks.first();
31844             Roo.log(firstItem);
31845             this.columnWidth  = this.containerWidth;
31846             if (firstItem && firstItem.attr('originalwidth') ) {
31847                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31848             }
31849             // columnWidth fall back to item of first element
31850             Roo.log("set column width?");
31851                         this.initialColumnWidth = this.columnWidth  ;
31852
31853             // if first elem has no width, default to size of container
31854             
31855         }
31856         
31857         
31858         if (this.initialColumnWidth) {
31859             this.columnWidth = this.initialColumnWidth;
31860         }
31861         
31862         
31863             
31864         // column width is fixed at the top - however if container width get's smaller we should
31865         // reduce it...
31866         
31867         // this bit calcs how man columns..
31868             
31869         var columnWidth = this.columnWidth += this.gutter;
31870       
31871         // calculate columns
31872         var containerWidth = this.containerWidth + this.gutter;
31873         
31874         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31875         // fix rounding errors, typically with gutters
31876         var excess = columnWidth - containerWidth % columnWidth;
31877         
31878         
31879         // if overshoot is less than a pixel, round up, otherwise floor it
31880         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31881         cols = Math[ mathMethod ]( cols );
31882         this.cols = Math.max( cols, 1 );
31883         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31884         
31885          // padding positioning..
31886         var totalColWidth = this.cols * this.columnWidth;
31887         var padavail = this.containerWidth - totalColWidth;
31888         // so for 2 columns - we need 3 'pads'
31889         
31890         var padNeeded = (1+this.cols) * this.padWidth;
31891         
31892         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31893         
31894         this.columnWidth += padExtra
31895         //this.padWidth = Math.floor(padavail /  ( this.cols));
31896         
31897         // adjust colum width so that padding is fixed??
31898         
31899         // we have 3 columns ... total = width * 3
31900         // we have X left over... that should be used by 
31901         
31902         //if (this.expandC) {
31903             
31904         //}
31905         
31906         
31907         
31908     },
31909     
31910     getContainerWidth : function()
31911     {
31912        /* // container is parent if fit width
31913         var container = this.isFitWidth ? this.element.parentNode : this.element;
31914         // check that this.size and size are there
31915         // IE8 triggers resize on body size change, so they might not be
31916         
31917         var size = getSize( container );  //FIXME
31918         this.containerWidth = size && size.innerWidth; //FIXME
31919         */
31920          
31921         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31922         
31923     },
31924     
31925     _getItemLayoutPosition : function( item )  // what is item?
31926     {
31927         // we resize the item to our columnWidth..
31928       
31929         item.setWidth(this.columnWidth);
31930         item.autoBoxAdjust  = false;
31931         
31932         var sz = item.getSize();
31933  
31934         // how many columns does this brick span
31935         var remainder = this.containerWidth % this.columnWidth;
31936         
31937         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31938         // round if off by 1 pixel, otherwise use ceil
31939         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31940         colSpan = Math.min( colSpan, this.cols );
31941         
31942         // normally this should be '1' as we dont' currently allow multi width columns..
31943         
31944         var colGroup = this._getColGroup( colSpan );
31945         // get the minimum Y value from the columns
31946         var minimumY = Math.min.apply( Math, colGroup );
31947         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31948         
31949         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31950          
31951         // position the brick
31952         var position = {
31953             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31954             y: this.currentSize.y + minimumY + this.padHeight
31955         };
31956         
31957         Roo.log(position);
31958         // apply setHeight to necessary columns
31959         var setHeight = minimumY + sz.height + this.padHeight;
31960         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31961         
31962         var setSpan = this.cols + 1 - colGroup.length;
31963         for ( var i = 0; i < setSpan; i++ ) {
31964           this.colYs[ shortColIndex + i ] = setHeight ;
31965         }
31966       
31967         return position;
31968     },
31969     
31970     /**
31971      * @param {Number} colSpan - number of columns the element spans
31972      * @returns {Array} colGroup
31973      */
31974     _getColGroup : function( colSpan )
31975     {
31976         if ( colSpan < 2 ) {
31977           // if brick spans only one column, use all the column Ys
31978           return this.colYs;
31979         }
31980       
31981         var colGroup = [];
31982         // how many different places could this brick fit horizontally
31983         var groupCount = this.cols + 1 - colSpan;
31984         // for each group potential horizontal position
31985         for ( var i = 0; i < groupCount; i++ ) {
31986           // make an array of colY values for that one group
31987           var groupColYs = this.colYs.slice( i, i + colSpan );
31988           // and get the max value of the array
31989           colGroup[i] = Math.max.apply( Math, groupColYs );
31990         }
31991         return colGroup;
31992     },
31993     /*
31994     _manageStamp : function( stamp )
31995     {
31996         var stampSize =  stamp.getSize();
31997         var offset = stamp.getBox();
31998         // get the columns that this stamp affects
31999         var firstX = this.isOriginLeft ? offset.x : offset.right;
32000         var lastX = firstX + stampSize.width;
32001         var firstCol = Math.floor( firstX / this.columnWidth );
32002         firstCol = Math.max( 0, firstCol );
32003         
32004         var lastCol = Math.floor( lastX / this.columnWidth );
32005         // lastCol should not go over if multiple of columnWidth #425
32006         lastCol -= lastX % this.columnWidth ? 0 : 1;
32007         lastCol = Math.min( this.cols - 1, lastCol );
32008         
32009         // set colYs to bottom of the stamp
32010         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32011             stampSize.height;
32012             
32013         for ( var i = firstCol; i <= lastCol; i++ ) {
32014           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32015         }
32016     },
32017     */
32018     
32019     _getContainerSize : function()
32020     {
32021         this.maxY = Math.max.apply( Math, this.colYs );
32022         var size = {
32023             height: this.maxY
32024         };
32025       
32026         if ( this.isFitWidth ) {
32027             size.width = this._getContainerFitWidth();
32028         }
32029       
32030         return size;
32031     },
32032     
32033     _getContainerFitWidth : function()
32034     {
32035         var unusedCols = 0;
32036         // count unused columns
32037         var i = this.cols;
32038         while ( --i ) {
32039           if ( this.colYs[i] !== 0 ) {
32040             break;
32041           }
32042           unusedCols++;
32043         }
32044         // fit container to columns that have been used
32045         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32046     },
32047     
32048     needsResizeLayout : function()
32049     {
32050         var previousWidth = this.containerWidth;
32051         this.getContainerWidth();
32052         return previousWidth !== this.containerWidth;
32053     }
32054  
32055 });
32056
32057  
32058
32059  /*
32060  * - LGPL
32061  *
32062  * element
32063  * 
32064  */
32065
32066 /**
32067  * @class Roo.bootstrap.MasonryBrick
32068  * @extends Roo.bootstrap.Component
32069  * Bootstrap MasonryBrick class
32070  * 
32071  * @constructor
32072  * Create a new MasonryBrick
32073  * @param {Object} config The config object
32074  */
32075
32076 Roo.bootstrap.MasonryBrick = function(config){
32077     
32078     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32079     
32080     Roo.bootstrap.MasonryBrick.register(this);
32081     
32082     this.addEvents({
32083         // raw events
32084         /**
32085          * @event click
32086          * When a MasonryBrick is clcik
32087          * @param {Roo.bootstrap.MasonryBrick} this
32088          * @param {Roo.EventObject} e
32089          */
32090         "click" : true
32091     });
32092 };
32093
32094 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32095     
32096     /**
32097      * @cfg {String} title
32098      */   
32099     title : '',
32100     /**
32101      * @cfg {String} html
32102      */   
32103     html : '',
32104     /**
32105      * @cfg {String} bgimage
32106      */   
32107     bgimage : '',
32108     /**
32109      * @cfg {String} videourl
32110      */   
32111     videourl : '',
32112     /**
32113      * @cfg {String} cls
32114      */   
32115     cls : '',
32116     /**
32117      * @cfg {String} href
32118      */   
32119     href : '',
32120     /**
32121      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32122      */   
32123     size : 'xs',
32124     
32125     /**
32126      * @cfg {String} placetitle (center|bottom)
32127      */   
32128     placetitle : '',
32129     
32130     /**
32131      * @cfg {Boolean} isFitContainer defalut true
32132      */   
32133     isFitContainer : true, 
32134     
32135     /**
32136      * @cfg {Boolean} preventDefault defalut false
32137      */   
32138     preventDefault : false, 
32139     
32140     /**
32141      * @cfg {Boolean} inverse defalut false
32142      */   
32143     maskInverse : false, 
32144     
32145     getAutoCreate : function()
32146     {
32147         if(!this.isFitContainer){
32148             return this.getSplitAutoCreate();
32149         }
32150         
32151         var cls = 'masonry-brick masonry-brick-full';
32152         
32153         if(this.href.length){
32154             cls += ' masonry-brick-link';
32155         }
32156         
32157         if(this.bgimage.length){
32158             cls += ' masonry-brick-image';
32159         }
32160         
32161         if(this.maskInverse){
32162             cls += ' mask-inverse';
32163         }
32164         
32165         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32166             cls += ' enable-mask';
32167         }
32168         
32169         if(this.size){
32170             cls += ' masonry-' + this.size + '-brick';
32171         }
32172         
32173         if(this.placetitle.length){
32174             
32175             switch (this.placetitle) {
32176                 case 'center' :
32177                     cls += ' masonry-center-title';
32178                     break;
32179                 case 'bottom' :
32180                     cls += ' masonry-bottom-title';
32181                     break;
32182                 default:
32183                     break;
32184             }
32185             
32186         } else {
32187             if(!this.html.length && !this.bgimage.length){
32188                 cls += ' masonry-center-title';
32189             }
32190
32191             if(!this.html.length && this.bgimage.length){
32192                 cls += ' masonry-bottom-title';
32193             }
32194         }
32195         
32196         if(this.cls){
32197             cls += ' ' + this.cls;
32198         }
32199         
32200         var cfg = {
32201             tag: (this.href.length) ? 'a' : 'div',
32202             cls: cls,
32203             cn: [
32204                 {
32205                     tag: 'div',
32206                     cls: 'masonry-brick-mask'
32207                 },
32208                 {
32209                     tag: 'div',
32210                     cls: 'masonry-brick-paragraph',
32211                     cn: []
32212                 }
32213             ]
32214         };
32215         
32216         if(this.href.length){
32217             cfg.href = this.href;
32218         }
32219         
32220         var cn = cfg.cn[1].cn;
32221         
32222         if(this.title.length){
32223             cn.push({
32224                 tag: 'h4',
32225                 cls: 'masonry-brick-title',
32226                 html: this.title
32227             });
32228         }
32229         
32230         if(this.html.length){
32231             cn.push({
32232                 tag: 'p',
32233                 cls: 'masonry-brick-text',
32234                 html: this.html
32235             });
32236         }
32237         
32238         if (!this.title.length && !this.html.length) {
32239             cfg.cn[1].cls += ' hide';
32240         }
32241         
32242         if(this.bgimage.length){
32243             cfg.cn.push({
32244                 tag: 'img',
32245                 cls: 'masonry-brick-image-view',
32246                 src: this.bgimage
32247             });
32248         }
32249         
32250         if(this.videourl.length){
32251             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32252             // youtube support only?
32253             cfg.cn.push({
32254                 tag: 'iframe',
32255                 cls: 'masonry-brick-image-view',
32256                 src: vurl,
32257                 frameborder : 0,
32258                 allowfullscreen : true
32259             });
32260         }
32261         
32262         return cfg;
32263         
32264     },
32265     
32266     getSplitAutoCreate : function()
32267     {
32268         var cls = 'masonry-brick masonry-brick-split';
32269         
32270         if(this.href.length){
32271             cls += ' masonry-brick-link';
32272         }
32273         
32274         if(this.bgimage.length){
32275             cls += ' masonry-brick-image';
32276         }
32277         
32278         if(this.size){
32279             cls += ' masonry-' + this.size + '-brick';
32280         }
32281         
32282         switch (this.placetitle) {
32283             case 'center' :
32284                 cls += ' masonry-center-title';
32285                 break;
32286             case 'bottom' :
32287                 cls += ' masonry-bottom-title';
32288                 break;
32289             default:
32290                 if(!this.bgimage.length){
32291                     cls += ' masonry-center-title';
32292                 }
32293
32294                 if(this.bgimage.length){
32295                     cls += ' masonry-bottom-title';
32296                 }
32297                 break;
32298         }
32299         
32300         if(this.cls){
32301             cls += ' ' + this.cls;
32302         }
32303         
32304         var cfg = {
32305             tag: (this.href.length) ? 'a' : 'div',
32306             cls: cls,
32307             cn: [
32308                 {
32309                     tag: 'div',
32310                     cls: 'masonry-brick-split-head',
32311                     cn: [
32312                         {
32313                             tag: 'div',
32314                             cls: 'masonry-brick-paragraph',
32315                             cn: []
32316                         }
32317                     ]
32318                 },
32319                 {
32320                     tag: 'div',
32321                     cls: 'masonry-brick-split-body',
32322                     cn: []
32323                 }
32324             ]
32325         };
32326         
32327         if(this.href.length){
32328             cfg.href = this.href;
32329         }
32330         
32331         if(this.title.length){
32332             cfg.cn[0].cn[0].cn.push({
32333                 tag: 'h4',
32334                 cls: 'masonry-brick-title',
32335                 html: this.title
32336             });
32337         }
32338         
32339         if(this.html.length){
32340             cfg.cn[1].cn.push({
32341                 tag: 'p',
32342                 cls: 'masonry-brick-text',
32343                 html: this.html
32344             });
32345         }
32346
32347         if(this.bgimage.length){
32348             cfg.cn[0].cn.push({
32349                 tag: 'img',
32350                 cls: 'masonry-brick-image-view',
32351                 src: this.bgimage
32352             });
32353         }
32354         
32355         if(this.videourl.length){
32356             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32357             // youtube support only?
32358             cfg.cn[0].cn.cn.push({
32359                 tag: 'iframe',
32360                 cls: 'masonry-brick-image-view',
32361                 src: vurl,
32362                 frameborder : 0,
32363                 allowfullscreen : true
32364             });
32365         }
32366         
32367         return cfg;
32368     },
32369     
32370     initEvents: function() 
32371     {
32372         switch (this.size) {
32373             case 'xs' :
32374                 this.x = 1;
32375                 this.y = 1;
32376                 break;
32377             case 'sm' :
32378                 this.x = 2;
32379                 this.y = 2;
32380                 break;
32381             case 'md' :
32382             case 'md-left' :
32383             case 'md-right' :
32384                 this.x = 3;
32385                 this.y = 3;
32386                 break;
32387             case 'tall' :
32388                 this.x = 2;
32389                 this.y = 3;
32390                 break;
32391             case 'wide' :
32392                 this.x = 3;
32393                 this.y = 2;
32394                 break;
32395             case 'wide-thin' :
32396                 this.x = 3;
32397                 this.y = 1;
32398                 break;
32399                         
32400             default :
32401                 break;
32402         }
32403         
32404         if(Roo.isTouch){
32405             this.el.on('touchstart', this.onTouchStart, this);
32406             this.el.on('touchmove', this.onTouchMove, this);
32407             this.el.on('touchend', this.onTouchEnd, this);
32408             this.el.on('contextmenu', this.onContextMenu, this);
32409         } else {
32410             this.el.on('mouseenter'  ,this.enter, this);
32411             this.el.on('mouseleave', this.leave, this);
32412             this.el.on('click', this.onClick, this);
32413         }
32414         
32415         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32416             this.parent().bricks.push(this);   
32417         }
32418         
32419     },
32420     
32421     onClick: function(e, el)
32422     {
32423         var time = this.endTimer - this.startTimer;
32424         // Roo.log(e.preventDefault());
32425         if(Roo.isTouch){
32426             if(time > 1000){
32427                 e.preventDefault();
32428                 return;
32429             }
32430         }
32431         
32432         if(!this.preventDefault){
32433             return;
32434         }
32435         
32436         e.preventDefault();
32437         
32438         if (this.activcClass != '') {
32439             this.selectBrick();
32440         }
32441         
32442         this.fireEvent('click', this);
32443     },
32444     
32445     enter: function(e, el)
32446     {
32447         e.preventDefault();
32448         
32449         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32450             return;
32451         }
32452         
32453         if(this.bgimage.length && this.html.length){
32454             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32455         }
32456     },
32457     
32458     leave: function(e, el)
32459     {
32460         e.preventDefault();
32461         
32462         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32463             return;
32464         }
32465         
32466         if(this.bgimage.length && this.html.length){
32467             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32468         }
32469     },
32470     
32471     onTouchStart: function(e, el)
32472     {
32473 //        e.preventDefault();
32474         
32475         this.touchmoved = false;
32476         
32477         if(!this.isFitContainer){
32478             return;
32479         }
32480         
32481         if(!this.bgimage.length || !this.html.length){
32482             return;
32483         }
32484         
32485         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32486         
32487         this.timer = new Date().getTime();
32488         
32489     },
32490     
32491     onTouchMove: function(e, el)
32492     {
32493         this.touchmoved = true;
32494     },
32495     
32496     onContextMenu : function(e,el)
32497     {
32498         e.preventDefault();
32499         e.stopPropagation();
32500         return false;
32501     },
32502     
32503     onTouchEnd: function(e, el)
32504     {
32505 //        e.preventDefault();
32506         
32507         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32508         
32509             this.leave(e,el);
32510             
32511             return;
32512         }
32513         
32514         if(!this.bgimage.length || !this.html.length){
32515             
32516             if(this.href.length){
32517                 window.location.href = this.href;
32518             }
32519             
32520             return;
32521         }
32522         
32523         if(!this.isFitContainer){
32524             return;
32525         }
32526         
32527         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32528         
32529         window.location.href = this.href;
32530     },
32531     
32532     //selection on single brick only
32533     selectBrick : function() {
32534         
32535         if (!this.parentId) {
32536             return;
32537         }
32538         
32539         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32540         var index = m.selectedBrick.indexOf(this.id);
32541         
32542         if ( index > -1) {
32543             m.selectedBrick.splice(index,1);
32544             this.el.removeClass(this.activeClass);
32545             return;
32546         }
32547         
32548         for(var i = 0; i < m.selectedBrick.length; i++) {
32549             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32550             b.el.removeClass(b.activeClass);
32551         }
32552         
32553         m.selectedBrick = [];
32554         
32555         m.selectedBrick.push(this.id);
32556         this.el.addClass(this.activeClass);
32557         return;
32558     }
32559     
32560 });
32561
32562 Roo.apply(Roo.bootstrap.MasonryBrick, {
32563     
32564     //groups: {},
32565     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32566      /**
32567     * register a Masonry Brick
32568     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32569     */
32570     
32571     register : function(brick)
32572     {
32573         //this.groups[brick.id] = brick;
32574         this.groups.add(brick.id, brick);
32575     },
32576     /**
32577     * fetch a  masonry brick based on the masonry brick ID
32578     * @param {string} the masonry brick to add
32579     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32580     */
32581     
32582     get: function(brick_id) 
32583     {
32584         // if (typeof(this.groups[brick_id]) == 'undefined') {
32585         //     return false;
32586         // }
32587         // return this.groups[brick_id] ;
32588         
32589         if(this.groups.key(brick_id)) {
32590             return this.groups.key(brick_id);
32591         }
32592         
32593         return false;
32594     }
32595     
32596     
32597     
32598 });
32599
32600  /*
32601  * - LGPL
32602  *
32603  * element
32604  * 
32605  */
32606
32607 /**
32608  * @class Roo.bootstrap.Brick
32609  * @extends Roo.bootstrap.Component
32610  * Bootstrap Brick class
32611  * 
32612  * @constructor
32613  * Create a new Brick
32614  * @param {Object} config The config object
32615  */
32616
32617 Roo.bootstrap.Brick = function(config){
32618     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32619     
32620     this.addEvents({
32621         // raw events
32622         /**
32623          * @event click
32624          * When a Brick is click
32625          * @param {Roo.bootstrap.Brick} this
32626          * @param {Roo.EventObject} e
32627          */
32628         "click" : true
32629     });
32630 };
32631
32632 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32633     
32634     /**
32635      * @cfg {String} title
32636      */   
32637     title : '',
32638     /**
32639      * @cfg {String} html
32640      */   
32641     html : '',
32642     /**
32643      * @cfg {String} bgimage
32644      */   
32645     bgimage : '',
32646     /**
32647      * @cfg {String} cls
32648      */   
32649     cls : '',
32650     /**
32651      * @cfg {String} href
32652      */   
32653     href : '',
32654     /**
32655      * @cfg {String} video
32656      */   
32657     video : '',
32658     /**
32659      * @cfg {Boolean} square
32660      */   
32661     square : true,
32662     
32663     getAutoCreate : function()
32664     {
32665         var cls = 'roo-brick';
32666         
32667         if(this.href.length){
32668             cls += ' roo-brick-link';
32669         }
32670         
32671         if(this.bgimage.length){
32672             cls += ' roo-brick-image';
32673         }
32674         
32675         if(!this.html.length && !this.bgimage.length){
32676             cls += ' roo-brick-center-title';
32677         }
32678         
32679         if(!this.html.length && this.bgimage.length){
32680             cls += ' roo-brick-bottom-title';
32681         }
32682         
32683         if(this.cls){
32684             cls += ' ' + this.cls;
32685         }
32686         
32687         var cfg = {
32688             tag: (this.href.length) ? 'a' : 'div',
32689             cls: cls,
32690             cn: [
32691                 {
32692                     tag: 'div',
32693                     cls: 'roo-brick-paragraph',
32694                     cn: []
32695                 }
32696             ]
32697         };
32698         
32699         if(this.href.length){
32700             cfg.href = this.href;
32701         }
32702         
32703         var cn = cfg.cn[0].cn;
32704         
32705         if(this.title.length){
32706             cn.push({
32707                 tag: 'h4',
32708                 cls: 'roo-brick-title',
32709                 html: this.title
32710             });
32711         }
32712         
32713         if(this.html.length){
32714             cn.push({
32715                 tag: 'p',
32716                 cls: 'roo-brick-text',
32717                 html: this.html
32718             });
32719         } else {
32720             cn.cls += ' hide';
32721         }
32722         
32723         if(this.bgimage.length){
32724             cfg.cn.push({
32725                 tag: 'img',
32726                 cls: 'roo-brick-image-view',
32727                 src: this.bgimage
32728             });
32729         }
32730         
32731         return cfg;
32732     },
32733     
32734     initEvents: function() 
32735     {
32736         if(this.title.length || this.html.length){
32737             this.el.on('mouseenter'  ,this.enter, this);
32738             this.el.on('mouseleave', this.leave, this);
32739         }
32740         
32741         Roo.EventManager.onWindowResize(this.resize, this); 
32742         
32743         if(this.bgimage.length){
32744             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32745             this.imageEl.on('load', this.onImageLoad, this);
32746             return;
32747         }
32748         
32749         this.resize();
32750     },
32751     
32752     onImageLoad : function()
32753     {
32754         this.resize();
32755     },
32756     
32757     resize : function()
32758     {
32759         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32760         
32761         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32762         
32763         if(this.bgimage.length){
32764             var image = this.el.select('.roo-brick-image-view', true).first();
32765             
32766             image.setWidth(paragraph.getWidth());
32767             
32768             if(this.square){
32769                 image.setHeight(paragraph.getWidth());
32770             }
32771             
32772             this.el.setHeight(image.getHeight());
32773             paragraph.setHeight(image.getHeight());
32774             
32775         }
32776         
32777     },
32778     
32779     enter: function(e, el)
32780     {
32781         e.preventDefault();
32782         
32783         if(this.bgimage.length){
32784             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32785             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32786         }
32787     },
32788     
32789     leave: function(e, el)
32790     {
32791         e.preventDefault();
32792         
32793         if(this.bgimage.length){
32794             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32795             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32796         }
32797     }
32798     
32799 });
32800
32801  
32802
32803  /*
32804  * - LGPL
32805  *
32806  * Input
32807  * 
32808  */
32809
32810 /**
32811  * @class Roo.bootstrap.NumberField
32812  * @extends Roo.bootstrap.Input
32813  * Bootstrap NumberField class
32814  * 
32815  * 
32816  * 
32817  * 
32818  * @constructor
32819  * Create a new NumberField
32820  * @param {Object} config The config object
32821  */
32822
32823 Roo.bootstrap.NumberField = function(config){
32824     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32825 };
32826
32827 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32828     
32829     /**
32830      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32831      */
32832     allowDecimals : true,
32833     /**
32834      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32835      */
32836     decimalSeparator : ".",
32837     /**
32838      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32839      */
32840     decimalPrecision : 2,
32841     /**
32842      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32843      */
32844     allowNegative : true,
32845     /**
32846      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32847      */
32848     minValue : Number.NEGATIVE_INFINITY,
32849     /**
32850      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32851      */
32852     maxValue : Number.MAX_VALUE,
32853     /**
32854      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32855      */
32856     minText : "The minimum value for this field is {0}",
32857     /**
32858      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32859      */
32860     maxText : "The maximum value for this field is {0}",
32861     /**
32862      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32863      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32864      */
32865     nanText : "{0} is not a valid number",
32866     /**
32867      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32868      */
32869     castInt : true,
32870
32871     // private
32872     initEvents : function()
32873     {   
32874         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32875         
32876         var allowed = "0123456789";
32877         
32878         if(this.allowDecimals){
32879             allowed += this.decimalSeparator;
32880         }
32881         
32882         if(this.allowNegative){
32883             allowed += "-";
32884         }
32885         
32886         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32887         
32888         var keyPress = function(e){
32889             
32890             var k = e.getKey();
32891             
32892             var c = e.getCharCode();
32893             
32894             if(
32895                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32896                     allowed.indexOf(String.fromCharCode(c)) === -1
32897             ){
32898                 e.stopEvent();
32899                 return;
32900             }
32901             
32902             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32903                 return;
32904             }
32905             
32906             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32907                 e.stopEvent();
32908             }
32909         };
32910         
32911         this.el.on("keypress", keyPress, this);
32912     },
32913     
32914     validateValue : function(value)
32915     {
32916         
32917         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32918             return false;
32919         }
32920         
32921         var num = this.parseValue(value);
32922         
32923         if(isNaN(num)){
32924             this.markInvalid(String.format(this.nanText, value));
32925             return false;
32926         }
32927         
32928         if(num < this.minValue){
32929             this.markInvalid(String.format(this.minText, this.minValue));
32930             return false;
32931         }
32932         
32933         if(num > this.maxValue){
32934             this.markInvalid(String.format(this.maxText, this.maxValue));
32935             return false;
32936         }
32937         
32938         return true;
32939     },
32940
32941     getValue : function()
32942     {
32943         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32944     },
32945
32946     parseValue : function(value)
32947     {
32948         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32949         return isNaN(value) ? '' : value;
32950     },
32951
32952     fixPrecision : function(value)
32953     {
32954         var nan = isNaN(value);
32955         
32956         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32957             return nan ? '' : value;
32958         }
32959         return parseFloat(value).toFixed(this.decimalPrecision);
32960     },
32961
32962     setValue : function(v)
32963     {
32964         v = this.fixPrecision(v);
32965         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32966     },
32967
32968     decimalPrecisionFcn : function(v)
32969     {
32970         return Math.floor(v);
32971     },
32972
32973     beforeBlur : function()
32974     {
32975         if(!this.castInt){
32976             return;
32977         }
32978         
32979         var v = this.parseValue(this.getRawValue());
32980         if(v){
32981             this.setValue(v);
32982         }
32983     }
32984     
32985 });
32986
32987  
32988
32989 /*
32990 * Licence: LGPL
32991 */
32992
32993 /**
32994  * @class Roo.bootstrap.DocumentSlider
32995  * @extends Roo.bootstrap.Component
32996  * Bootstrap DocumentSlider class
32997  * 
32998  * @constructor
32999  * Create a new DocumentViewer
33000  * @param {Object} config The config object
33001  */
33002
33003 Roo.bootstrap.DocumentSlider = function(config){
33004     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33005     
33006     this.files = [];
33007     
33008     this.addEvents({
33009         /**
33010          * @event initial
33011          * Fire after initEvent
33012          * @param {Roo.bootstrap.DocumentSlider} this
33013          */
33014         "initial" : true,
33015         /**
33016          * @event update
33017          * Fire after update
33018          * @param {Roo.bootstrap.DocumentSlider} this
33019          */
33020         "update" : true,
33021         /**
33022          * @event click
33023          * Fire after click
33024          * @param {Roo.bootstrap.DocumentSlider} this
33025          */
33026         "click" : true
33027     });
33028 };
33029
33030 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33031     
33032     files : false,
33033     
33034     indicator : 0,
33035     
33036     getAutoCreate : function()
33037     {
33038         var cfg = {
33039             tag : 'div',
33040             cls : 'roo-document-slider',
33041             cn : [
33042                 {
33043                     tag : 'div',
33044                     cls : 'roo-document-slider-header',
33045                     cn : [
33046                         {
33047                             tag : 'div',
33048                             cls : 'roo-document-slider-header-title'
33049                         }
33050                     ]
33051                 },
33052                 {
33053                     tag : 'div',
33054                     cls : 'roo-document-slider-body',
33055                     cn : [
33056                         {
33057                             tag : 'div',
33058                             cls : 'roo-document-slider-prev',
33059                             cn : [
33060                                 {
33061                                     tag : 'i',
33062                                     cls : 'fa fa-chevron-left'
33063                                 }
33064                             ]
33065                         },
33066                         {
33067                             tag : 'div',
33068                             cls : 'roo-document-slider-thumb',
33069                             cn : [
33070                                 {
33071                                     tag : 'img',
33072                                     cls : 'roo-document-slider-image'
33073                                 }
33074                             ]
33075                         },
33076                         {
33077                             tag : 'div',
33078                             cls : 'roo-document-slider-next',
33079                             cn : [
33080                                 {
33081                                     tag : 'i',
33082                                     cls : 'fa fa-chevron-right'
33083                                 }
33084                             ]
33085                         }
33086                     ]
33087                 }
33088             ]
33089         };
33090         
33091         return cfg;
33092     },
33093     
33094     initEvents : function()
33095     {
33096         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33097         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33098         
33099         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33100         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33101         
33102         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33103         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33104         
33105         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33106         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33107         
33108         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33109         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33110         
33111         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33112         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33113         
33114         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33115         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33116         
33117         this.thumbEl.on('click', this.onClick, this);
33118         
33119         this.prevIndicator.on('click', this.prev, this);
33120         
33121         this.nextIndicator.on('click', this.next, this);
33122         
33123     },
33124     
33125     initial : function()
33126     {
33127         if(this.files.length){
33128             this.indicator = 1;
33129             this.update()
33130         }
33131         
33132         this.fireEvent('initial', this);
33133     },
33134     
33135     update : function()
33136     {
33137         this.imageEl.attr('src', this.files[this.indicator - 1]);
33138         
33139         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33140         
33141         this.prevIndicator.show();
33142         
33143         if(this.indicator == 1){
33144             this.prevIndicator.hide();
33145         }
33146         
33147         this.nextIndicator.show();
33148         
33149         if(this.indicator == this.files.length){
33150             this.nextIndicator.hide();
33151         }
33152         
33153         this.thumbEl.scrollTo('top');
33154         
33155         this.fireEvent('update', this);
33156     },
33157     
33158     onClick : function(e)
33159     {
33160         e.preventDefault();
33161         
33162         this.fireEvent('click', this);
33163     },
33164     
33165     prev : function(e)
33166     {
33167         e.preventDefault();
33168         
33169         this.indicator = Math.max(1, this.indicator - 1);
33170         
33171         this.update();
33172     },
33173     
33174     next : function(e)
33175     {
33176         e.preventDefault();
33177         
33178         this.indicator = Math.min(this.files.length, this.indicator + 1);
33179         
33180         this.update();
33181     }
33182 });
33183 /*
33184  * - LGPL
33185  *
33186  * RadioSet
33187  *
33188  *
33189  */
33190
33191 /**
33192  * @class Roo.bootstrap.RadioSet
33193  * @extends Roo.bootstrap.Input
33194  * Bootstrap RadioSet class
33195  * @cfg {String} indicatorpos (left|right) default left
33196  * @cfg {Boolean} inline (true|false) inline the element (default true)
33197  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33198  * @constructor
33199  * Create a new RadioSet
33200  * @param {Object} config The config object
33201  */
33202
33203 Roo.bootstrap.RadioSet = function(config){
33204     
33205     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33206     
33207     this.radioes = [];
33208     
33209     Roo.bootstrap.RadioSet.register(this);
33210     
33211     this.addEvents({
33212         /**
33213         * @event check
33214         * Fires when the element is checked or unchecked.
33215         * @param {Roo.bootstrap.RadioSet} this This radio
33216         * @param {Roo.bootstrap.Radio} item The checked item
33217         */
33218        check : true
33219     });
33220     
33221 };
33222
33223 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33224
33225     radioes : false,
33226     
33227     inline : true,
33228     
33229     weight : '',
33230     
33231     indicatorpos : 'left',
33232     
33233     getAutoCreate : function()
33234     {
33235         var label = {
33236             tag : 'label',
33237             cls : 'roo-radio-set-label',
33238             cn : [
33239                 {
33240                     tag : 'span',
33241                     html : this.fieldLabel
33242                 }
33243             ]
33244         };
33245         
33246         if(this.indicatorpos == 'left'){
33247             label.cn.unshift({
33248                 tag : 'i',
33249                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33250                 tooltip : 'This field is required'
33251             });
33252         } else {
33253             label.cn.push({
33254                 tag : 'i',
33255                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33256                 tooltip : 'This field is required'
33257             });
33258         }
33259         
33260         var items = {
33261             tag : 'div',
33262             cls : 'roo-radio-set-items'
33263         };
33264         
33265         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33266         
33267         if (align === 'left' && this.fieldLabel.length) {
33268             
33269             items = {
33270                 cls : "roo-radio-set-right", 
33271                 cn: [
33272                     items
33273                 ]
33274             };
33275             
33276             if(this.labelWidth > 12){
33277                 label.style = "width: " + this.labelWidth + 'px';
33278             }
33279             
33280             if(this.labelWidth < 13 && this.labelmd == 0){
33281                 this.labelmd = this.labelWidth;
33282             }
33283             
33284             if(this.labellg > 0){
33285                 label.cls += ' col-lg-' + this.labellg;
33286                 items.cls += ' col-lg-' + (12 - this.labellg);
33287             }
33288             
33289             if(this.labelmd > 0){
33290                 label.cls += ' col-md-' + this.labelmd;
33291                 items.cls += ' col-md-' + (12 - this.labelmd);
33292             }
33293             
33294             if(this.labelsm > 0){
33295                 label.cls += ' col-sm-' + this.labelsm;
33296                 items.cls += ' col-sm-' + (12 - this.labelsm);
33297             }
33298             
33299             if(this.labelxs > 0){
33300                 label.cls += ' col-xs-' + this.labelxs;
33301                 items.cls += ' col-xs-' + (12 - this.labelxs);
33302             }
33303         }
33304         
33305         var cfg = {
33306             tag : 'div',
33307             cls : 'roo-radio-set',
33308             cn : [
33309                 {
33310                     tag : 'input',
33311                     cls : 'roo-radio-set-input',
33312                     type : 'hidden',
33313                     name : this.name,
33314                     value : this.value ? this.value :  ''
33315                 },
33316                 label,
33317                 items
33318             ]
33319         };
33320         
33321         if(this.weight.length){
33322             cfg.cls += ' roo-radio-' + this.weight;
33323         }
33324         
33325         if(this.inline) {
33326             cfg.cls += ' roo-radio-set-inline';
33327         }
33328         
33329         var settings=this;
33330         ['xs','sm','md','lg'].map(function(size){
33331             if (settings[size]) {
33332                 cfg.cls += ' col-' + size + '-' + settings[size];
33333             }
33334         });
33335         
33336         return cfg;
33337         
33338     },
33339
33340     initEvents : function()
33341     {
33342         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33343         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33344         
33345         if(!this.fieldLabel.length){
33346             this.labelEl.hide();
33347         }
33348         
33349         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33350         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33351         
33352         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33353         this.indicatorEl().hide();
33354         
33355         this.originalValue = this.getValue();
33356         
33357     },
33358     
33359     inputEl: function ()
33360     {
33361         return this.el.select('.roo-radio-set-input', true).first();
33362     },
33363     
33364     getChildContainer : function()
33365     {
33366         return this.itemsEl;
33367     },
33368     
33369     register : function(item)
33370     {
33371         this.radioes.push(item);
33372         
33373     },
33374     
33375     validate : function()
33376     {   
33377         var valid = false;
33378         
33379         Roo.each(this.radioes, function(i){
33380             if(!i.checked){
33381                 return;
33382             }
33383             
33384             valid = true;
33385             return false;
33386         });
33387         
33388         if(this.allowBlank) {
33389             return true;
33390         }
33391         
33392         if(this.disabled || valid){
33393             this.markValid();
33394             return true;
33395         }
33396         
33397         this.markInvalid();
33398         return false;
33399         
33400     },
33401     
33402     markValid : function()
33403     {
33404         if(this.labelEl.isVisible(true)){
33405             this.indicatorEl().hide();
33406         }
33407         
33408         this.el.removeClass([this.invalidClass, this.validClass]);
33409         this.el.addClass(this.validClass);
33410         
33411         this.fireEvent('valid', this);
33412     },
33413     
33414     markInvalid : function(msg)
33415     {
33416         if(this.allowBlank || this.disabled){
33417             return;
33418         }
33419         
33420         if(this.labelEl.isVisible(true)){
33421             this.indicatorEl().show();
33422         }
33423         
33424         this.el.removeClass([this.invalidClass, this.validClass]);
33425         this.el.addClass(this.invalidClass);
33426         
33427         this.fireEvent('invalid', this, msg);
33428         
33429     },
33430     
33431     setValue : function(v, suppressEvent)
33432     {   
33433         this.value = v;
33434         if(this.rendered){
33435             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33436         }
33437         
33438         Roo.each(this.radioes, function(i){
33439             
33440             i.checked = false;
33441             i.el.removeClass('checked');
33442             
33443             if(i.value === v || i.value.toString() === v.toString()){
33444                 i.checked = true;
33445                 i.el.addClass('checked');
33446                 
33447                 if(suppressEvent !== true){
33448                     this.fireEvent('check', this, i);
33449                 }
33450             }
33451             
33452         }, this);
33453         
33454         this.validate();
33455     },
33456     
33457     clearInvalid : function(){
33458         
33459         if(!this.el || this.preventMark){
33460             return;
33461         }
33462         
33463         this.el.removeClass([this.invalidClass]);
33464         
33465         this.fireEvent('valid', this);
33466     }
33467     
33468 });
33469
33470 Roo.apply(Roo.bootstrap.RadioSet, {
33471     
33472     groups: {},
33473     
33474     register : function(set)
33475     {
33476         this.groups[set.name] = set;
33477     },
33478     
33479     get: function(name) 
33480     {
33481         if (typeof(this.groups[name]) == 'undefined') {
33482             return false;
33483         }
33484         
33485         return this.groups[name] ;
33486     }
33487     
33488 });
33489 /*
33490  * Based on:
33491  * Ext JS Library 1.1.1
33492  * Copyright(c) 2006-2007, Ext JS, LLC.
33493  *
33494  * Originally Released Under LGPL - original licence link has changed is not relivant.
33495  *
33496  * Fork - LGPL
33497  * <script type="text/javascript">
33498  */
33499
33500
33501 /**
33502  * @class Roo.bootstrap.SplitBar
33503  * @extends Roo.util.Observable
33504  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33505  * <br><br>
33506  * Usage:
33507  * <pre><code>
33508 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33509                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33510 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33511 split.minSize = 100;
33512 split.maxSize = 600;
33513 split.animate = true;
33514 split.on('moved', splitterMoved);
33515 </code></pre>
33516  * @constructor
33517  * Create a new SplitBar
33518  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33519  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33520  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33521  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33522                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33523                         position of the SplitBar).
33524  */
33525 Roo.bootstrap.SplitBar = function(cfg){
33526     
33527     /** @private */
33528     
33529     //{
33530     //  dragElement : elm
33531     //  resizingElement: el,
33532         // optional..
33533     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33534     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33535         // existingProxy ???
33536     //}
33537     
33538     this.el = Roo.get(cfg.dragElement, true);
33539     this.el.dom.unselectable = "on";
33540     /** @private */
33541     this.resizingEl = Roo.get(cfg.resizingElement, true);
33542
33543     /**
33544      * @private
33545      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33546      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33547      * @type Number
33548      */
33549     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33550     
33551     /**
33552      * The minimum size of the resizing element. (Defaults to 0)
33553      * @type Number
33554      */
33555     this.minSize = 0;
33556     
33557     /**
33558      * The maximum size of the resizing element. (Defaults to 2000)
33559      * @type Number
33560      */
33561     this.maxSize = 2000;
33562     
33563     /**
33564      * Whether to animate the transition to the new size
33565      * @type Boolean
33566      */
33567     this.animate = false;
33568     
33569     /**
33570      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33571      * @type Boolean
33572      */
33573     this.useShim = false;
33574     
33575     /** @private */
33576     this.shim = null;
33577     
33578     if(!cfg.existingProxy){
33579         /** @private */
33580         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33581     }else{
33582         this.proxy = Roo.get(cfg.existingProxy).dom;
33583     }
33584     /** @private */
33585     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33586     
33587     /** @private */
33588     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33589     
33590     /** @private */
33591     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33592     
33593     /** @private */
33594     this.dragSpecs = {};
33595     
33596     /**
33597      * @private The adapter to use to positon and resize elements
33598      */
33599     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33600     this.adapter.init(this);
33601     
33602     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33603         /** @private */
33604         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33605         this.el.addClass("roo-splitbar-h");
33606     }else{
33607         /** @private */
33608         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33609         this.el.addClass("roo-splitbar-v");
33610     }
33611     
33612     this.addEvents({
33613         /**
33614          * @event resize
33615          * Fires when the splitter is moved (alias for {@link #event-moved})
33616          * @param {Roo.bootstrap.SplitBar} this
33617          * @param {Number} newSize the new width or height
33618          */
33619         "resize" : true,
33620         /**
33621          * @event moved
33622          * Fires when the splitter is moved
33623          * @param {Roo.bootstrap.SplitBar} this
33624          * @param {Number} newSize the new width or height
33625          */
33626         "moved" : true,
33627         /**
33628          * @event beforeresize
33629          * Fires before the splitter is dragged
33630          * @param {Roo.bootstrap.SplitBar} this
33631          */
33632         "beforeresize" : true,
33633
33634         "beforeapply" : true
33635     });
33636
33637     Roo.util.Observable.call(this);
33638 };
33639
33640 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33641     onStartProxyDrag : function(x, y){
33642         this.fireEvent("beforeresize", this);
33643         if(!this.overlay){
33644             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33645             o.unselectable();
33646             o.enableDisplayMode("block");
33647             // all splitbars share the same overlay
33648             Roo.bootstrap.SplitBar.prototype.overlay = o;
33649         }
33650         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33651         this.overlay.show();
33652         Roo.get(this.proxy).setDisplayed("block");
33653         var size = this.adapter.getElementSize(this);
33654         this.activeMinSize = this.getMinimumSize();;
33655         this.activeMaxSize = this.getMaximumSize();;
33656         var c1 = size - this.activeMinSize;
33657         var c2 = Math.max(this.activeMaxSize - size, 0);
33658         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33659             this.dd.resetConstraints();
33660             this.dd.setXConstraint(
33661                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33662                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33663             );
33664             this.dd.setYConstraint(0, 0);
33665         }else{
33666             this.dd.resetConstraints();
33667             this.dd.setXConstraint(0, 0);
33668             this.dd.setYConstraint(
33669                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33670                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33671             );
33672          }
33673         this.dragSpecs.startSize = size;
33674         this.dragSpecs.startPoint = [x, y];
33675         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33676     },
33677     
33678     /** 
33679      * @private Called after the drag operation by the DDProxy
33680      */
33681     onEndProxyDrag : function(e){
33682         Roo.get(this.proxy).setDisplayed(false);
33683         var endPoint = Roo.lib.Event.getXY(e);
33684         if(this.overlay){
33685             this.overlay.hide();
33686         }
33687         var newSize;
33688         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33689             newSize = this.dragSpecs.startSize + 
33690                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33691                     endPoint[0] - this.dragSpecs.startPoint[0] :
33692                     this.dragSpecs.startPoint[0] - endPoint[0]
33693                 );
33694         }else{
33695             newSize = this.dragSpecs.startSize + 
33696                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33697                     endPoint[1] - this.dragSpecs.startPoint[1] :
33698                     this.dragSpecs.startPoint[1] - endPoint[1]
33699                 );
33700         }
33701         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33702         if(newSize != this.dragSpecs.startSize){
33703             if(this.fireEvent('beforeapply', this, newSize) !== false){
33704                 this.adapter.setElementSize(this, newSize);
33705                 this.fireEvent("moved", this, newSize);
33706                 this.fireEvent("resize", this, newSize);
33707             }
33708         }
33709     },
33710     
33711     /**
33712      * Get the adapter this SplitBar uses
33713      * @return The adapter object
33714      */
33715     getAdapter : function(){
33716         return this.adapter;
33717     },
33718     
33719     /**
33720      * Set the adapter this SplitBar uses
33721      * @param {Object} adapter A SplitBar adapter object
33722      */
33723     setAdapter : function(adapter){
33724         this.adapter = adapter;
33725         this.adapter.init(this);
33726     },
33727     
33728     /**
33729      * Gets the minimum size for the resizing element
33730      * @return {Number} The minimum size
33731      */
33732     getMinimumSize : function(){
33733         return this.minSize;
33734     },
33735     
33736     /**
33737      * Sets the minimum size for the resizing element
33738      * @param {Number} minSize The minimum size
33739      */
33740     setMinimumSize : function(minSize){
33741         this.minSize = minSize;
33742     },
33743     
33744     /**
33745      * Gets the maximum size for the resizing element
33746      * @return {Number} The maximum size
33747      */
33748     getMaximumSize : function(){
33749         return this.maxSize;
33750     },
33751     
33752     /**
33753      * Sets the maximum size for the resizing element
33754      * @param {Number} maxSize The maximum size
33755      */
33756     setMaximumSize : function(maxSize){
33757         this.maxSize = maxSize;
33758     },
33759     
33760     /**
33761      * Sets the initialize size for the resizing element
33762      * @param {Number} size The initial size
33763      */
33764     setCurrentSize : function(size){
33765         var oldAnimate = this.animate;
33766         this.animate = false;
33767         this.adapter.setElementSize(this, size);
33768         this.animate = oldAnimate;
33769     },
33770     
33771     /**
33772      * Destroy this splitbar. 
33773      * @param {Boolean} removeEl True to remove the element
33774      */
33775     destroy : function(removeEl){
33776         if(this.shim){
33777             this.shim.remove();
33778         }
33779         this.dd.unreg();
33780         this.proxy.parentNode.removeChild(this.proxy);
33781         if(removeEl){
33782             this.el.remove();
33783         }
33784     }
33785 });
33786
33787 /**
33788  * @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.
33789  */
33790 Roo.bootstrap.SplitBar.createProxy = function(dir){
33791     var proxy = new Roo.Element(document.createElement("div"));
33792     proxy.unselectable();
33793     var cls = 'roo-splitbar-proxy';
33794     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33795     document.body.appendChild(proxy.dom);
33796     return proxy.dom;
33797 };
33798
33799 /** 
33800  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33801  * Default Adapter. It assumes the splitter and resizing element are not positioned
33802  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33803  */
33804 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33805 };
33806
33807 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33808     // do nothing for now
33809     init : function(s){
33810     
33811     },
33812     /**
33813      * Called before drag operations to get the current size of the resizing element. 
33814      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33815      */
33816      getElementSize : function(s){
33817         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33818             return s.resizingEl.getWidth();
33819         }else{
33820             return s.resizingEl.getHeight();
33821         }
33822     },
33823     
33824     /**
33825      * Called after drag operations to set the size of the resizing element.
33826      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33827      * @param {Number} newSize The new size to set
33828      * @param {Function} onComplete A function to be invoked when resizing is complete
33829      */
33830     setElementSize : function(s, newSize, onComplete){
33831         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33832             if(!s.animate){
33833                 s.resizingEl.setWidth(newSize);
33834                 if(onComplete){
33835                     onComplete(s, newSize);
33836                 }
33837             }else{
33838                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33839             }
33840         }else{
33841             
33842             if(!s.animate){
33843                 s.resizingEl.setHeight(newSize);
33844                 if(onComplete){
33845                     onComplete(s, newSize);
33846                 }
33847             }else{
33848                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33849             }
33850         }
33851     }
33852 };
33853
33854 /** 
33855  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33856  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33857  * Adapter that  moves the splitter element to align with the resized sizing element. 
33858  * Used with an absolute positioned SplitBar.
33859  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33860  * document.body, make sure you assign an id to the body element.
33861  */
33862 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33863     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33864     this.container = Roo.get(container);
33865 };
33866
33867 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33868     init : function(s){
33869         this.basic.init(s);
33870     },
33871     
33872     getElementSize : function(s){
33873         return this.basic.getElementSize(s);
33874     },
33875     
33876     setElementSize : function(s, newSize, onComplete){
33877         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33878     },
33879     
33880     moveSplitter : function(s){
33881         var yes = Roo.bootstrap.SplitBar;
33882         switch(s.placement){
33883             case yes.LEFT:
33884                 s.el.setX(s.resizingEl.getRight());
33885                 break;
33886             case yes.RIGHT:
33887                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33888                 break;
33889             case yes.TOP:
33890                 s.el.setY(s.resizingEl.getBottom());
33891                 break;
33892             case yes.BOTTOM:
33893                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33894                 break;
33895         }
33896     }
33897 };
33898
33899 /**
33900  * Orientation constant - Create a vertical SplitBar
33901  * @static
33902  * @type Number
33903  */
33904 Roo.bootstrap.SplitBar.VERTICAL = 1;
33905
33906 /**
33907  * Orientation constant - Create a horizontal SplitBar
33908  * @static
33909  * @type Number
33910  */
33911 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33912
33913 /**
33914  * Placement constant - The resizing element is to the left of the splitter element
33915  * @static
33916  * @type Number
33917  */
33918 Roo.bootstrap.SplitBar.LEFT = 1;
33919
33920 /**
33921  * Placement constant - The resizing element is to the right of the splitter element
33922  * @static
33923  * @type Number
33924  */
33925 Roo.bootstrap.SplitBar.RIGHT = 2;
33926
33927 /**
33928  * Placement constant - The resizing element is positioned above the splitter element
33929  * @static
33930  * @type Number
33931  */
33932 Roo.bootstrap.SplitBar.TOP = 3;
33933
33934 /**
33935  * Placement constant - The resizing element is positioned under splitter element
33936  * @static
33937  * @type Number
33938  */
33939 Roo.bootstrap.SplitBar.BOTTOM = 4;
33940 Roo.namespace("Roo.bootstrap.layout");/*
33941  * Based on:
33942  * Ext JS Library 1.1.1
33943  * Copyright(c) 2006-2007, Ext JS, LLC.
33944  *
33945  * Originally Released Under LGPL - original licence link has changed is not relivant.
33946  *
33947  * Fork - LGPL
33948  * <script type="text/javascript">
33949  */
33950
33951 /**
33952  * @class Roo.bootstrap.layout.Manager
33953  * @extends Roo.bootstrap.Component
33954  * Base class for layout managers.
33955  */
33956 Roo.bootstrap.layout.Manager = function(config)
33957 {
33958     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33959
33960
33961
33962
33963
33964     /** false to disable window resize monitoring @type Boolean */
33965     this.monitorWindowResize = true;
33966     this.regions = {};
33967     this.addEvents({
33968         /**
33969          * @event layout
33970          * Fires when a layout is performed.
33971          * @param {Roo.LayoutManager} this
33972          */
33973         "layout" : true,
33974         /**
33975          * @event regionresized
33976          * Fires when the user resizes a region.
33977          * @param {Roo.LayoutRegion} region The resized region
33978          * @param {Number} newSize The new size (width for east/west, height for north/south)
33979          */
33980         "regionresized" : true,
33981         /**
33982          * @event regioncollapsed
33983          * Fires when a region is collapsed.
33984          * @param {Roo.LayoutRegion} region The collapsed region
33985          */
33986         "regioncollapsed" : true,
33987         /**
33988          * @event regionexpanded
33989          * Fires when a region is expanded.
33990          * @param {Roo.LayoutRegion} region The expanded region
33991          */
33992         "regionexpanded" : true
33993     });
33994     this.updating = false;
33995
33996     if (config.el) {
33997         this.el = Roo.get(config.el);
33998         this.initEvents();
33999     }
34000
34001 };
34002
34003 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34004
34005
34006     regions : null,
34007
34008     monitorWindowResize : true,
34009
34010
34011     updating : false,
34012
34013
34014     onRender : function(ct, position)
34015     {
34016         if(!this.el){
34017             this.el = Roo.get(ct);
34018             this.initEvents();
34019         }
34020         //this.fireEvent('render',this);
34021     },
34022
34023
34024     initEvents: function()
34025     {
34026
34027
34028         // ie scrollbar fix
34029         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34030             document.body.scroll = "no";
34031         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34032             this.el.position('relative');
34033         }
34034         this.id = this.el.id;
34035         this.el.addClass("roo-layout-container");
34036         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34037         if(this.el.dom != document.body ) {
34038             this.el.on('resize', this.layout,this);
34039             this.el.on('show', this.layout,this);
34040         }
34041
34042     },
34043
34044     /**
34045      * Returns true if this layout is currently being updated
34046      * @return {Boolean}
34047      */
34048     isUpdating : function(){
34049         return this.updating;
34050     },
34051
34052     /**
34053      * Suspend the LayoutManager from doing auto-layouts while
34054      * making multiple add or remove calls
34055      */
34056     beginUpdate : function(){
34057         this.updating = true;
34058     },
34059
34060     /**
34061      * Restore auto-layouts and optionally disable the manager from performing a layout
34062      * @param {Boolean} noLayout true to disable a layout update
34063      */
34064     endUpdate : function(noLayout){
34065         this.updating = false;
34066         if(!noLayout){
34067             this.layout();
34068         }
34069     },
34070
34071     layout: function(){
34072         // abstract...
34073     },
34074
34075     onRegionResized : function(region, newSize){
34076         this.fireEvent("regionresized", region, newSize);
34077         this.layout();
34078     },
34079
34080     onRegionCollapsed : function(region){
34081         this.fireEvent("regioncollapsed", region);
34082     },
34083
34084     onRegionExpanded : function(region){
34085         this.fireEvent("regionexpanded", region);
34086     },
34087
34088     /**
34089      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34090      * performs box-model adjustments.
34091      * @return {Object} The size as an object {width: (the width), height: (the height)}
34092      */
34093     getViewSize : function()
34094     {
34095         var size;
34096         if(this.el.dom != document.body){
34097             size = this.el.getSize();
34098         }else{
34099             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34100         }
34101         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34102         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34103         return size;
34104     },
34105
34106     /**
34107      * Returns the Element this layout is bound to.
34108      * @return {Roo.Element}
34109      */
34110     getEl : function(){
34111         return this.el;
34112     },
34113
34114     /**
34115      * Returns the specified region.
34116      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34117      * @return {Roo.LayoutRegion}
34118      */
34119     getRegion : function(target){
34120         return this.regions[target.toLowerCase()];
34121     },
34122
34123     onWindowResize : function(){
34124         if(this.monitorWindowResize){
34125             this.layout();
34126         }
34127     }
34128 });
34129 /*
34130  * Based on:
34131  * Ext JS Library 1.1.1
34132  * Copyright(c) 2006-2007, Ext JS, LLC.
34133  *
34134  * Originally Released Under LGPL - original licence link has changed is not relivant.
34135  *
34136  * Fork - LGPL
34137  * <script type="text/javascript">
34138  */
34139 /**
34140  * @class Roo.bootstrap.layout.Border
34141  * @extends Roo.bootstrap.layout.Manager
34142  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34143  * please see: examples/bootstrap/nested.html<br><br>
34144  
34145 <b>The container the layout is rendered into can be either the body element or any other element.
34146 If it is not the body element, the container needs to either be an absolute positioned element,
34147 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34148 the container size if it is not the body element.</b>
34149
34150 * @constructor
34151 * Create a new Border
34152 * @param {Object} config Configuration options
34153  */
34154 Roo.bootstrap.layout.Border = function(config){
34155     config = config || {};
34156     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34157     
34158     
34159     
34160     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34161         if(config[region]){
34162             config[region].region = region;
34163             this.addRegion(config[region]);
34164         }
34165     },this);
34166     
34167 };
34168
34169 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34170
34171 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34172     /**
34173      * Creates and adds a new region if it doesn't already exist.
34174      * @param {String} target The target region key (north, south, east, west or center).
34175      * @param {Object} config The regions config object
34176      * @return {BorderLayoutRegion} The new region
34177      */
34178     addRegion : function(config)
34179     {
34180         if(!this.regions[config.region]){
34181             var r = this.factory(config);
34182             this.bindRegion(r);
34183         }
34184         return this.regions[config.region];
34185     },
34186
34187     // private (kinda)
34188     bindRegion : function(r){
34189         this.regions[r.config.region] = r;
34190         
34191         r.on("visibilitychange",    this.layout, this);
34192         r.on("paneladded",          this.layout, this);
34193         r.on("panelremoved",        this.layout, this);
34194         r.on("invalidated",         this.layout, this);
34195         r.on("resized",             this.onRegionResized, this);
34196         r.on("collapsed",           this.onRegionCollapsed, this);
34197         r.on("expanded",            this.onRegionExpanded, this);
34198     },
34199
34200     /**
34201      * Performs a layout update.
34202      */
34203     layout : function()
34204     {
34205         if(this.updating) {
34206             return;
34207         }
34208         
34209         // render all the rebions if they have not been done alreayd?
34210         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34211             if(this.regions[region] && !this.regions[region].bodyEl){
34212                 this.regions[region].onRender(this.el)
34213             }
34214         },this);
34215         
34216         var size = this.getViewSize();
34217         var w = size.width;
34218         var h = size.height;
34219         var centerW = w;
34220         var centerH = h;
34221         var centerY = 0;
34222         var centerX = 0;
34223         //var x = 0, y = 0;
34224
34225         var rs = this.regions;
34226         var north = rs["north"];
34227         var south = rs["south"]; 
34228         var west = rs["west"];
34229         var east = rs["east"];
34230         var center = rs["center"];
34231         //if(this.hideOnLayout){ // not supported anymore
34232             //c.el.setStyle("display", "none");
34233         //}
34234         if(north && north.isVisible()){
34235             var b = north.getBox();
34236             var m = north.getMargins();
34237             b.width = w - (m.left+m.right);
34238             b.x = m.left;
34239             b.y = m.top;
34240             centerY = b.height + b.y + m.bottom;
34241             centerH -= centerY;
34242             north.updateBox(this.safeBox(b));
34243         }
34244         if(south && south.isVisible()){
34245             var b = south.getBox();
34246             var m = south.getMargins();
34247             b.width = w - (m.left+m.right);
34248             b.x = m.left;
34249             var totalHeight = (b.height + m.top + m.bottom);
34250             b.y = h - totalHeight + m.top;
34251             centerH -= totalHeight;
34252             south.updateBox(this.safeBox(b));
34253         }
34254         if(west && west.isVisible()){
34255             var b = west.getBox();
34256             var m = west.getMargins();
34257             b.height = centerH - (m.top+m.bottom);
34258             b.x = m.left;
34259             b.y = centerY + m.top;
34260             var totalWidth = (b.width + m.left + m.right);
34261             centerX += totalWidth;
34262             centerW -= totalWidth;
34263             west.updateBox(this.safeBox(b));
34264         }
34265         if(east && east.isVisible()){
34266             var b = east.getBox();
34267             var m = east.getMargins();
34268             b.height = centerH - (m.top+m.bottom);
34269             var totalWidth = (b.width + m.left + m.right);
34270             b.x = w - totalWidth + m.left;
34271             b.y = centerY + m.top;
34272             centerW -= totalWidth;
34273             east.updateBox(this.safeBox(b));
34274         }
34275         if(center){
34276             var m = center.getMargins();
34277             var centerBox = {
34278                 x: centerX + m.left,
34279                 y: centerY + m.top,
34280                 width: centerW - (m.left+m.right),
34281                 height: centerH - (m.top+m.bottom)
34282             };
34283             //if(this.hideOnLayout){
34284                 //center.el.setStyle("display", "block");
34285             //}
34286             center.updateBox(this.safeBox(centerBox));
34287         }
34288         this.el.repaint();
34289         this.fireEvent("layout", this);
34290     },
34291
34292     // private
34293     safeBox : function(box){
34294         box.width = Math.max(0, box.width);
34295         box.height = Math.max(0, box.height);
34296         return box;
34297     },
34298
34299     /**
34300      * Adds a ContentPanel (or subclass) to this layout.
34301      * @param {String} target The target region key (north, south, east, west or center).
34302      * @param {Roo.ContentPanel} panel The panel to add
34303      * @return {Roo.ContentPanel} The added panel
34304      */
34305     add : function(target, panel){
34306          
34307         target = target.toLowerCase();
34308         return this.regions[target].add(panel);
34309     },
34310
34311     /**
34312      * Remove a ContentPanel (or subclass) to this layout.
34313      * @param {String} target The target region key (north, south, east, west or center).
34314      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34315      * @return {Roo.ContentPanel} The removed panel
34316      */
34317     remove : function(target, panel){
34318         target = target.toLowerCase();
34319         return this.regions[target].remove(panel);
34320     },
34321
34322     /**
34323      * Searches all regions for a panel with the specified id
34324      * @param {String} panelId
34325      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34326      */
34327     findPanel : function(panelId){
34328         var rs = this.regions;
34329         for(var target in rs){
34330             if(typeof rs[target] != "function"){
34331                 var p = rs[target].getPanel(panelId);
34332                 if(p){
34333                     return p;
34334                 }
34335             }
34336         }
34337         return null;
34338     },
34339
34340     /**
34341      * Searches all regions for a panel with the specified id and activates (shows) it.
34342      * @param {String/ContentPanel} panelId The panels id or the panel itself
34343      * @return {Roo.ContentPanel} The shown panel or null
34344      */
34345     showPanel : function(panelId) {
34346       var rs = this.regions;
34347       for(var target in rs){
34348          var r = rs[target];
34349          if(typeof r != "function"){
34350             if(r.hasPanel(panelId)){
34351                return r.showPanel(panelId);
34352             }
34353          }
34354       }
34355       return null;
34356    },
34357
34358    /**
34359      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34360      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34361      */
34362    /*
34363     restoreState : function(provider){
34364         if(!provider){
34365             provider = Roo.state.Manager;
34366         }
34367         var sm = new Roo.LayoutStateManager();
34368         sm.init(this, provider);
34369     },
34370 */
34371  
34372  
34373     /**
34374      * Adds a xtype elements to the layout.
34375      * <pre><code>
34376
34377 layout.addxtype({
34378        xtype : 'ContentPanel',
34379        region: 'west',
34380        items: [ .... ]
34381    }
34382 );
34383
34384 layout.addxtype({
34385         xtype : 'NestedLayoutPanel',
34386         region: 'west',
34387         layout: {
34388            center: { },
34389            west: { }   
34390         },
34391         items : [ ... list of content panels or nested layout panels.. ]
34392    }
34393 );
34394 </code></pre>
34395      * @param {Object} cfg Xtype definition of item to add.
34396      */
34397     addxtype : function(cfg)
34398     {
34399         // basically accepts a pannel...
34400         // can accept a layout region..!?!?
34401         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34402         
34403         
34404         // theory?  children can only be panels??
34405         
34406         //if (!cfg.xtype.match(/Panel$/)) {
34407         //    return false;
34408         //}
34409         var ret = false;
34410         
34411         if (typeof(cfg.region) == 'undefined') {
34412             Roo.log("Failed to add Panel, region was not set");
34413             Roo.log(cfg);
34414             return false;
34415         }
34416         var region = cfg.region;
34417         delete cfg.region;
34418         
34419           
34420         var xitems = [];
34421         if (cfg.items) {
34422             xitems = cfg.items;
34423             delete cfg.items;
34424         }
34425         var nb = false;
34426         
34427         switch(cfg.xtype) 
34428         {
34429             case 'Content':  // ContentPanel (el, cfg)
34430             case 'Scroll':  // ContentPanel (el, cfg)
34431             case 'View': 
34432                 cfg.autoCreate = true;
34433                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34434                 //} else {
34435                 //    var el = this.el.createChild();
34436                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34437                 //}
34438                 
34439                 this.add(region, ret);
34440                 break;
34441             
34442             /*
34443             case 'TreePanel': // our new panel!
34444                 cfg.el = this.el.createChild();
34445                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34446                 this.add(region, ret);
34447                 break;
34448             */
34449             
34450             case 'Nest': 
34451                 // create a new Layout (which is  a Border Layout...
34452                 
34453                 var clayout = cfg.layout;
34454                 clayout.el  = this.el.createChild();
34455                 clayout.items   = clayout.items  || [];
34456                 
34457                 delete cfg.layout;
34458                 
34459                 // replace this exitems with the clayout ones..
34460                 xitems = clayout.items;
34461                  
34462                 // force background off if it's in center...
34463                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34464                     cfg.background = false;
34465                 }
34466                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34467                 
34468                 
34469                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34470                 //console.log('adding nested layout panel '  + cfg.toSource());
34471                 this.add(region, ret);
34472                 nb = {}; /// find first...
34473                 break;
34474             
34475             case 'Grid':
34476                 
34477                 // needs grid and region
34478                 
34479                 //var el = this.getRegion(region).el.createChild();
34480                 /*
34481                  *var el = this.el.createChild();
34482                 // create the grid first...
34483                 cfg.grid.container = el;
34484                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34485                 */
34486                 
34487                 if (region == 'center' && this.active ) {
34488                     cfg.background = false;
34489                 }
34490                 
34491                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34492                 
34493                 this.add(region, ret);
34494                 /*
34495                 if (cfg.background) {
34496                     // render grid on panel activation (if panel background)
34497                     ret.on('activate', function(gp) {
34498                         if (!gp.grid.rendered) {
34499                     //        gp.grid.render(el);
34500                         }
34501                     });
34502                 } else {
34503                   //  cfg.grid.render(el);
34504                 }
34505                 */
34506                 break;
34507            
34508            
34509             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34510                 // it was the old xcomponent building that caused this before.
34511                 // espeically if border is the top element in the tree.
34512                 ret = this;
34513                 break; 
34514                 
34515                     
34516                 
34517                 
34518                 
34519             default:
34520                 /*
34521                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34522                     
34523                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34524                     this.add(region, ret);
34525                 } else {
34526                 */
34527                     Roo.log(cfg);
34528                     throw "Can not add '" + cfg.xtype + "' to Border";
34529                     return null;
34530              
34531                                 
34532              
34533         }
34534         this.beginUpdate();
34535         // add children..
34536         var region = '';
34537         var abn = {};
34538         Roo.each(xitems, function(i)  {
34539             region = nb && i.region ? i.region : false;
34540             
34541             var add = ret.addxtype(i);
34542            
34543             if (region) {
34544                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34545                 if (!i.background) {
34546                     abn[region] = nb[region] ;
34547                 }
34548             }
34549             
34550         });
34551         this.endUpdate();
34552
34553         // make the last non-background panel active..
34554         //if (nb) { Roo.log(abn); }
34555         if (nb) {
34556             
34557             for(var r in abn) {
34558                 region = this.getRegion(r);
34559                 if (region) {
34560                     // tried using nb[r], but it does not work..
34561                      
34562                     region.showPanel(abn[r]);
34563                    
34564                 }
34565             }
34566         }
34567         return ret;
34568         
34569     },
34570     
34571     
34572 // private
34573     factory : function(cfg)
34574     {
34575         
34576         var validRegions = Roo.bootstrap.layout.Border.regions;
34577
34578         var target = cfg.region;
34579         cfg.mgr = this;
34580         
34581         var r = Roo.bootstrap.layout;
34582         Roo.log(target);
34583         switch(target){
34584             case "north":
34585                 return new r.North(cfg);
34586             case "south":
34587                 return new r.South(cfg);
34588             case "east":
34589                 return new r.East(cfg);
34590             case "west":
34591                 return new r.West(cfg);
34592             case "center":
34593                 return new r.Center(cfg);
34594         }
34595         throw 'Layout region "'+target+'" not supported.';
34596     }
34597     
34598     
34599 });
34600  /*
34601  * Based on:
34602  * Ext JS Library 1.1.1
34603  * Copyright(c) 2006-2007, Ext JS, LLC.
34604  *
34605  * Originally Released Under LGPL - original licence link has changed is not relivant.
34606  *
34607  * Fork - LGPL
34608  * <script type="text/javascript">
34609  */
34610  
34611 /**
34612  * @class Roo.bootstrap.layout.Basic
34613  * @extends Roo.util.Observable
34614  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34615  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34616  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34617  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34618  * @cfg {string}   region  the region that it inhabits..
34619  * @cfg {bool}   skipConfig skip config?
34620  * 
34621
34622  */
34623 Roo.bootstrap.layout.Basic = function(config){
34624     
34625     this.mgr = config.mgr;
34626     
34627     this.position = config.region;
34628     
34629     var skipConfig = config.skipConfig;
34630     
34631     this.events = {
34632         /**
34633          * @scope Roo.BasicLayoutRegion
34634          */
34635         
34636         /**
34637          * @event beforeremove
34638          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34639          * @param {Roo.LayoutRegion} this
34640          * @param {Roo.ContentPanel} panel The panel
34641          * @param {Object} e The cancel event object
34642          */
34643         "beforeremove" : true,
34644         /**
34645          * @event invalidated
34646          * Fires when the layout for this region is changed.
34647          * @param {Roo.LayoutRegion} this
34648          */
34649         "invalidated" : true,
34650         /**
34651          * @event visibilitychange
34652          * Fires when this region is shown or hidden 
34653          * @param {Roo.LayoutRegion} this
34654          * @param {Boolean} visibility true or false
34655          */
34656         "visibilitychange" : true,
34657         /**
34658          * @event paneladded
34659          * Fires when a panel is added. 
34660          * @param {Roo.LayoutRegion} this
34661          * @param {Roo.ContentPanel} panel The panel
34662          */
34663         "paneladded" : true,
34664         /**
34665          * @event panelremoved
34666          * Fires when a panel is removed. 
34667          * @param {Roo.LayoutRegion} this
34668          * @param {Roo.ContentPanel} panel The panel
34669          */
34670         "panelremoved" : true,
34671         /**
34672          * @event beforecollapse
34673          * Fires when this region before collapse.
34674          * @param {Roo.LayoutRegion} this
34675          */
34676         "beforecollapse" : true,
34677         /**
34678          * @event collapsed
34679          * Fires when this region is collapsed.
34680          * @param {Roo.LayoutRegion} this
34681          */
34682         "collapsed" : true,
34683         /**
34684          * @event expanded
34685          * Fires when this region is expanded.
34686          * @param {Roo.LayoutRegion} this
34687          */
34688         "expanded" : true,
34689         /**
34690          * @event slideshow
34691          * Fires when this region is slid into view.
34692          * @param {Roo.LayoutRegion} this
34693          */
34694         "slideshow" : true,
34695         /**
34696          * @event slidehide
34697          * Fires when this region slides out of view. 
34698          * @param {Roo.LayoutRegion} this
34699          */
34700         "slidehide" : true,
34701         /**
34702          * @event panelactivated
34703          * Fires when a panel is activated. 
34704          * @param {Roo.LayoutRegion} this
34705          * @param {Roo.ContentPanel} panel The activated panel
34706          */
34707         "panelactivated" : true,
34708         /**
34709          * @event resized
34710          * Fires when the user resizes this region. 
34711          * @param {Roo.LayoutRegion} this
34712          * @param {Number} newSize The new size (width for east/west, height for north/south)
34713          */
34714         "resized" : true
34715     };
34716     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34717     this.panels = new Roo.util.MixedCollection();
34718     this.panels.getKey = this.getPanelId.createDelegate(this);
34719     this.box = null;
34720     this.activePanel = null;
34721     // ensure listeners are added...
34722     
34723     if (config.listeners || config.events) {
34724         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34725             listeners : config.listeners || {},
34726             events : config.events || {}
34727         });
34728     }
34729     
34730     if(skipConfig !== true){
34731         this.applyConfig(config);
34732     }
34733 };
34734
34735 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34736 {
34737     getPanelId : function(p){
34738         return p.getId();
34739     },
34740     
34741     applyConfig : function(config){
34742         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34743         this.config = config;
34744         
34745     },
34746     
34747     /**
34748      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34749      * the width, for horizontal (north, south) the height.
34750      * @param {Number} newSize The new width or height
34751      */
34752     resizeTo : function(newSize){
34753         var el = this.el ? this.el :
34754                  (this.activePanel ? this.activePanel.getEl() : null);
34755         if(el){
34756             switch(this.position){
34757                 case "east":
34758                 case "west":
34759                     el.setWidth(newSize);
34760                     this.fireEvent("resized", this, newSize);
34761                 break;
34762                 case "north":
34763                 case "south":
34764                     el.setHeight(newSize);
34765                     this.fireEvent("resized", this, newSize);
34766                 break;                
34767             }
34768         }
34769     },
34770     
34771     getBox : function(){
34772         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34773     },
34774     
34775     getMargins : function(){
34776         return this.margins;
34777     },
34778     
34779     updateBox : function(box){
34780         this.box = box;
34781         var el = this.activePanel.getEl();
34782         el.dom.style.left = box.x + "px";
34783         el.dom.style.top = box.y + "px";
34784         this.activePanel.setSize(box.width, box.height);
34785     },
34786     
34787     /**
34788      * Returns the container element for this region.
34789      * @return {Roo.Element}
34790      */
34791     getEl : function(){
34792         return this.activePanel;
34793     },
34794     
34795     /**
34796      * Returns true if this region is currently visible.
34797      * @return {Boolean}
34798      */
34799     isVisible : function(){
34800         return this.activePanel ? true : false;
34801     },
34802     
34803     setActivePanel : function(panel){
34804         panel = this.getPanel(panel);
34805         if(this.activePanel && this.activePanel != panel){
34806             this.activePanel.setActiveState(false);
34807             this.activePanel.getEl().setLeftTop(-10000,-10000);
34808         }
34809         this.activePanel = panel;
34810         panel.setActiveState(true);
34811         if(this.box){
34812             panel.setSize(this.box.width, this.box.height);
34813         }
34814         this.fireEvent("panelactivated", this, panel);
34815         this.fireEvent("invalidated");
34816     },
34817     
34818     /**
34819      * Show the specified panel.
34820      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34821      * @return {Roo.ContentPanel} The shown panel or null
34822      */
34823     showPanel : function(panel){
34824         panel = this.getPanel(panel);
34825         if(panel){
34826             this.setActivePanel(panel);
34827         }
34828         return panel;
34829     },
34830     
34831     /**
34832      * Get the active panel for this region.
34833      * @return {Roo.ContentPanel} The active panel or null
34834      */
34835     getActivePanel : function(){
34836         return this.activePanel;
34837     },
34838     
34839     /**
34840      * Add the passed ContentPanel(s)
34841      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34842      * @return {Roo.ContentPanel} The panel added (if only one was added)
34843      */
34844     add : function(panel){
34845         if(arguments.length > 1){
34846             for(var i = 0, len = arguments.length; i < len; i++) {
34847                 this.add(arguments[i]);
34848             }
34849             return null;
34850         }
34851         if(this.hasPanel(panel)){
34852             this.showPanel(panel);
34853             return panel;
34854         }
34855         var el = panel.getEl();
34856         if(el.dom.parentNode != this.mgr.el.dom){
34857             this.mgr.el.dom.appendChild(el.dom);
34858         }
34859         if(panel.setRegion){
34860             panel.setRegion(this);
34861         }
34862         this.panels.add(panel);
34863         el.setStyle("position", "absolute");
34864         if(!panel.background){
34865             this.setActivePanel(panel);
34866             if(this.config.initialSize && this.panels.getCount()==1){
34867                 this.resizeTo(this.config.initialSize);
34868             }
34869         }
34870         this.fireEvent("paneladded", this, panel);
34871         return panel;
34872     },
34873     
34874     /**
34875      * Returns true if the panel is in this region.
34876      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34877      * @return {Boolean}
34878      */
34879     hasPanel : function(panel){
34880         if(typeof panel == "object"){ // must be panel obj
34881             panel = panel.getId();
34882         }
34883         return this.getPanel(panel) ? true : false;
34884     },
34885     
34886     /**
34887      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34888      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34889      * @param {Boolean} preservePanel Overrides the config preservePanel option
34890      * @return {Roo.ContentPanel} The panel that was removed
34891      */
34892     remove : function(panel, preservePanel){
34893         panel = this.getPanel(panel);
34894         if(!panel){
34895             return null;
34896         }
34897         var e = {};
34898         this.fireEvent("beforeremove", this, panel, e);
34899         if(e.cancel === true){
34900             return null;
34901         }
34902         var panelId = panel.getId();
34903         this.panels.removeKey(panelId);
34904         return panel;
34905     },
34906     
34907     /**
34908      * Returns the panel specified or null if it's not in this region.
34909      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34910      * @return {Roo.ContentPanel}
34911      */
34912     getPanel : function(id){
34913         if(typeof id == "object"){ // must be panel obj
34914             return id;
34915         }
34916         return this.panels.get(id);
34917     },
34918     
34919     /**
34920      * Returns this regions position (north/south/east/west/center).
34921      * @return {String} 
34922      */
34923     getPosition: function(){
34924         return this.position;    
34925     }
34926 });/*
34927  * Based on:
34928  * Ext JS Library 1.1.1
34929  * Copyright(c) 2006-2007, Ext JS, LLC.
34930  *
34931  * Originally Released Under LGPL - original licence link has changed is not relivant.
34932  *
34933  * Fork - LGPL
34934  * <script type="text/javascript">
34935  */
34936  
34937 /**
34938  * @class Roo.bootstrap.layout.Region
34939  * @extends Roo.bootstrap.layout.Basic
34940  * This class represents a region in a layout manager.
34941  
34942  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34943  * @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})
34944  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34945  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34946  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34947  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34948  * @cfg {String}    title           The title for the region (overrides panel titles)
34949  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34950  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34951  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34952  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34953  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34954  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34955  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34956  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34957  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34958  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34959
34960  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34961  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34962  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34963  * @cfg {Number}    width           For East/West panels
34964  * @cfg {Number}    height          For North/South panels
34965  * @cfg {Boolean}   split           To show the splitter
34966  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34967  * 
34968  * @cfg {string}   cls             Extra CSS classes to add to region
34969  * 
34970  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34971  * @cfg {string}   region  the region that it inhabits..
34972  *
34973
34974  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34975  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34976
34977  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34978  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34979  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34980  */
34981 Roo.bootstrap.layout.Region = function(config)
34982 {
34983     this.applyConfig(config);
34984
34985     var mgr = config.mgr;
34986     var pos = config.region;
34987     config.skipConfig = true;
34988     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34989     
34990     if (mgr.el) {
34991         this.onRender(mgr.el);   
34992     }
34993      
34994     this.visible = true;
34995     this.collapsed = false;
34996     this.unrendered_panels = [];
34997 };
34998
34999 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35000
35001     position: '', // set by wrapper (eg. north/south etc..)
35002     unrendered_panels : null,  // unrendered panels.
35003     createBody : function(){
35004         /** This region's body element 
35005         * @type Roo.Element */
35006         this.bodyEl = this.el.createChild({
35007                 tag: "div",
35008                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35009         });
35010     },
35011
35012     onRender: function(ctr, pos)
35013     {
35014         var dh = Roo.DomHelper;
35015         /** This region's container element 
35016         * @type Roo.Element */
35017         this.el = dh.append(ctr.dom, {
35018                 tag: "div",
35019                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35020             }, true);
35021         /** This region's title element 
35022         * @type Roo.Element */
35023     
35024         this.titleEl = dh.append(this.el.dom,
35025             {
35026                     tag: "div",
35027                     unselectable: "on",
35028                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35029                     children:[
35030                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35031                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35032                     ]}, true);
35033         
35034         this.titleEl.enableDisplayMode();
35035         /** This region's title text element 
35036         * @type HTMLElement */
35037         this.titleTextEl = this.titleEl.dom.firstChild;
35038         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35039         /*
35040         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35041         this.closeBtn.enableDisplayMode();
35042         this.closeBtn.on("click", this.closeClicked, this);
35043         this.closeBtn.hide();
35044     */
35045         this.createBody(this.config);
35046         if(this.config.hideWhenEmpty){
35047             this.hide();
35048             this.on("paneladded", this.validateVisibility, this);
35049             this.on("panelremoved", this.validateVisibility, this);
35050         }
35051         if(this.autoScroll){
35052             this.bodyEl.setStyle("overflow", "auto");
35053         }else{
35054             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35055         }
35056         //if(c.titlebar !== false){
35057             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35058                 this.titleEl.hide();
35059             }else{
35060                 this.titleEl.show();
35061                 if(this.config.title){
35062                     this.titleTextEl.innerHTML = this.config.title;
35063                 }
35064             }
35065         //}
35066         if(this.config.collapsed){
35067             this.collapse(true);
35068         }
35069         if(this.config.hidden){
35070             this.hide();
35071         }
35072         
35073         if (this.unrendered_panels && this.unrendered_panels.length) {
35074             for (var i =0;i< this.unrendered_panels.length; i++) {
35075                 this.add(this.unrendered_panels[i]);
35076             }
35077             this.unrendered_panels = null;
35078             
35079         }
35080         
35081     },
35082     
35083     applyConfig : function(c)
35084     {
35085         /*
35086          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35087             var dh = Roo.DomHelper;
35088             if(c.titlebar !== false){
35089                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35090                 this.collapseBtn.on("click", this.collapse, this);
35091                 this.collapseBtn.enableDisplayMode();
35092                 /*
35093                 if(c.showPin === true || this.showPin){
35094                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35095                     this.stickBtn.enableDisplayMode();
35096                     this.stickBtn.on("click", this.expand, this);
35097                     this.stickBtn.hide();
35098                 }
35099                 
35100             }
35101             */
35102             /** This region's collapsed element
35103             * @type Roo.Element */
35104             /*
35105              *
35106             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35107                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35108             ]}, true);
35109             
35110             if(c.floatable !== false){
35111                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35112                this.collapsedEl.on("click", this.collapseClick, this);
35113             }
35114
35115             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35116                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35117                    id: "message", unselectable: "on", style:{"float":"left"}});
35118                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35119              }
35120             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35121             this.expandBtn.on("click", this.expand, this);
35122             
35123         }
35124         
35125         if(this.collapseBtn){
35126             this.collapseBtn.setVisible(c.collapsible == true);
35127         }
35128         
35129         this.cmargins = c.cmargins || this.cmargins ||
35130                          (this.position == "west" || this.position == "east" ?
35131                              {top: 0, left: 2, right:2, bottom: 0} :
35132                              {top: 2, left: 0, right:0, bottom: 2});
35133         */
35134         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35135         
35136         
35137         this.bottomTabs = c.tabPosition != "top";
35138         
35139         this.autoScroll = c.autoScroll || false;
35140         
35141         
35142        
35143         
35144         this.duration = c.duration || .30;
35145         this.slideDuration = c.slideDuration || .45;
35146         this.config = c;
35147        
35148     },
35149     /**
35150      * Returns true if this region is currently visible.
35151      * @return {Boolean}
35152      */
35153     isVisible : function(){
35154         return this.visible;
35155     },
35156
35157     /**
35158      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35159      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35160      */
35161     //setCollapsedTitle : function(title){
35162     //    title = title || "&#160;";
35163      //   if(this.collapsedTitleTextEl){
35164       //      this.collapsedTitleTextEl.innerHTML = title;
35165        // }
35166     //},
35167
35168     getBox : function(){
35169         var b;
35170       //  if(!this.collapsed){
35171             b = this.el.getBox(false, true);
35172        // }else{
35173           //  b = this.collapsedEl.getBox(false, true);
35174         //}
35175         return b;
35176     },
35177
35178     getMargins : function(){
35179         return this.margins;
35180         //return this.collapsed ? this.cmargins : this.margins;
35181     },
35182 /*
35183     highlight : function(){
35184         this.el.addClass("x-layout-panel-dragover");
35185     },
35186
35187     unhighlight : function(){
35188         this.el.removeClass("x-layout-panel-dragover");
35189     },
35190 */
35191     updateBox : function(box)
35192     {
35193         if (!this.bodyEl) {
35194             return; // not rendered yet..
35195         }
35196         
35197         this.box = box;
35198         if(!this.collapsed){
35199             this.el.dom.style.left = box.x + "px";
35200             this.el.dom.style.top = box.y + "px";
35201             this.updateBody(box.width, box.height);
35202         }else{
35203             this.collapsedEl.dom.style.left = box.x + "px";
35204             this.collapsedEl.dom.style.top = box.y + "px";
35205             this.collapsedEl.setSize(box.width, box.height);
35206         }
35207         if(this.tabs){
35208             this.tabs.autoSizeTabs();
35209         }
35210     },
35211
35212     updateBody : function(w, h)
35213     {
35214         if(w !== null){
35215             this.el.setWidth(w);
35216             w -= this.el.getBorderWidth("rl");
35217             if(this.config.adjustments){
35218                 w += this.config.adjustments[0];
35219             }
35220         }
35221         if(h !== null && h > 0){
35222             this.el.setHeight(h);
35223             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35224             h -= this.el.getBorderWidth("tb");
35225             if(this.config.adjustments){
35226                 h += this.config.adjustments[1];
35227             }
35228             this.bodyEl.setHeight(h);
35229             if(this.tabs){
35230                 h = this.tabs.syncHeight(h);
35231             }
35232         }
35233         if(this.panelSize){
35234             w = w !== null ? w : this.panelSize.width;
35235             h = h !== null ? h : this.panelSize.height;
35236         }
35237         if(this.activePanel){
35238             var el = this.activePanel.getEl();
35239             w = w !== null ? w : el.getWidth();
35240             h = h !== null ? h : el.getHeight();
35241             this.panelSize = {width: w, height: h};
35242             this.activePanel.setSize(w, h);
35243         }
35244         if(Roo.isIE && this.tabs){
35245             this.tabs.el.repaint();
35246         }
35247     },
35248
35249     /**
35250      * Returns the container element for this region.
35251      * @return {Roo.Element}
35252      */
35253     getEl : function(){
35254         return this.el;
35255     },
35256
35257     /**
35258      * Hides this region.
35259      */
35260     hide : function(){
35261         //if(!this.collapsed){
35262             this.el.dom.style.left = "-2000px";
35263             this.el.hide();
35264         //}else{
35265          //   this.collapsedEl.dom.style.left = "-2000px";
35266          //   this.collapsedEl.hide();
35267        // }
35268         this.visible = false;
35269         this.fireEvent("visibilitychange", this, false);
35270     },
35271
35272     /**
35273      * Shows this region if it was previously hidden.
35274      */
35275     show : function(){
35276         //if(!this.collapsed){
35277             this.el.show();
35278         //}else{
35279         //    this.collapsedEl.show();
35280        // }
35281         this.visible = true;
35282         this.fireEvent("visibilitychange", this, true);
35283     },
35284 /*
35285     closeClicked : function(){
35286         if(this.activePanel){
35287             this.remove(this.activePanel);
35288         }
35289     },
35290
35291     collapseClick : function(e){
35292         if(this.isSlid){
35293            e.stopPropagation();
35294            this.slideIn();
35295         }else{
35296            e.stopPropagation();
35297            this.slideOut();
35298         }
35299     },
35300 */
35301     /**
35302      * Collapses this region.
35303      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35304      */
35305     /*
35306     collapse : function(skipAnim, skipCheck = false){
35307         if(this.collapsed) {
35308             return;
35309         }
35310         
35311         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35312             
35313             this.collapsed = true;
35314             if(this.split){
35315                 this.split.el.hide();
35316             }
35317             if(this.config.animate && skipAnim !== true){
35318                 this.fireEvent("invalidated", this);
35319                 this.animateCollapse();
35320             }else{
35321                 this.el.setLocation(-20000,-20000);
35322                 this.el.hide();
35323                 this.collapsedEl.show();
35324                 this.fireEvent("collapsed", this);
35325                 this.fireEvent("invalidated", this);
35326             }
35327         }
35328         
35329     },
35330 */
35331     animateCollapse : function(){
35332         // overridden
35333     },
35334
35335     /**
35336      * Expands this region if it was previously collapsed.
35337      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35338      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35339      */
35340     /*
35341     expand : function(e, skipAnim){
35342         if(e) {
35343             e.stopPropagation();
35344         }
35345         if(!this.collapsed || this.el.hasActiveFx()) {
35346             return;
35347         }
35348         if(this.isSlid){
35349             this.afterSlideIn();
35350             skipAnim = true;
35351         }
35352         this.collapsed = false;
35353         if(this.config.animate && skipAnim !== true){
35354             this.animateExpand();
35355         }else{
35356             this.el.show();
35357             if(this.split){
35358                 this.split.el.show();
35359             }
35360             this.collapsedEl.setLocation(-2000,-2000);
35361             this.collapsedEl.hide();
35362             this.fireEvent("invalidated", this);
35363             this.fireEvent("expanded", this);
35364         }
35365     },
35366 */
35367     animateExpand : function(){
35368         // overridden
35369     },
35370
35371     initTabs : function()
35372     {
35373         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35374         
35375         var ts = new Roo.bootstrap.panel.Tabs({
35376                 el: this.bodyEl.dom,
35377                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35378                 disableTooltips: this.config.disableTabTips,
35379                 toolbar : this.config.toolbar
35380             });
35381         
35382         if(this.config.hideTabs){
35383             ts.stripWrap.setDisplayed(false);
35384         }
35385         this.tabs = ts;
35386         ts.resizeTabs = this.config.resizeTabs === true;
35387         ts.minTabWidth = this.config.minTabWidth || 40;
35388         ts.maxTabWidth = this.config.maxTabWidth || 250;
35389         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35390         ts.monitorResize = false;
35391         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35392         ts.bodyEl.addClass('roo-layout-tabs-body');
35393         this.panels.each(this.initPanelAsTab, this);
35394     },
35395
35396     initPanelAsTab : function(panel){
35397         var ti = this.tabs.addTab(
35398             panel.getEl().id,
35399             panel.getTitle(),
35400             null,
35401             this.config.closeOnTab && panel.isClosable(),
35402             panel.tpl
35403         );
35404         if(panel.tabTip !== undefined){
35405             ti.setTooltip(panel.tabTip);
35406         }
35407         ti.on("activate", function(){
35408               this.setActivePanel(panel);
35409         }, this);
35410         
35411         if(this.config.closeOnTab){
35412             ti.on("beforeclose", function(t, e){
35413                 e.cancel = true;
35414                 this.remove(panel);
35415             }, this);
35416         }
35417         
35418         panel.tabItem = ti;
35419         
35420         return ti;
35421     },
35422
35423     updatePanelTitle : function(panel, title)
35424     {
35425         if(this.activePanel == panel){
35426             this.updateTitle(title);
35427         }
35428         if(this.tabs){
35429             var ti = this.tabs.getTab(panel.getEl().id);
35430             ti.setText(title);
35431             if(panel.tabTip !== undefined){
35432                 ti.setTooltip(panel.tabTip);
35433             }
35434         }
35435     },
35436
35437     updateTitle : function(title){
35438         if(this.titleTextEl && !this.config.title){
35439             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35440         }
35441     },
35442
35443     setActivePanel : function(panel)
35444     {
35445         panel = this.getPanel(panel);
35446         if(this.activePanel && this.activePanel != panel){
35447             this.activePanel.setActiveState(false);
35448         }
35449         this.activePanel = panel;
35450         panel.setActiveState(true);
35451         if(this.panelSize){
35452             panel.setSize(this.panelSize.width, this.panelSize.height);
35453         }
35454         if(this.closeBtn){
35455             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35456         }
35457         this.updateTitle(panel.getTitle());
35458         if(this.tabs){
35459             this.fireEvent("invalidated", this);
35460         }
35461         this.fireEvent("panelactivated", this, panel);
35462     },
35463
35464     /**
35465      * Shows the specified panel.
35466      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35467      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35468      */
35469     showPanel : function(panel)
35470     {
35471         panel = this.getPanel(panel);
35472         if(panel){
35473             if(this.tabs){
35474                 var tab = this.tabs.getTab(panel.getEl().id);
35475                 if(tab.isHidden()){
35476                     this.tabs.unhideTab(tab.id);
35477                 }
35478                 tab.activate();
35479             }else{
35480                 this.setActivePanel(panel);
35481             }
35482         }
35483         return panel;
35484     },
35485
35486     /**
35487      * Get the active panel for this region.
35488      * @return {Roo.ContentPanel} The active panel or null
35489      */
35490     getActivePanel : function(){
35491         return this.activePanel;
35492     },
35493
35494     validateVisibility : function(){
35495         if(this.panels.getCount() < 1){
35496             this.updateTitle("&#160;");
35497             this.closeBtn.hide();
35498             this.hide();
35499         }else{
35500             if(!this.isVisible()){
35501                 this.show();
35502             }
35503         }
35504     },
35505
35506     /**
35507      * Adds the passed ContentPanel(s) to this region.
35508      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35509      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35510      */
35511     add : function(panel)
35512     {
35513         if(arguments.length > 1){
35514             for(var i = 0, len = arguments.length; i < len; i++) {
35515                 this.add(arguments[i]);
35516             }
35517             return null;
35518         }
35519         
35520         // if we have not been rendered yet, then we can not really do much of this..
35521         if (!this.bodyEl) {
35522             this.unrendered_panels.push(panel);
35523             return panel;
35524         }
35525         
35526         
35527         
35528         
35529         if(this.hasPanel(panel)){
35530             this.showPanel(panel);
35531             return panel;
35532         }
35533         panel.setRegion(this);
35534         this.panels.add(panel);
35535        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35536             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35537             // and hide them... ???
35538             this.bodyEl.dom.appendChild(panel.getEl().dom);
35539             if(panel.background !== true){
35540                 this.setActivePanel(panel);
35541             }
35542             this.fireEvent("paneladded", this, panel);
35543             return panel;
35544         }
35545         */
35546         if(!this.tabs){
35547             this.initTabs();
35548         }else{
35549             this.initPanelAsTab(panel);
35550         }
35551         
35552         
35553         if(panel.background !== true){
35554             this.tabs.activate(panel.getEl().id);
35555         }
35556         this.fireEvent("paneladded", this, panel);
35557         return panel;
35558     },
35559
35560     /**
35561      * Hides the tab for the specified panel.
35562      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35563      */
35564     hidePanel : function(panel){
35565         if(this.tabs && (panel = this.getPanel(panel))){
35566             this.tabs.hideTab(panel.getEl().id);
35567         }
35568     },
35569
35570     /**
35571      * Unhides the tab for a previously hidden panel.
35572      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35573      */
35574     unhidePanel : function(panel){
35575         if(this.tabs && (panel = this.getPanel(panel))){
35576             this.tabs.unhideTab(panel.getEl().id);
35577         }
35578     },
35579
35580     clearPanels : function(){
35581         while(this.panels.getCount() > 0){
35582              this.remove(this.panels.first());
35583         }
35584     },
35585
35586     /**
35587      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35588      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35589      * @param {Boolean} preservePanel Overrides the config preservePanel option
35590      * @return {Roo.ContentPanel} The panel that was removed
35591      */
35592     remove : function(panel, preservePanel)
35593     {
35594         panel = this.getPanel(panel);
35595         if(!panel){
35596             return null;
35597         }
35598         var e = {};
35599         this.fireEvent("beforeremove", this, panel, e);
35600         if(e.cancel === true){
35601             return null;
35602         }
35603         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35604         var panelId = panel.getId();
35605         this.panels.removeKey(panelId);
35606         if(preservePanel){
35607             document.body.appendChild(panel.getEl().dom);
35608         }
35609         if(this.tabs){
35610             this.tabs.removeTab(panel.getEl().id);
35611         }else if (!preservePanel){
35612             this.bodyEl.dom.removeChild(panel.getEl().dom);
35613         }
35614         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35615             var p = this.panels.first();
35616             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35617             tempEl.appendChild(p.getEl().dom);
35618             this.bodyEl.update("");
35619             this.bodyEl.dom.appendChild(p.getEl().dom);
35620             tempEl = null;
35621             this.updateTitle(p.getTitle());
35622             this.tabs = null;
35623             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35624             this.setActivePanel(p);
35625         }
35626         panel.setRegion(null);
35627         if(this.activePanel == panel){
35628             this.activePanel = null;
35629         }
35630         if(this.config.autoDestroy !== false && preservePanel !== true){
35631             try{panel.destroy();}catch(e){}
35632         }
35633         this.fireEvent("panelremoved", this, panel);
35634         return panel;
35635     },
35636
35637     /**
35638      * Returns the TabPanel component used by this region
35639      * @return {Roo.TabPanel}
35640      */
35641     getTabs : function(){
35642         return this.tabs;
35643     },
35644
35645     createTool : function(parentEl, className){
35646         var btn = Roo.DomHelper.append(parentEl, {
35647             tag: "div",
35648             cls: "x-layout-tools-button",
35649             children: [ {
35650                 tag: "div",
35651                 cls: "roo-layout-tools-button-inner " + className,
35652                 html: "&#160;"
35653             }]
35654         }, true);
35655         btn.addClassOnOver("roo-layout-tools-button-over");
35656         return btn;
35657     }
35658 });/*
35659  * Based on:
35660  * Ext JS Library 1.1.1
35661  * Copyright(c) 2006-2007, Ext JS, LLC.
35662  *
35663  * Originally Released Under LGPL - original licence link has changed is not relivant.
35664  *
35665  * Fork - LGPL
35666  * <script type="text/javascript">
35667  */
35668  
35669
35670
35671 /**
35672  * @class Roo.SplitLayoutRegion
35673  * @extends Roo.LayoutRegion
35674  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35675  */
35676 Roo.bootstrap.layout.Split = function(config){
35677     this.cursor = config.cursor;
35678     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35679 };
35680
35681 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35682 {
35683     splitTip : "Drag to resize.",
35684     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35685     useSplitTips : false,
35686
35687     applyConfig : function(config){
35688         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35689     },
35690     
35691     onRender : function(ctr,pos) {
35692         
35693         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35694         if(!this.config.split){
35695             return;
35696         }
35697         if(!this.split){
35698             
35699             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35700                             tag: "div",
35701                             id: this.el.id + "-split",
35702                             cls: "roo-layout-split roo-layout-split-"+this.position,
35703                             html: "&#160;"
35704             });
35705             /** The SplitBar for this region 
35706             * @type Roo.SplitBar */
35707             // does not exist yet...
35708             Roo.log([this.position, this.orientation]);
35709             
35710             this.split = new Roo.bootstrap.SplitBar({
35711                 dragElement : splitEl,
35712                 resizingElement: this.el,
35713                 orientation : this.orientation
35714             });
35715             
35716             this.split.on("moved", this.onSplitMove, this);
35717             this.split.useShim = this.config.useShim === true;
35718             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35719             if(this.useSplitTips){
35720                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35721             }
35722             //if(config.collapsible){
35723             //    this.split.el.on("dblclick", this.collapse,  this);
35724             //}
35725         }
35726         if(typeof this.config.minSize != "undefined"){
35727             this.split.minSize = this.config.minSize;
35728         }
35729         if(typeof this.config.maxSize != "undefined"){
35730             this.split.maxSize = this.config.maxSize;
35731         }
35732         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35733             this.hideSplitter();
35734         }
35735         
35736     },
35737
35738     getHMaxSize : function(){
35739          var cmax = this.config.maxSize || 10000;
35740          var center = this.mgr.getRegion("center");
35741          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35742     },
35743
35744     getVMaxSize : function(){
35745          var cmax = this.config.maxSize || 10000;
35746          var center = this.mgr.getRegion("center");
35747          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35748     },
35749
35750     onSplitMove : function(split, newSize){
35751         this.fireEvent("resized", this, newSize);
35752     },
35753     
35754     /** 
35755      * Returns the {@link Roo.SplitBar} for this region.
35756      * @return {Roo.SplitBar}
35757      */
35758     getSplitBar : function(){
35759         return this.split;
35760     },
35761     
35762     hide : function(){
35763         this.hideSplitter();
35764         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35765     },
35766
35767     hideSplitter : function(){
35768         if(this.split){
35769             this.split.el.setLocation(-2000,-2000);
35770             this.split.el.hide();
35771         }
35772     },
35773
35774     show : function(){
35775         if(this.split){
35776             this.split.el.show();
35777         }
35778         Roo.bootstrap.layout.Split.superclass.show.call(this);
35779     },
35780     
35781     beforeSlide: function(){
35782         if(Roo.isGecko){// firefox overflow auto bug workaround
35783             this.bodyEl.clip();
35784             if(this.tabs) {
35785                 this.tabs.bodyEl.clip();
35786             }
35787             if(this.activePanel){
35788                 this.activePanel.getEl().clip();
35789                 
35790                 if(this.activePanel.beforeSlide){
35791                     this.activePanel.beforeSlide();
35792                 }
35793             }
35794         }
35795     },
35796     
35797     afterSlide : function(){
35798         if(Roo.isGecko){// firefox overflow auto bug workaround
35799             this.bodyEl.unclip();
35800             if(this.tabs) {
35801                 this.tabs.bodyEl.unclip();
35802             }
35803             if(this.activePanel){
35804                 this.activePanel.getEl().unclip();
35805                 if(this.activePanel.afterSlide){
35806                     this.activePanel.afterSlide();
35807                 }
35808             }
35809         }
35810     },
35811
35812     initAutoHide : function(){
35813         if(this.autoHide !== false){
35814             if(!this.autoHideHd){
35815                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35816                 this.autoHideHd = {
35817                     "mouseout": function(e){
35818                         if(!e.within(this.el, true)){
35819                             st.delay(500);
35820                         }
35821                     },
35822                     "mouseover" : function(e){
35823                         st.cancel();
35824                     },
35825                     scope : this
35826                 };
35827             }
35828             this.el.on(this.autoHideHd);
35829         }
35830     },
35831
35832     clearAutoHide : function(){
35833         if(this.autoHide !== false){
35834             this.el.un("mouseout", this.autoHideHd.mouseout);
35835             this.el.un("mouseover", this.autoHideHd.mouseover);
35836         }
35837     },
35838
35839     clearMonitor : function(){
35840         Roo.get(document).un("click", this.slideInIf, this);
35841     },
35842
35843     // these names are backwards but not changed for compat
35844     slideOut : function(){
35845         if(this.isSlid || this.el.hasActiveFx()){
35846             return;
35847         }
35848         this.isSlid = true;
35849         if(this.collapseBtn){
35850             this.collapseBtn.hide();
35851         }
35852         this.closeBtnState = this.closeBtn.getStyle('display');
35853         this.closeBtn.hide();
35854         if(this.stickBtn){
35855             this.stickBtn.show();
35856         }
35857         this.el.show();
35858         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35859         this.beforeSlide();
35860         this.el.setStyle("z-index", 10001);
35861         this.el.slideIn(this.getSlideAnchor(), {
35862             callback: function(){
35863                 this.afterSlide();
35864                 this.initAutoHide();
35865                 Roo.get(document).on("click", this.slideInIf, this);
35866                 this.fireEvent("slideshow", this);
35867             },
35868             scope: this,
35869             block: true
35870         });
35871     },
35872
35873     afterSlideIn : function(){
35874         this.clearAutoHide();
35875         this.isSlid = false;
35876         this.clearMonitor();
35877         this.el.setStyle("z-index", "");
35878         if(this.collapseBtn){
35879             this.collapseBtn.show();
35880         }
35881         this.closeBtn.setStyle('display', this.closeBtnState);
35882         if(this.stickBtn){
35883             this.stickBtn.hide();
35884         }
35885         this.fireEvent("slidehide", this);
35886     },
35887
35888     slideIn : function(cb){
35889         if(!this.isSlid || this.el.hasActiveFx()){
35890             Roo.callback(cb);
35891             return;
35892         }
35893         this.isSlid = false;
35894         this.beforeSlide();
35895         this.el.slideOut(this.getSlideAnchor(), {
35896             callback: function(){
35897                 this.el.setLeftTop(-10000, -10000);
35898                 this.afterSlide();
35899                 this.afterSlideIn();
35900                 Roo.callback(cb);
35901             },
35902             scope: this,
35903             block: true
35904         });
35905     },
35906     
35907     slideInIf : function(e){
35908         if(!e.within(this.el)){
35909             this.slideIn();
35910         }
35911     },
35912
35913     animateCollapse : function(){
35914         this.beforeSlide();
35915         this.el.setStyle("z-index", 20000);
35916         var anchor = this.getSlideAnchor();
35917         this.el.slideOut(anchor, {
35918             callback : function(){
35919                 this.el.setStyle("z-index", "");
35920                 this.collapsedEl.slideIn(anchor, {duration:.3});
35921                 this.afterSlide();
35922                 this.el.setLocation(-10000,-10000);
35923                 this.el.hide();
35924                 this.fireEvent("collapsed", this);
35925             },
35926             scope: this,
35927             block: true
35928         });
35929     },
35930
35931     animateExpand : function(){
35932         this.beforeSlide();
35933         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35934         this.el.setStyle("z-index", 20000);
35935         this.collapsedEl.hide({
35936             duration:.1
35937         });
35938         this.el.slideIn(this.getSlideAnchor(), {
35939             callback : function(){
35940                 this.el.setStyle("z-index", "");
35941                 this.afterSlide();
35942                 if(this.split){
35943                     this.split.el.show();
35944                 }
35945                 this.fireEvent("invalidated", this);
35946                 this.fireEvent("expanded", this);
35947             },
35948             scope: this,
35949             block: true
35950         });
35951     },
35952
35953     anchors : {
35954         "west" : "left",
35955         "east" : "right",
35956         "north" : "top",
35957         "south" : "bottom"
35958     },
35959
35960     sanchors : {
35961         "west" : "l",
35962         "east" : "r",
35963         "north" : "t",
35964         "south" : "b"
35965     },
35966
35967     canchors : {
35968         "west" : "tl-tr",
35969         "east" : "tr-tl",
35970         "north" : "tl-bl",
35971         "south" : "bl-tl"
35972     },
35973
35974     getAnchor : function(){
35975         return this.anchors[this.position];
35976     },
35977
35978     getCollapseAnchor : function(){
35979         return this.canchors[this.position];
35980     },
35981
35982     getSlideAnchor : function(){
35983         return this.sanchors[this.position];
35984     },
35985
35986     getAlignAdj : function(){
35987         var cm = this.cmargins;
35988         switch(this.position){
35989             case "west":
35990                 return [0, 0];
35991             break;
35992             case "east":
35993                 return [0, 0];
35994             break;
35995             case "north":
35996                 return [0, 0];
35997             break;
35998             case "south":
35999                 return [0, 0];
36000             break;
36001         }
36002     },
36003
36004     getExpandAdj : function(){
36005         var c = this.collapsedEl, cm = this.cmargins;
36006         switch(this.position){
36007             case "west":
36008                 return [-(cm.right+c.getWidth()+cm.left), 0];
36009             break;
36010             case "east":
36011                 return [cm.right+c.getWidth()+cm.left, 0];
36012             break;
36013             case "north":
36014                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36015             break;
36016             case "south":
36017                 return [0, cm.top+cm.bottom+c.getHeight()];
36018             break;
36019         }
36020     }
36021 });/*
36022  * Based on:
36023  * Ext JS Library 1.1.1
36024  * Copyright(c) 2006-2007, Ext JS, LLC.
36025  *
36026  * Originally Released Under LGPL - original licence link has changed is not relivant.
36027  *
36028  * Fork - LGPL
36029  * <script type="text/javascript">
36030  */
36031 /*
36032  * These classes are private internal classes
36033  */
36034 Roo.bootstrap.layout.Center = function(config){
36035     config.region = "center";
36036     Roo.bootstrap.layout.Region.call(this, config);
36037     this.visible = true;
36038     this.minWidth = config.minWidth || 20;
36039     this.minHeight = config.minHeight || 20;
36040 };
36041
36042 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36043     hide : function(){
36044         // center panel can't be hidden
36045     },
36046     
36047     show : function(){
36048         // center panel can't be hidden
36049     },
36050     
36051     getMinWidth: function(){
36052         return this.minWidth;
36053     },
36054     
36055     getMinHeight: function(){
36056         return this.minHeight;
36057     }
36058 });
36059
36060
36061
36062
36063  
36064
36065
36066
36067
36068
36069 Roo.bootstrap.layout.North = function(config)
36070 {
36071     config.region = 'north';
36072     config.cursor = 'n-resize';
36073     
36074     Roo.bootstrap.layout.Split.call(this, config);
36075     
36076     
36077     if(this.split){
36078         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36079         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36080         this.split.el.addClass("roo-layout-split-v");
36081     }
36082     var size = config.initialSize || config.height;
36083     if(typeof size != "undefined"){
36084         this.el.setHeight(size);
36085     }
36086 };
36087 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36088 {
36089     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36090     
36091     
36092     
36093     getBox : function(){
36094         if(this.collapsed){
36095             return this.collapsedEl.getBox();
36096         }
36097         var box = this.el.getBox();
36098         if(this.split){
36099             box.height += this.split.el.getHeight();
36100         }
36101         return box;
36102     },
36103     
36104     updateBox : function(box){
36105         if(this.split && !this.collapsed){
36106             box.height -= this.split.el.getHeight();
36107             this.split.el.setLeft(box.x);
36108             this.split.el.setTop(box.y+box.height);
36109             this.split.el.setWidth(box.width);
36110         }
36111         if(this.collapsed){
36112             this.updateBody(box.width, null);
36113         }
36114         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36115     }
36116 });
36117
36118
36119
36120
36121
36122 Roo.bootstrap.layout.South = function(config){
36123     config.region = 'south';
36124     config.cursor = 's-resize';
36125     Roo.bootstrap.layout.Split.call(this, config);
36126     if(this.split){
36127         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36128         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36129         this.split.el.addClass("roo-layout-split-v");
36130     }
36131     var size = config.initialSize || config.height;
36132     if(typeof size != "undefined"){
36133         this.el.setHeight(size);
36134     }
36135 };
36136
36137 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36138     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36139     getBox : function(){
36140         if(this.collapsed){
36141             return this.collapsedEl.getBox();
36142         }
36143         var box = this.el.getBox();
36144         if(this.split){
36145             var sh = this.split.el.getHeight();
36146             box.height += sh;
36147             box.y -= sh;
36148         }
36149         return box;
36150     },
36151     
36152     updateBox : function(box){
36153         if(this.split && !this.collapsed){
36154             var sh = this.split.el.getHeight();
36155             box.height -= sh;
36156             box.y += sh;
36157             this.split.el.setLeft(box.x);
36158             this.split.el.setTop(box.y-sh);
36159             this.split.el.setWidth(box.width);
36160         }
36161         if(this.collapsed){
36162             this.updateBody(box.width, null);
36163         }
36164         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36165     }
36166 });
36167
36168 Roo.bootstrap.layout.East = function(config){
36169     config.region = "east";
36170     config.cursor = "e-resize";
36171     Roo.bootstrap.layout.Split.call(this, config);
36172     if(this.split){
36173         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36174         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36175         this.split.el.addClass("roo-layout-split-h");
36176     }
36177     var size = config.initialSize || config.width;
36178     if(typeof size != "undefined"){
36179         this.el.setWidth(size);
36180     }
36181 };
36182 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36183     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36184     getBox : function(){
36185         if(this.collapsed){
36186             return this.collapsedEl.getBox();
36187         }
36188         var box = this.el.getBox();
36189         if(this.split){
36190             var sw = this.split.el.getWidth();
36191             box.width += sw;
36192             box.x -= sw;
36193         }
36194         return box;
36195     },
36196
36197     updateBox : function(box){
36198         if(this.split && !this.collapsed){
36199             var sw = this.split.el.getWidth();
36200             box.width -= sw;
36201             this.split.el.setLeft(box.x);
36202             this.split.el.setTop(box.y);
36203             this.split.el.setHeight(box.height);
36204             box.x += sw;
36205         }
36206         if(this.collapsed){
36207             this.updateBody(null, box.height);
36208         }
36209         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36210     }
36211 });
36212
36213 Roo.bootstrap.layout.West = function(config){
36214     config.region = "west";
36215     config.cursor = "w-resize";
36216     
36217     Roo.bootstrap.layout.Split.call(this, config);
36218     if(this.split){
36219         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36220         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36221         this.split.el.addClass("roo-layout-split-h");
36222     }
36223     
36224 };
36225 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36226     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36227     
36228     onRender: function(ctr, pos)
36229     {
36230         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36231         var size = this.config.initialSize || this.config.width;
36232         if(typeof size != "undefined"){
36233             this.el.setWidth(size);
36234         }
36235     },
36236     
36237     getBox : function(){
36238         if(this.collapsed){
36239             return this.collapsedEl.getBox();
36240         }
36241         var box = this.el.getBox();
36242         if(this.split){
36243             box.width += this.split.el.getWidth();
36244         }
36245         return box;
36246     },
36247     
36248     updateBox : function(box){
36249         if(this.split && !this.collapsed){
36250             var sw = this.split.el.getWidth();
36251             box.width -= sw;
36252             this.split.el.setLeft(box.x+box.width);
36253             this.split.el.setTop(box.y);
36254             this.split.el.setHeight(box.height);
36255         }
36256         if(this.collapsed){
36257             this.updateBody(null, box.height);
36258         }
36259         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36260     }
36261 });
36262 Roo.namespace("Roo.bootstrap.panel");/*
36263  * Based on:
36264  * Ext JS Library 1.1.1
36265  * Copyright(c) 2006-2007, Ext JS, LLC.
36266  *
36267  * Originally Released Under LGPL - original licence link has changed is not relivant.
36268  *
36269  * Fork - LGPL
36270  * <script type="text/javascript">
36271  */
36272 /**
36273  * @class Roo.ContentPanel
36274  * @extends Roo.util.Observable
36275  * A basic ContentPanel element.
36276  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36277  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36278  * @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
36279  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36280  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36281  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36282  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36283  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36284  * @cfg {String} title          The title for this panel
36285  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36286  * @cfg {String} url            Calls {@link #setUrl} with this value
36287  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36288  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36289  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36290  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36291  * @cfg {Boolean} badges render the badges
36292
36293  * @constructor
36294  * Create a new ContentPanel.
36295  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36296  * @param {String/Object} config A string to set only the title or a config object
36297  * @param {String} content (optional) Set the HTML content for this panel
36298  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36299  */
36300 Roo.bootstrap.panel.Content = function( config){
36301     
36302     this.tpl = config.tpl || false;
36303     
36304     var el = config.el;
36305     var content = config.content;
36306
36307     if(config.autoCreate){ // xtype is available if this is called from factory
36308         el = Roo.id();
36309     }
36310     this.el = Roo.get(el);
36311     if(!this.el && config && config.autoCreate){
36312         if(typeof config.autoCreate == "object"){
36313             if(!config.autoCreate.id){
36314                 config.autoCreate.id = config.id||el;
36315             }
36316             this.el = Roo.DomHelper.append(document.body,
36317                         config.autoCreate, true);
36318         }else{
36319             var elcfg =  {   tag: "div",
36320                             cls: "roo-layout-inactive-content",
36321                             id: config.id||el
36322                             };
36323             if (config.html) {
36324                 elcfg.html = config.html;
36325                 
36326             }
36327                         
36328             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36329         }
36330     } 
36331     this.closable = false;
36332     this.loaded = false;
36333     this.active = false;
36334    
36335       
36336     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36337         
36338         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36339         
36340         this.wrapEl = this.el; //this.el.wrap();
36341         var ti = [];
36342         if (config.toolbar.items) {
36343             ti = config.toolbar.items ;
36344             delete config.toolbar.items ;
36345         }
36346         
36347         var nitems = [];
36348         this.toolbar.render(this.wrapEl, 'before');
36349         for(var i =0;i < ti.length;i++) {
36350           //  Roo.log(['add child', items[i]]);
36351             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36352         }
36353         this.toolbar.items = nitems;
36354         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36355         delete config.toolbar;
36356         
36357     }
36358     /*
36359     // xtype created footer. - not sure if will work as we normally have to render first..
36360     if (this.footer && !this.footer.el && this.footer.xtype) {
36361         if (!this.wrapEl) {
36362             this.wrapEl = this.el.wrap();
36363         }
36364     
36365         this.footer.container = this.wrapEl.createChild();
36366          
36367         this.footer = Roo.factory(this.footer, Roo);
36368         
36369     }
36370     */
36371     
36372      if(typeof config == "string"){
36373         this.title = config;
36374     }else{
36375         Roo.apply(this, config);
36376     }
36377     
36378     if(this.resizeEl){
36379         this.resizeEl = Roo.get(this.resizeEl, true);
36380     }else{
36381         this.resizeEl = this.el;
36382     }
36383     // handle view.xtype
36384     
36385  
36386     
36387     
36388     this.addEvents({
36389         /**
36390          * @event activate
36391          * Fires when this panel is activated. 
36392          * @param {Roo.ContentPanel} this
36393          */
36394         "activate" : true,
36395         /**
36396          * @event deactivate
36397          * Fires when this panel is activated. 
36398          * @param {Roo.ContentPanel} this
36399          */
36400         "deactivate" : true,
36401
36402         /**
36403          * @event resize
36404          * Fires when this panel is resized if fitToFrame is true.
36405          * @param {Roo.ContentPanel} this
36406          * @param {Number} width The width after any component adjustments
36407          * @param {Number} height The height after any component adjustments
36408          */
36409         "resize" : true,
36410         
36411          /**
36412          * @event render
36413          * Fires when this tab is created
36414          * @param {Roo.ContentPanel} this
36415          */
36416         "render" : true
36417         
36418         
36419         
36420     });
36421     
36422
36423     
36424     
36425     if(this.autoScroll){
36426         this.resizeEl.setStyle("overflow", "auto");
36427     } else {
36428         // fix randome scrolling
36429         //this.el.on('scroll', function() {
36430         //    Roo.log('fix random scolling');
36431         //    this.scrollTo('top',0); 
36432         //});
36433     }
36434     content = content || this.content;
36435     if(content){
36436         this.setContent(content);
36437     }
36438     if(config && config.url){
36439         this.setUrl(this.url, this.params, this.loadOnce);
36440     }
36441     
36442     
36443     
36444     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36445     
36446     if (this.view && typeof(this.view.xtype) != 'undefined') {
36447         this.view.el = this.el.appendChild(document.createElement("div"));
36448         this.view = Roo.factory(this.view); 
36449         this.view.render  &&  this.view.render(false, '');  
36450     }
36451     
36452     
36453     this.fireEvent('render', this);
36454 };
36455
36456 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36457     
36458     tabTip : '',
36459     
36460     setRegion : function(region){
36461         this.region = region;
36462         this.setActiveClass(region && !this.background);
36463     },
36464     
36465     
36466     setActiveClass: function(state)
36467     {
36468         if(state){
36469            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36470            this.el.setStyle('position','relative');
36471         }else{
36472            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36473            this.el.setStyle('position', 'absolute');
36474         } 
36475     },
36476     
36477     /**
36478      * Returns the toolbar for this Panel if one was configured. 
36479      * @return {Roo.Toolbar} 
36480      */
36481     getToolbar : function(){
36482         return this.toolbar;
36483     },
36484     
36485     setActiveState : function(active)
36486     {
36487         this.active = active;
36488         this.setActiveClass(active);
36489         if(!active){
36490             this.fireEvent("deactivate", this);
36491         }else{
36492             this.fireEvent("activate", this);
36493         }
36494     },
36495     /**
36496      * Updates this panel's element
36497      * @param {String} content The new content
36498      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36499     */
36500     setContent : function(content, loadScripts){
36501         this.el.update(content, loadScripts);
36502     },
36503
36504     ignoreResize : function(w, h){
36505         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36506             return true;
36507         }else{
36508             this.lastSize = {width: w, height: h};
36509             return false;
36510         }
36511     },
36512     /**
36513      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36514      * @return {Roo.UpdateManager} The UpdateManager
36515      */
36516     getUpdateManager : function(){
36517         return this.el.getUpdateManager();
36518     },
36519      /**
36520      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36521      * @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:
36522 <pre><code>
36523 panel.load({
36524     url: "your-url.php",
36525     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36526     callback: yourFunction,
36527     scope: yourObject, //(optional scope)
36528     discardUrl: false,
36529     nocache: false,
36530     text: "Loading...",
36531     timeout: 30,
36532     scripts: false
36533 });
36534 </code></pre>
36535      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36536      * 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.
36537      * @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}
36538      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36539      * @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.
36540      * @return {Roo.ContentPanel} this
36541      */
36542     load : function(){
36543         var um = this.el.getUpdateManager();
36544         um.update.apply(um, arguments);
36545         return this;
36546     },
36547
36548
36549     /**
36550      * 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.
36551      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36552      * @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)
36553      * @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)
36554      * @return {Roo.UpdateManager} The UpdateManager
36555      */
36556     setUrl : function(url, params, loadOnce){
36557         if(this.refreshDelegate){
36558             this.removeListener("activate", this.refreshDelegate);
36559         }
36560         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36561         this.on("activate", this.refreshDelegate);
36562         return this.el.getUpdateManager();
36563     },
36564     
36565     _handleRefresh : function(url, params, loadOnce){
36566         if(!loadOnce || !this.loaded){
36567             var updater = this.el.getUpdateManager();
36568             updater.update(url, params, this._setLoaded.createDelegate(this));
36569         }
36570     },
36571     
36572     _setLoaded : function(){
36573         this.loaded = true;
36574     }, 
36575     
36576     /**
36577      * Returns this panel's id
36578      * @return {String} 
36579      */
36580     getId : function(){
36581         return this.el.id;
36582     },
36583     
36584     /** 
36585      * Returns this panel's element - used by regiosn to add.
36586      * @return {Roo.Element} 
36587      */
36588     getEl : function(){
36589         return this.wrapEl || this.el;
36590     },
36591     
36592    
36593     
36594     adjustForComponents : function(width, height)
36595     {
36596         //Roo.log('adjustForComponents ');
36597         if(this.resizeEl != this.el){
36598             width -= this.el.getFrameWidth('lr');
36599             height -= this.el.getFrameWidth('tb');
36600         }
36601         if(this.toolbar){
36602             var te = this.toolbar.getEl();
36603             te.setWidth(width);
36604             height -= te.getHeight();
36605         }
36606         if(this.footer){
36607             var te = this.footer.getEl();
36608             te.setWidth(width);
36609             height -= te.getHeight();
36610         }
36611         
36612         
36613         if(this.adjustments){
36614             width += this.adjustments[0];
36615             height += this.adjustments[1];
36616         }
36617         return {"width": width, "height": height};
36618     },
36619     
36620     setSize : function(width, height){
36621         if(this.fitToFrame && !this.ignoreResize(width, height)){
36622             if(this.fitContainer && this.resizeEl != this.el){
36623                 this.el.setSize(width, height);
36624             }
36625             var size = this.adjustForComponents(width, height);
36626             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36627             this.fireEvent('resize', this, size.width, size.height);
36628         }
36629     },
36630     
36631     /**
36632      * Returns this panel's title
36633      * @return {String} 
36634      */
36635     getTitle : function(){
36636         
36637         if (typeof(this.title) != 'object') {
36638             return this.title;
36639         }
36640         
36641         var t = '';
36642         for (var k in this.title) {
36643             if (!this.title.hasOwnProperty(k)) {
36644                 continue;
36645             }
36646             
36647             if (k.indexOf('-') >= 0) {
36648                 var s = k.split('-');
36649                 for (var i = 0; i<s.length; i++) {
36650                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36651                 }
36652             } else {
36653                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36654             }
36655         }
36656         return t;
36657     },
36658     
36659     /**
36660      * Set this panel's title
36661      * @param {String} title
36662      */
36663     setTitle : function(title){
36664         this.title = title;
36665         if(this.region){
36666             this.region.updatePanelTitle(this, title);
36667         }
36668     },
36669     
36670     /**
36671      * Returns true is this panel was configured to be closable
36672      * @return {Boolean} 
36673      */
36674     isClosable : function(){
36675         return this.closable;
36676     },
36677     
36678     beforeSlide : function(){
36679         this.el.clip();
36680         this.resizeEl.clip();
36681     },
36682     
36683     afterSlide : function(){
36684         this.el.unclip();
36685         this.resizeEl.unclip();
36686     },
36687     
36688     /**
36689      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36690      *   Will fail silently if the {@link #setUrl} method has not been called.
36691      *   This does not activate the panel, just updates its content.
36692      */
36693     refresh : function(){
36694         if(this.refreshDelegate){
36695            this.loaded = false;
36696            this.refreshDelegate();
36697         }
36698     },
36699     
36700     /**
36701      * Destroys this panel
36702      */
36703     destroy : function(){
36704         this.el.removeAllListeners();
36705         var tempEl = document.createElement("span");
36706         tempEl.appendChild(this.el.dom);
36707         tempEl.innerHTML = "";
36708         this.el.remove();
36709         this.el = null;
36710     },
36711     
36712     /**
36713      * form - if the content panel contains a form - this is a reference to it.
36714      * @type {Roo.form.Form}
36715      */
36716     form : false,
36717     /**
36718      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36719      *    This contains a reference to it.
36720      * @type {Roo.View}
36721      */
36722     view : false,
36723     
36724       /**
36725      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36726      * <pre><code>
36727
36728 layout.addxtype({
36729        xtype : 'Form',
36730        items: [ .... ]
36731    }
36732 );
36733
36734 </code></pre>
36735      * @param {Object} cfg Xtype definition of item to add.
36736      */
36737     
36738     
36739     getChildContainer: function () {
36740         return this.getEl();
36741     }
36742     
36743     
36744     /*
36745         var  ret = new Roo.factory(cfg);
36746         return ret;
36747         
36748         
36749         // add form..
36750         if (cfg.xtype.match(/^Form$/)) {
36751             
36752             var el;
36753             //if (this.footer) {
36754             //    el = this.footer.container.insertSibling(false, 'before');
36755             //} else {
36756                 el = this.el.createChild();
36757             //}
36758
36759             this.form = new  Roo.form.Form(cfg);
36760             
36761             
36762             if ( this.form.allItems.length) {
36763                 this.form.render(el.dom);
36764             }
36765             return this.form;
36766         }
36767         // should only have one of theses..
36768         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36769             // views.. should not be just added - used named prop 'view''
36770             
36771             cfg.el = this.el.appendChild(document.createElement("div"));
36772             // factory?
36773             
36774             var ret = new Roo.factory(cfg);
36775              
36776              ret.render && ret.render(false, ''); // render blank..
36777             this.view = ret;
36778             return ret;
36779         }
36780         return false;
36781     }
36782     \*/
36783 });
36784  
36785 /**
36786  * @class Roo.bootstrap.panel.Grid
36787  * @extends Roo.bootstrap.panel.Content
36788  * @constructor
36789  * Create a new GridPanel.
36790  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36791  * @param {Object} config A the config object
36792   
36793  */
36794
36795
36796
36797 Roo.bootstrap.panel.Grid = function(config)
36798 {
36799     
36800       
36801     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36802         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36803
36804     config.el = this.wrapper;
36805     //this.el = this.wrapper;
36806     
36807       if (config.container) {
36808         // ctor'ed from a Border/panel.grid
36809         
36810         
36811         this.wrapper.setStyle("overflow", "hidden");
36812         this.wrapper.addClass('roo-grid-container');
36813
36814     }
36815     
36816     
36817     if(config.toolbar){
36818         var tool_el = this.wrapper.createChild();    
36819         this.toolbar = Roo.factory(config.toolbar);
36820         var ti = [];
36821         if (config.toolbar.items) {
36822             ti = config.toolbar.items ;
36823             delete config.toolbar.items ;
36824         }
36825         
36826         var nitems = [];
36827         this.toolbar.render(tool_el);
36828         for(var i =0;i < ti.length;i++) {
36829           //  Roo.log(['add child', items[i]]);
36830             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36831         }
36832         this.toolbar.items = nitems;
36833         
36834         delete config.toolbar;
36835     }
36836     
36837     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36838     config.grid.scrollBody = true;;
36839     config.grid.monitorWindowResize = false; // turn off autosizing
36840     config.grid.autoHeight = false;
36841     config.grid.autoWidth = false;
36842     
36843     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36844     
36845     if (config.background) {
36846         // render grid on panel activation (if panel background)
36847         this.on('activate', function(gp) {
36848             if (!gp.grid.rendered) {
36849                 gp.grid.render(this.wrapper);
36850                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36851             }
36852         });
36853             
36854     } else {
36855         this.grid.render(this.wrapper);
36856         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36857
36858     }
36859     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36860     // ??? needed ??? config.el = this.wrapper;
36861     
36862     
36863     
36864   
36865     // xtype created footer. - not sure if will work as we normally have to render first..
36866     if (this.footer && !this.footer.el && this.footer.xtype) {
36867         
36868         var ctr = this.grid.getView().getFooterPanel(true);
36869         this.footer.dataSource = this.grid.dataSource;
36870         this.footer = Roo.factory(this.footer, Roo);
36871         this.footer.render(ctr);
36872         
36873     }
36874     
36875     
36876     
36877     
36878      
36879 };
36880
36881 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36882     getId : function(){
36883         return this.grid.id;
36884     },
36885     
36886     /**
36887      * Returns the grid for this panel
36888      * @return {Roo.bootstrap.Table} 
36889      */
36890     getGrid : function(){
36891         return this.grid;    
36892     },
36893     
36894     setSize : function(width, height){
36895         if(!this.ignoreResize(width, height)){
36896             var grid = this.grid;
36897             var size = this.adjustForComponents(width, height);
36898             var gridel = grid.getGridEl();
36899             gridel.setSize(size.width, size.height);
36900             /*
36901             var thd = grid.getGridEl().select('thead',true).first();
36902             var tbd = grid.getGridEl().select('tbody', true).first();
36903             if (tbd) {
36904                 tbd.setSize(width, height - thd.getHeight());
36905             }
36906             */
36907             grid.autoSize();
36908         }
36909     },
36910      
36911     
36912     
36913     beforeSlide : function(){
36914         this.grid.getView().scroller.clip();
36915     },
36916     
36917     afterSlide : function(){
36918         this.grid.getView().scroller.unclip();
36919     },
36920     
36921     destroy : function(){
36922         this.grid.destroy();
36923         delete this.grid;
36924         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36925     }
36926 });
36927
36928 /**
36929  * @class Roo.bootstrap.panel.Nest
36930  * @extends Roo.bootstrap.panel.Content
36931  * @constructor
36932  * Create a new Panel, that can contain a layout.Border.
36933  * 
36934  * 
36935  * @param {Roo.BorderLayout} layout The layout for this panel
36936  * @param {String/Object} config A string to set only the title or a config object
36937  */
36938 Roo.bootstrap.panel.Nest = function(config)
36939 {
36940     // construct with only one argument..
36941     /* FIXME - implement nicer consturctors
36942     if (layout.layout) {
36943         config = layout;
36944         layout = config.layout;
36945         delete config.layout;
36946     }
36947     if (layout.xtype && !layout.getEl) {
36948         // then layout needs constructing..
36949         layout = Roo.factory(layout, Roo);
36950     }
36951     */
36952     
36953     config.el =  config.layout.getEl();
36954     
36955     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36956     
36957     config.layout.monitorWindowResize = false; // turn off autosizing
36958     this.layout = config.layout;
36959     this.layout.getEl().addClass("roo-layout-nested-layout");
36960     
36961     
36962     
36963     
36964 };
36965
36966 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36967
36968     setSize : function(width, height){
36969         if(!this.ignoreResize(width, height)){
36970             var size = this.adjustForComponents(width, height);
36971             var el = this.layout.getEl();
36972             if (size.height < 1) {
36973                 el.setWidth(size.width);   
36974             } else {
36975                 el.setSize(size.width, size.height);
36976             }
36977             var touch = el.dom.offsetWidth;
36978             this.layout.layout();
36979             // ie requires a double layout on the first pass
36980             if(Roo.isIE && !this.initialized){
36981                 this.initialized = true;
36982                 this.layout.layout();
36983             }
36984         }
36985     },
36986     
36987     // activate all subpanels if not currently active..
36988     
36989     setActiveState : function(active){
36990         this.active = active;
36991         this.setActiveClass(active);
36992         
36993         if(!active){
36994             this.fireEvent("deactivate", this);
36995             return;
36996         }
36997         
36998         this.fireEvent("activate", this);
36999         // not sure if this should happen before or after..
37000         if (!this.layout) {
37001             return; // should not happen..
37002         }
37003         var reg = false;
37004         for (var r in this.layout.regions) {
37005             reg = this.layout.getRegion(r);
37006             if (reg.getActivePanel()) {
37007                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37008                 reg.setActivePanel(reg.getActivePanel());
37009                 continue;
37010             }
37011             if (!reg.panels.length) {
37012                 continue;
37013             }
37014             reg.showPanel(reg.getPanel(0));
37015         }
37016         
37017         
37018         
37019         
37020     },
37021     
37022     /**
37023      * Returns the nested BorderLayout for this panel
37024      * @return {Roo.BorderLayout} 
37025      */
37026     getLayout : function(){
37027         return this.layout;
37028     },
37029     
37030      /**
37031      * Adds a xtype elements to the layout of the nested panel
37032      * <pre><code>
37033
37034 panel.addxtype({
37035        xtype : 'ContentPanel',
37036        region: 'west',
37037        items: [ .... ]
37038    }
37039 );
37040
37041 panel.addxtype({
37042         xtype : 'NestedLayoutPanel',
37043         region: 'west',
37044         layout: {
37045            center: { },
37046            west: { }   
37047         },
37048         items : [ ... list of content panels or nested layout panels.. ]
37049    }
37050 );
37051 </code></pre>
37052      * @param {Object} cfg Xtype definition of item to add.
37053      */
37054     addxtype : function(cfg) {
37055         return this.layout.addxtype(cfg);
37056     
37057     }
37058 });        /*
37059  * Based on:
37060  * Ext JS Library 1.1.1
37061  * Copyright(c) 2006-2007, Ext JS, LLC.
37062  *
37063  * Originally Released Under LGPL - original licence link has changed is not relivant.
37064  *
37065  * Fork - LGPL
37066  * <script type="text/javascript">
37067  */
37068 /**
37069  * @class Roo.TabPanel
37070  * @extends Roo.util.Observable
37071  * A lightweight tab container.
37072  * <br><br>
37073  * Usage:
37074  * <pre><code>
37075 // basic tabs 1, built from existing content
37076 var tabs = new Roo.TabPanel("tabs1");
37077 tabs.addTab("script", "View Script");
37078 tabs.addTab("markup", "View Markup");
37079 tabs.activate("script");
37080
37081 // more advanced tabs, built from javascript
37082 var jtabs = new Roo.TabPanel("jtabs");
37083 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37084
37085 // set up the UpdateManager
37086 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37087 var updater = tab2.getUpdateManager();
37088 updater.setDefaultUrl("ajax1.htm");
37089 tab2.on('activate', updater.refresh, updater, true);
37090
37091 // Use setUrl for Ajax loading
37092 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37093 tab3.setUrl("ajax2.htm", null, true);
37094
37095 // Disabled tab
37096 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37097 tab4.disable();
37098
37099 jtabs.activate("jtabs-1");
37100  * </code></pre>
37101  * @constructor
37102  * Create a new TabPanel.
37103  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37104  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37105  */
37106 Roo.bootstrap.panel.Tabs = function(config){
37107     /**
37108     * The container element for this TabPanel.
37109     * @type Roo.Element
37110     */
37111     this.el = Roo.get(config.el);
37112     delete config.el;
37113     if(config){
37114         if(typeof config == "boolean"){
37115             this.tabPosition = config ? "bottom" : "top";
37116         }else{
37117             Roo.apply(this, config);
37118         }
37119     }
37120     
37121     if(this.tabPosition == "bottom"){
37122         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37123         this.el.addClass("roo-tabs-bottom");
37124     }
37125     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37126     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37127     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37128     if(Roo.isIE){
37129         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37130     }
37131     if(this.tabPosition != "bottom"){
37132         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37133          * @type Roo.Element
37134          */
37135         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37136         this.el.addClass("roo-tabs-top");
37137     }
37138     this.items = [];
37139
37140     this.bodyEl.setStyle("position", "relative");
37141
37142     this.active = null;
37143     this.activateDelegate = this.activate.createDelegate(this);
37144
37145     this.addEvents({
37146         /**
37147          * @event tabchange
37148          * Fires when the active tab changes
37149          * @param {Roo.TabPanel} this
37150          * @param {Roo.TabPanelItem} activePanel The new active tab
37151          */
37152         "tabchange": true,
37153         /**
37154          * @event beforetabchange
37155          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37156          * @param {Roo.TabPanel} this
37157          * @param {Object} e Set cancel to true on this object to cancel the tab change
37158          * @param {Roo.TabPanelItem} tab The tab being changed to
37159          */
37160         "beforetabchange" : true
37161     });
37162
37163     Roo.EventManager.onWindowResize(this.onResize, this);
37164     this.cpad = this.el.getPadding("lr");
37165     this.hiddenCount = 0;
37166
37167
37168     // toolbar on the tabbar support...
37169     if (this.toolbar) {
37170         alert("no toolbar support yet");
37171         this.toolbar  = false;
37172         /*
37173         var tcfg = this.toolbar;
37174         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37175         this.toolbar = new Roo.Toolbar(tcfg);
37176         if (Roo.isSafari) {
37177             var tbl = tcfg.container.child('table', true);
37178             tbl.setAttribute('width', '100%');
37179         }
37180         */
37181         
37182     }
37183    
37184
37185
37186     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37187 };
37188
37189 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37190     /*
37191      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37192      */
37193     tabPosition : "top",
37194     /*
37195      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37196      */
37197     currentTabWidth : 0,
37198     /*
37199      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37200      */
37201     minTabWidth : 40,
37202     /*
37203      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37204      */
37205     maxTabWidth : 250,
37206     /*
37207      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37208      */
37209     preferredTabWidth : 175,
37210     /*
37211      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37212      */
37213     resizeTabs : false,
37214     /*
37215      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37216      */
37217     monitorResize : true,
37218     /*
37219      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37220      */
37221     toolbar : false,
37222
37223     /**
37224      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37225      * @param {String} id The id of the div to use <b>or create</b>
37226      * @param {String} text The text for the tab
37227      * @param {String} content (optional) Content to put in the TabPanelItem body
37228      * @param {Boolean} closable (optional) True to create a close icon on the tab
37229      * @return {Roo.TabPanelItem} The created TabPanelItem
37230      */
37231     addTab : function(id, text, content, closable, tpl)
37232     {
37233         var item = new Roo.bootstrap.panel.TabItem({
37234             panel: this,
37235             id : id,
37236             text : text,
37237             closable : closable,
37238             tpl : tpl
37239         });
37240         this.addTabItem(item);
37241         if(content){
37242             item.setContent(content);
37243         }
37244         return item;
37245     },
37246
37247     /**
37248      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37249      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37250      * @return {Roo.TabPanelItem}
37251      */
37252     getTab : function(id){
37253         return this.items[id];
37254     },
37255
37256     /**
37257      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37258      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37259      */
37260     hideTab : function(id){
37261         var t = this.items[id];
37262         if(!t.isHidden()){
37263            t.setHidden(true);
37264            this.hiddenCount++;
37265            this.autoSizeTabs();
37266         }
37267     },
37268
37269     /**
37270      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37271      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37272      */
37273     unhideTab : function(id){
37274         var t = this.items[id];
37275         if(t.isHidden()){
37276            t.setHidden(false);
37277            this.hiddenCount--;
37278            this.autoSizeTabs();
37279         }
37280     },
37281
37282     /**
37283      * Adds an existing {@link Roo.TabPanelItem}.
37284      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37285      */
37286     addTabItem : function(item){
37287         this.items[item.id] = item;
37288         this.items.push(item);
37289       //  if(this.resizeTabs){
37290     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37291   //         this.autoSizeTabs();
37292 //        }else{
37293 //            item.autoSize();
37294        // }
37295     },
37296
37297     /**
37298      * Removes a {@link Roo.TabPanelItem}.
37299      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37300      */
37301     removeTab : function(id){
37302         var items = this.items;
37303         var tab = items[id];
37304         if(!tab) { return; }
37305         var index = items.indexOf(tab);
37306         if(this.active == tab && items.length > 1){
37307             var newTab = this.getNextAvailable(index);
37308             if(newTab) {
37309                 newTab.activate();
37310             }
37311         }
37312         this.stripEl.dom.removeChild(tab.pnode.dom);
37313         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37314             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37315         }
37316         items.splice(index, 1);
37317         delete this.items[tab.id];
37318         tab.fireEvent("close", tab);
37319         tab.purgeListeners();
37320         this.autoSizeTabs();
37321     },
37322
37323     getNextAvailable : function(start){
37324         var items = this.items;
37325         var index = start;
37326         // look for a next tab that will slide over to
37327         // replace the one being removed
37328         while(index < items.length){
37329             var item = items[++index];
37330             if(item && !item.isHidden()){
37331                 return item;
37332             }
37333         }
37334         // if one isn't found select the previous tab (on the left)
37335         index = start;
37336         while(index >= 0){
37337             var item = items[--index];
37338             if(item && !item.isHidden()){
37339                 return item;
37340             }
37341         }
37342         return null;
37343     },
37344
37345     /**
37346      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37347      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37348      */
37349     disableTab : function(id){
37350         var tab = this.items[id];
37351         if(tab && this.active != tab){
37352             tab.disable();
37353         }
37354     },
37355
37356     /**
37357      * Enables a {@link Roo.TabPanelItem} that is disabled.
37358      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37359      */
37360     enableTab : function(id){
37361         var tab = this.items[id];
37362         tab.enable();
37363     },
37364
37365     /**
37366      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37367      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37368      * @return {Roo.TabPanelItem} The TabPanelItem.
37369      */
37370     activate : function(id){
37371         var tab = this.items[id];
37372         if(!tab){
37373             return null;
37374         }
37375         if(tab == this.active || tab.disabled){
37376             return tab;
37377         }
37378         var e = {};
37379         this.fireEvent("beforetabchange", this, e, tab);
37380         if(e.cancel !== true && !tab.disabled){
37381             if(this.active){
37382                 this.active.hide();
37383             }
37384             this.active = this.items[id];
37385             this.active.show();
37386             this.fireEvent("tabchange", this, this.active);
37387         }
37388         return tab;
37389     },
37390
37391     /**
37392      * Gets the active {@link Roo.TabPanelItem}.
37393      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37394      */
37395     getActiveTab : function(){
37396         return this.active;
37397     },
37398
37399     /**
37400      * Updates the tab body element to fit the height of the container element
37401      * for overflow scrolling
37402      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37403      */
37404     syncHeight : function(targetHeight){
37405         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37406         var bm = this.bodyEl.getMargins();
37407         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37408         this.bodyEl.setHeight(newHeight);
37409         return newHeight;
37410     },
37411
37412     onResize : function(){
37413         if(this.monitorResize){
37414             this.autoSizeTabs();
37415         }
37416     },
37417
37418     /**
37419      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37420      */
37421     beginUpdate : function(){
37422         this.updating = true;
37423     },
37424
37425     /**
37426      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37427      */
37428     endUpdate : function(){
37429         this.updating = false;
37430         this.autoSizeTabs();
37431     },
37432
37433     /**
37434      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37435      */
37436     autoSizeTabs : function(){
37437         var count = this.items.length;
37438         var vcount = count - this.hiddenCount;
37439         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37440             return;
37441         }
37442         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37443         var availWidth = Math.floor(w / vcount);
37444         var b = this.stripBody;
37445         if(b.getWidth() > w){
37446             var tabs = this.items;
37447             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37448             if(availWidth < this.minTabWidth){
37449                 /*if(!this.sleft){    // incomplete scrolling code
37450                     this.createScrollButtons();
37451                 }
37452                 this.showScroll();
37453                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37454             }
37455         }else{
37456             if(this.currentTabWidth < this.preferredTabWidth){
37457                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37458             }
37459         }
37460     },
37461
37462     /**
37463      * Returns the number of tabs in this TabPanel.
37464      * @return {Number}
37465      */
37466      getCount : function(){
37467          return this.items.length;
37468      },
37469
37470     /**
37471      * Resizes all the tabs to the passed width
37472      * @param {Number} The new width
37473      */
37474     setTabWidth : function(width){
37475         this.currentTabWidth = width;
37476         for(var i = 0, len = this.items.length; i < len; i++) {
37477                 if(!this.items[i].isHidden()) {
37478                 this.items[i].setWidth(width);
37479             }
37480         }
37481     },
37482
37483     /**
37484      * Destroys this TabPanel
37485      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37486      */
37487     destroy : function(removeEl){
37488         Roo.EventManager.removeResizeListener(this.onResize, this);
37489         for(var i = 0, len = this.items.length; i < len; i++){
37490             this.items[i].purgeListeners();
37491         }
37492         if(removeEl === true){
37493             this.el.update("");
37494             this.el.remove();
37495         }
37496     },
37497     
37498     createStrip : function(container)
37499     {
37500         var strip = document.createElement("nav");
37501         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37502         container.appendChild(strip);
37503         return strip;
37504     },
37505     
37506     createStripList : function(strip)
37507     {
37508         // div wrapper for retard IE
37509         // returns the "tr" element.
37510         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37511         //'<div class="x-tabs-strip-wrap">'+
37512           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37513           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37514         return strip.firstChild; //.firstChild.firstChild.firstChild;
37515     },
37516     createBody : function(container)
37517     {
37518         var body = document.createElement("div");
37519         Roo.id(body, "tab-body");
37520         //Roo.fly(body).addClass("x-tabs-body");
37521         Roo.fly(body).addClass("tab-content");
37522         container.appendChild(body);
37523         return body;
37524     },
37525     createItemBody :function(bodyEl, id){
37526         var body = Roo.getDom(id);
37527         if(!body){
37528             body = document.createElement("div");
37529             body.id = id;
37530         }
37531         //Roo.fly(body).addClass("x-tabs-item-body");
37532         Roo.fly(body).addClass("tab-pane");
37533          bodyEl.insertBefore(body, bodyEl.firstChild);
37534         return body;
37535     },
37536     /** @private */
37537     createStripElements :  function(stripEl, text, closable, tpl)
37538     {
37539         var td = document.createElement("li"); // was td..
37540         
37541         
37542         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37543         
37544         
37545         stripEl.appendChild(td);
37546         /*if(closable){
37547             td.className = "x-tabs-closable";
37548             if(!this.closeTpl){
37549                 this.closeTpl = new Roo.Template(
37550                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37551                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37552                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37553                 );
37554             }
37555             var el = this.closeTpl.overwrite(td, {"text": text});
37556             var close = el.getElementsByTagName("div")[0];
37557             var inner = el.getElementsByTagName("em")[0];
37558             return {"el": el, "close": close, "inner": inner};
37559         } else {
37560         */
37561         // not sure what this is..
37562 //            if(!this.tabTpl){
37563                 //this.tabTpl = new Roo.Template(
37564                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37565                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37566                 //);
37567 //                this.tabTpl = new Roo.Template(
37568 //                   '<a href="#">' +
37569 //                   '<span unselectable="on"' +
37570 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37571 //                            ' >{text}</span></a>'
37572 //                );
37573 //                
37574 //            }
37575
37576
37577             var template = tpl || this.tabTpl || false;
37578             
37579             if(!template){
37580                 
37581                 template = new Roo.Template(
37582                    '<a href="#">' +
37583                    '<span unselectable="on"' +
37584                             (this.disableTooltips ? '' : ' title="{text}"') +
37585                             ' >{text}</span></a>'
37586                 );
37587             }
37588             
37589             switch (typeof(template)) {
37590                 case 'object' :
37591                     break;
37592                 case 'string' :
37593                     template = new Roo.Template(template);
37594                     break;
37595                 default :
37596                     break;
37597             }
37598             
37599             var el = template.overwrite(td, {"text": text});
37600             
37601             var inner = el.getElementsByTagName("span")[0];
37602             
37603             return {"el": el, "inner": inner};
37604             
37605     }
37606         
37607     
37608 });
37609
37610 /**
37611  * @class Roo.TabPanelItem
37612  * @extends Roo.util.Observable
37613  * Represents an individual item (tab plus body) in a TabPanel.
37614  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37615  * @param {String} id The id of this TabPanelItem
37616  * @param {String} text The text for the tab of this TabPanelItem
37617  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37618  */
37619 Roo.bootstrap.panel.TabItem = function(config){
37620     /**
37621      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37622      * @type Roo.TabPanel
37623      */
37624     this.tabPanel = config.panel;
37625     /**
37626      * The id for this TabPanelItem
37627      * @type String
37628      */
37629     this.id = config.id;
37630     /** @private */
37631     this.disabled = false;
37632     /** @private */
37633     this.text = config.text;
37634     /** @private */
37635     this.loaded = false;
37636     this.closable = config.closable;
37637
37638     /**
37639      * The body element for this TabPanelItem.
37640      * @type Roo.Element
37641      */
37642     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37643     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37644     this.bodyEl.setStyle("display", "block");
37645     this.bodyEl.setStyle("zoom", "1");
37646     //this.hideAction();
37647
37648     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37649     /** @private */
37650     this.el = Roo.get(els.el);
37651     this.inner = Roo.get(els.inner, true);
37652     this.textEl = Roo.get(this.el.dom.firstChild, true);
37653     this.pnode = Roo.get(els.el.parentNode, true);
37654     this.el.on("mousedown", this.onTabMouseDown, this);
37655     this.el.on("click", this.onTabClick, this);
37656     /** @private */
37657     if(config.closable){
37658         var c = Roo.get(els.close, true);
37659         c.dom.title = this.closeText;
37660         c.addClassOnOver("close-over");
37661         c.on("click", this.closeClick, this);
37662      }
37663
37664     this.addEvents({
37665          /**
37666          * @event activate
37667          * Fires when this tab becomes the active tab.
37668          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37669          * @param {Roo.TabPanelItem} this
37670          */
37671         "activate": true,
37672         /**
37673          * @event beforeclose
37674          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37675          * @param {Roo.TabPanelItem} this
37676          * @param {Object} e Set cancel to true on this object to cancel the close.
37677          */
37678         "beforeclose": true,
37679         /**
37680          * @event close
37681          * Fires when this tab is closed.
37682          * @param {Roo.TabPanelItem} this
37683          */
37684          "close": true,
37685         /**
37686          * @event deactivate
37687          * Fires when this tab is no longer the active tab.
37688          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37689          * @param {Roo.TabPanelItem} this
37690          */
37691          "deactivate" : true
37692     });
37693     this.hidden = false;
37694
37695     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37696 };
37697
37698 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37699            {
37700     purgeListeners : function(){
37701        Roo.util.Observable.prototype.purgeListeners.call(this);
37702        this.el.removeAllListeners();
37703     },
37704     /**
37705      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37706      */
37707     show : function(){
37708         this.pnode.addClass("active");
37709         this.showAction();
37710         if(Roo.isOpera){
37711             this.tabPanel.stripWrap.repaint();
37712         }
37713         this.fireEvent("activate", this.tabPanel, this);
37714     },
37715
37716     /**
37717      * Returns true if this tab is the active tab.
37718      * @return {Boolean}
37719      */
37720     isActive : function(){
37721         return this.tabPanel.getActiveTab() == this;
37722     },
37723
37724     /**
37725      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37726      */
37727     hide : function(){
37728         this.pnode.removeClass("active");
37729         this.hideAction();
37730         this.fireEvent("deactivate", this.tabPanel, this);
37731     },
37732
37733     hideAction : function(){
37734         this.bodyEl.hide();
37735         this.bodyEl.setStyle("position", "absolute");
37736         this.bodyEl.setLeft("-20000px");
37737         this.bodyEl.setTop("-20000px");
37738     },
37739
37740     showAction : function(){
37741         this.bodyEl.setStyle("position", "relative");
37742         this.bodyEl.setTop("");
37743         this.bodyEl.setLeft("");
37744         this.bodyEl.show();
37745     },
37746
37747     /**
37748      * Set the tooltip for the tab.
37749      * @param {String} tooltip The tab's tooltip
37750      */
37751     setTooltip : function(text){
37752         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37753             this.textEl.dom.qtip = text;
37754             this.textEl.dom.removeAttribute('title');
37755         }else{
37756             this.textEl.dom.title = text;
37757         }
37758     },
37759
37760     onTabClick : function(e){
37761         e.preventDefault();
37762         this.tabPanel.activate(this.id);
37763     },
37764
37765     onTabMouseDown : function(e){
37766         e.preventDefault();
37767         this.tabPanel.activate(this.id);
37768     },
37769 /*
37770     getWidth : function(){
37771         return this.inner.getWidth();
37772     },
37773
37774     setWidth : function(width){
37775         var iwidth = width - this.pnode.getPadding("lr");
37776         this.inner.setWidth(iwidth);
37777         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37778         this.pnode.setWidth(width);
37779     },
37780 */
37781     /**
37782      * Show or hide the tab
37783      * @param {Boolean} hidden True to hide or false to show.
37784      */
37785     setHidden : function(hidden){
37786         this.hidden = hidden;
37787         this.pnode.setStyle("display", hidden ? "none" : "");
37788     },
37789
37790     /**
37791      * Returns true if this tab is "hidden"
37792      * @return {Boolean}
37793      */
37794     isHidden : function(){
37795         return this.hidden;
37796     },
37797
37798     /**
37799      * Returns the text for this tab
37800      * @return {String}
37801      */
37802     getText : function(){
37803         return this.text;
37804     },
37805     /*
37806     autoSize : function(){
37807         //this.el.beginMeasure();
37808         this.textEl.setWidth(1);
37809         /*
37810          *  #2804 [new] Tabs in Roojs
37811          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37812          */
37813         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37814         //this.el.endMeasure();
37815     //},
37816
37817     /**
37818      * Sets the text for the tab (Note: this also sets the tooltip text)
37819      * @param {String} text The tab's text and tooltip
37820      */
37821     setText : function(text){
37822         this.text = text;
37823         this.textEl.update(text);
37824         this.setTooltip(text);
37825         //if(!this.tabPanel.resizeTabs){
37826         //    this.autoSize();
37827         //}
37828     },
37829     /**
37830      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37831      */
37832     activate : function(){
37833         this.tabPanel.activate(this.id);
37834     },
37835
37836     /**
37837      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37838      */
37839     disable : function(){
37840         if(this.tabPanel.active != this){
37841             this.disabled = true;
37842             this.pnode.addClass("disabled");
37843         }
37844     },
37845
37846     /**
37847      * Enables this TabPanelItem if it was previously disabled.
37848      */
37849     enable : function(){
37850         this.disabled = false;
37851         this.pnode.removeClass("disabled");
37852     },
37853
37854     /**
37855      * Sets the content for this TabPanelItem.
37856      * @param {String} content The content
37857      * @param {Boolean} loadScripts true to look for and load scripts
37858      */
37859     setContent : function(content, loadScripts){
37860         this.bodyEl.update(content, loadScripts);
37861     },
37862
37863     /**
37864      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37865      * @return {Roo.UpdateManager} The UpdateManager
37866      */
37867     getUpdateManager : function(){
37868         return this.bodyEl.getUpdateManager();
37869     },
37870
37871     /**
37872      * Set a URL to be used to load the content for this TabPanelItem.
37873      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37874      * @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)
37875      * @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)
37876      * @return {Roo.UpdateManager} The UpdateManager
37877      */
37878     setUrl : function(url, params, loadOnce){
37879         if(this.refreshDelegate){
37880             this.un('activate', this.refreshDelegate);
37881         }
37882         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37883         this.on("activate", this.refreshDelegate);
37884         return this.bodyEl.getUpdateManager();
37885     },
37886
37887     /** @private */
37888     _handleRefresh : function(url, params, loadOnce){
37889         if(!loadOnce || !this.loaded){
37890             var updater = this.bodyEl.getUpdateManager();
37891             updater.update(url, params, this._setLoaded.createDelegate(this));
37892         }
37893     },
37894
37895     /**
37896      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37897      *   Will fail silently if the setUrl method has not been called.
37898      *   This does not activate the panel, just updates its content.
37899      */
37900     refresh : function(){
37901         if(this.refreshDelegate){
37902            this.loaded = false;
37903            this.refreshDelegate();
37904         }
37905     },
37906
37907     /** @private */
37908     _setLoaded : function(){
37909         this.loaded = true;
37910     },
37911
37912     /** @private */
37913     closeClick : function(e){
37914         var o = {};
37915         e.stopEvent();
37916         this.fireEvent("beforeclose", this, o);
37917         if(o.cancel !== true){
37918             this.tabPanel.removeTab(this.id);
37919         }
37920     },
37921     /**
37922      * The text displayed in the tooltip for the close icon.
37923      * @type String
37924      */
37925     closeText : "Close this tab"
37926 });
37927 Roo.bootstrap.PhoneInput = function(config) {
37928     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37929 };
37930
37931 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField,  {
37932         
37933         triggerList : true,
37934         
37935         getAutoCreate : function()
37936         {
37937             var align = this.labelAlign || this.parentLabelAlign();
37938             var id = Roo.id();
37939             
37940             var cfg = {
37941                 cls: 'form-group', //input-group
37942                 cn: []
37943             };
37944             
37945             var input =  {
37946                 tag: 'input',
37947                 id : id,
37948                 type : 'tel',
37949                 cls : 'form-control tel-input',
37950                 autocomplete: 'new-password'
37951                 //placeholder : this.placeholder || '' 
37952             };
37953             
37954             if (this.name) {
37955                 input.name = this.name;
37956             }
37957             
37958             if (this.disabled) {
37959                 input.disabled=true;
37960             }
37961             
37962             var flag_container = {
37963                 tag: 'div',
37964                 cls: 'flag-container',
37965                 cn: [
37966                     {
37967                         tag: 'div',
37968                         cls: 'flag'
37969                     },
37970                     {
37971                         tag: 'div',
37972                         cls: 'caret'
37973                     }
37974                 ]
37975             };
37976             
37977             var box = {
37978                 tag: 'div',
37979                 cls: this.hasFeedback ? 'has-feedback' : '',
37980                 cn: [
37981                     input
37982                 ]
37983             };
37984             
37985             var container = {
37986                 cls: 'roo-select2-container input-group',
37987                 cn: [
37988                     flag_container,
37989                     box
37990                 ]
37991             };
37992             
37993             if (this.fieldLabel.length) {
37994                 var indicator = {
37995                     tag: 'i',
37996                     tooltip: 'This field is required'
37997                 };
37998                 
37999                 var label = {
38000                     tag: 'label',
38001                     'for':  id,
38002                     cls: 'control-label',
38003                     cn: []
38004                 };
38005                 
38006                 var label_text = {
38007                     tag: 'span',
38008                     html: this.fieldLabel
38009                 };
38010                 
38011                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
38012                 label.cn = [
38013                     indicator,
38014                     label_text
38015                 ];
38016                 
38017                 if(this.indicatorpos == 'right') {
38018                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
38019                     label.cn = [
38020                         label_text,
38021                         indicator
38022                     ];
38023                 }
38024                 
38025                 if(align == 'left') {
38026                     container = {
38027                         tag: 'div',
38028                         cn: [
38029                             container
38030                         ]
38031                     };
38032                     
38033                     if(this.labelWidth > 12){
38034                         label.style = "width: " + this.labelWidth + 'px';
38035                     }
38036                     if(this.labelWidth < 13 && this.labelmd == 0){
38037                         this.labelmd = this.labelWidth;
38038                     }
38039                     if(this.labellg > 0){
38040                         label.cls += ' col-lg-' + this.labellg;
38041                         input.cls += ' col-lg-' + (12 - this.labellg);
38042                     }
38043                     if(this.labelmd > 0){
38044                         label.cls += ' col-md-' + this.labelmd;
38045                         container.cls += ' col-md-' + (12 - this.labelmd);
38046                     }
38047                     if(this.labelsm > 0){
38048                         label.cls += ' col-sm-' + this.labelsm;
38049                         container.cls += ' col-sm-' + (12 - this.labelsm);
38050                     }
38051                     if(this.labelxs > 0){
38052                         label.cls += ' col-xs-' + this.labelxs;
38053                         container.cls += ' col-xs-' + (12 - this.labelxs);
38054                     }
38055                 }
38056             }
38057             
38058             cfg.cn = [
38059                 label,
38060                 container
38061             ];
38062             
38063             
38064             var settings = this;
38065             
38066             ['xs','sm','md','lg'].map(function(size){
38067                 if (settings[size]) {
38068                     cfg.cls += ' col-' + size + '-' + settings[size];
38069                 }
38070             });
38071             
38072             return cfg;
38073         },
38074         
38075         initEvents : function()
38076         {
38077             this.createList();
38078             
38079             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38080             
38081             this.trigger = this.el.select('div.flag-container',true).first();
38082             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38083         },
38084         
38085         onTriggerClick : function(e)
38086         {
38087             Roo.log('trigger click');
38088             
38089             if(this.disabled || !this.triggerList){
38090                 return;
38091             }
38092             
38093             this.page = 0;
38094             this.loadNext = false;
38095             
38096             if(this.isExpanded()){
38097                 this.collapse();
38098             }else {
38099                 this.hasFocus = true;
38100                 if(this.triggerAction == 'all') {
38101                     //Original data flow: doQuery() -> store.load() -> proxy.load() -> store.loadRecords()
38102                 } else {
38103                     
38104                 }
38105                 if (!this.blockFocus) {
38106                     this.inputEl().focus();
38107                 }
38108             }
38109         },
38110         
38111         isExpanded : function(){
38112             return this.list.isVisible();
38113         },
38114         
38115         collapse : function(){
38116             if(!this.isExpanded()){
38117                 return;
38118             }
38119             this.list.hide();
38120             this.hasFocus = false;
38121             Roo.get(document).un('mousedown', this.collapseIf, this);
38122             Roo.get(document).un('mousewheel', this.collapseIf, this);
38123             this.fireEvent('collapse', this);
38124             
38125             this.validate();
38126         }
38127 });