sync
[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         };
9806         if (this.name) {
9807             input.name = this.name;
9808         }
9809         if (this.size) {
9810             input.cls += ' input-' + this.size;
9811         }
9812         
9813         if (this.disabled) {
9814             input.disabled=true;
9815         }
9816         
9817         var inputblock = input;
9818         
9819         if(this.hasFeedback && !this.allowBlank){
9820             
9821             var feedback = {
9822                 tag: 'span',
9823                 cls: 'glyphicon form-control-feedback'
9824             };
9825             
9826             if(this.removable && !this.editable && !this.tickable){
9827                 inputblock = {
9828                     cls : 'has-feedback',
9829                     cn :  [
9830                         inputblock,
9831                         {
9832                             tag: 'button',
9833                             html : 'x',
9834                             cls : 'roo-combo-removable-btn close'
9835                         },
9836                         feedback
9837                     ] 
9838                 };
9839             } else {
9840                 inputblock = {
9841                     cls : 'has-feedback',
9842                     cn :  [
9843                         inputblock,
9844                         feedback
9845                     ] 
9846                 };
9847             }
9848
9849         } else {
9850             if(this.removable && !this.editable && !this.tickable){
9851                 inputblock = {
9852                     cls : 'roo-removable',
9853                     cn :  [
9854                         inputblock,
9855                         {
9856                             tag: 'button',
9857                             html : 'x',
9858                             cls : 'roo-combo-removable-btn close'
9859                         }
9860                     ] 
9861                 };
9862             }
9863         }
9864         
9865         if (this.before || this.after) {
9866             
9867             inputblock = {
9868                 cls : 'input-group',
9869                 cn :  [] 
9870             };
9871             if (this.before) {
9872                 inputblock.cn.push({
9873                     tag :'span',
9874                     cls : 'input-group-addon',
9875                     html : this.before
9876                 });
9877             }
9878             
9879             inputblock.cn.push(input);
9880             
9881             if(this.hasFeedback && !this.allowBlank){
9882                 inputblock.cls += ' has-feedback';
9883                 inputblock.cn.push(feedback);
9884             }
9885             
9886             if (this.after) {
9887                 inputblock.cn.push({
9888                     tag :'span',
9889                     cls : 'input-group-addon',
9890                     html : this.after
9891                 });
9892             }
9893             
9894         };
9895         
9896         var box = {
9897             tag: 'div',
9898             cn: [
9899                 {
9900                     tag: 'input',
9901                     type : 'hidden',
9902                     cls: 'form-hidden-field'
9903                 },
9904                 inputblock
9905             ]
9906             
9907         };
9908         
9909         if(this.multiple){
9910             box = {
9911                 tag: 'div',
9912                 cn: [
9913                     {
9914                         tag: 'input',
9915                         type : 'hidden',
9916                         cls: 'form-hidden-field'
9917                     },
9918                     {
9919                         tag: 'ul',
9920                         cls: 'roo-select2-choices',
9921                         cn:[
9922                             {
9923                                 tag: 'li',
9924                                 cls: 'roo-select2-search-field',
9925                                 cn: [
9926
9927                                     inputblock
9928                                 ]
9929                             }
9930                         ]
9931                     }
9932                 ]
9933             }
9934         };
9935         
9936         var combobox = {
9937             cls: 'roo-select2-container input-group',
9938             cn: [
9939                 box
9940 //                {
9941 //                    tag: 'ul',
9942 //                    cls: 'typeahead typeahead-long dropdown-menu',
9943 //                    style: 'display:none'
9944 //                }
9945             ]
9946         };
9947         
9948         if(!this.multiple && this.showToggleBtn){
9949             
9950             var caret = {
9951                         tag: 'span',
9952                         cls: 'caret'
9953              };
9954             if (this.caret != false) {
9955                 caret = {
9956                      tag: 'i',
9957                      cls: 'fa fa-' + this.caret
9958                 };
9959                 
9960             }
9961             
9962             combobox.cn.push({
9963                 tag :'span',
9964                 cls : 'input-group-addon btn dropdown-toggle',
9965                 cn : [
9966                     caret,
9967                     {
9968                         tag: 'span',
9969                         cls: 'combobox-clear',
9970                         cn  : [
9971                             {
9972                                 tag : 'i',
9973                                 cls: 'icon-remove'
9974                             }
9975                         ]
9976                     }
9977                 ]
9978
9979             })
9980         }
9981         
9982         if(this.multiple){
9983             combobox.cls += ' roo-select2-container-multi';
9984         }
9985         
9986         if (align ==='left' && this.fieldLabel.length) {
9987             
9988             cfg.cls += ' roo-form-group-label-left';
9989
9990             cfg.cn = [
9991                 {
9992                     tag : 'i',
9993                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9994                     tooltip : 'This field is required'
9995                 },
9996                 {
9997                     tag: 'label',
9998                     'for' :  id,
9999                     cls : 'control-label',
10000                     html : this.fieldLabel
10001
10002                 },
10003                 {
10004                     cls : "", 
10005                     cn: [
10006                         combobox
10007                     ]
10008                 }
10009
10010             ];
10011             
10012             var labelCfg = cfg.cn[1];
10013             var contentCfg = cfg.cn[2];
10014             
10015             if(this.indicatorpos == 'right'){
10016                 cfg.cn = [
10017                     {
10018                         tag: 'label',
10019                         'for' :  id,
10020                         cls : 'control-label',
10021                         cn : [
10022                             {
10023                                 tag : 'span',
10024                                 html : this.fieldLabel
10025                             },
10026                             {
10027                                 tag : 'i',
10028                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10029                                 tooltip : 'This field is required'
10030                             }
10031                         ]
10032                     },
10033                     {
10034                         cls : "", 
10035                         cn: [
10036                             combobox
10037                         ]
10038                     }
10039
10040                 ];
10041                 
10042                 labelCfg = cfg.cn[0];
10043                 contentCfg = cfg.cn[1];
10044             }
10045             
10046             if(this.labelWidth > 12){
10047                 labelCfg.style = "width: " + this.labelWidth + 'px';
10048             }
10049             
10050             if(this.labelWidth < 13 && this.labelmd == 0){
10051                 this.labelmd = this.labelWidth;
10052             }
10053             
10054             if(this.labellg > 0){
10055                 labelCfg.cls += ' col-lg-' + this.labellg;
10056                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10057             }
10058             
10059             if(this.labelmd > 0){
10060                 labelCfg.cls += ' col-md-' + this.labelmd;
10061                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10062             }
10063             
10064             if(this.labelsm > 0){
10065                 labelCfg.cls += ' col-sm-' + this.labelsm;
10066                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10067             }
10068             
10069             if(this.labelxs > 0){
10070                 labelCfg.cls += ' col-xs-' + this.labelxs;
10071                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10072             }
10073             
10074         } else if ( this.fieldLabel.length) {
10075 //                Roo.log(" label");
10076             cfg.cn = [
10077                 {
10078                    tag : 'i',
10079                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10080                    tooltip : 'This field is required'
10081                },
10082                {
10083                    tag: 'label',
10084                    //cls : 'input-group-addon',
10085                    html : this.fieldLabel
10086
10087                },
10088
10089                combobox
10090
10091             ];
10092             
10093             if(this.indicatorpos == 'right'){
10094                 
10095                 cfg.cn = [
10096                     {
10097                        tag: 'label',
10098                        cn : [
10099                            {
10100                                tag : 'span',
10101                                html : this.fieldLabel
10102                            },
10103                            {
10104                               tag : 'i',
10105                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10106                               tooltip : 'This field is required'
10107                            }
10108                        ]
10109
10110                     },
10111                     combobox
10112
10113                 ];
10114
10115             }
10116
10117         } else {
10118             
10119 //                Roo.log(" no label && no align");
10120                 cfg = combobox
10121                      
10122                 
10123         }
10124         
10125         var settings=this;
10126         ['xs','sm','md','lg'].map(function(size){
10127             if (settings[size]) {
10128                 cfg.cls += ' col-' + size + '-' + settings[size];
10129             }
10130         });
10131         
10132         return cfg;
10133         
10134     },
10135     
10136     
10137     
10138     // private
10139     onResize : function(w, h){
10140 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10141 //        if(typeof w == 'number'){
10142 //            var x = w - this.trigger.getWidth();
10143 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10144 //            this.trigger.setStyle('left', x+'px');
10145 //        }
10146     },
10147
10148     // private
10149     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10150
10151     // private
10152     getResizeEl : function(){
10153         return this.inputEl();
10154     },
10155
10156     // private
10157     getPositionEl : function(){
10158         return this.inputEl();
10159     },
10160
10161     // private
10162     alignErrorIcon : function(){
10163         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10164     },
10165
10166     // private
10167     initEvents : function(){
10168         
10169         this.createList();
10170         
10171         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10172         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10173         if(!this.multiple && this.showToggleBtn){
10174             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10175             if(this.hideTrigger){
10176                 this.trigger.setDisplayed(false);
10177             }
10178             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10179         }
10180         
10181         if(this.multiple){
10182             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10183         }
10184         
10185         if(this.removable && !this.editable && !this.tickable){
10186             var close = this.closeTriggerEl();
10187             
10188             if(close){
10189                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10190                 close.on('click', this.removeBtnClick, this, close);
10191             }
10192         }
10193         
10194         //this.trigger.addClassOnOver('x-form-trigger-over');
10195         //this.trigger.addClassOnClick('x-form-trigger-click');
10196         
10197         //if(!this.width){
10198         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10199         //}
10200     },
10201     
10202     closeTriggerEl : function()
10203     {
10204         var close = this.el.select('.roo-combo-removable-btn', true).first();
10205         return close ? close : false;
10206     },
10207     
10208     removeBtnClick : function(e, h, el)
10209     {
10210         e.preventDefault();
10211         
10212         if(this.fireEvent("remove", this) !== false){
10213             this.reset();
10214             this.fireEvent("afterremove", this)
10215         }
10216     },
10217     
10218     createList : function()
10219     {
10220         this.list = Roo.get(document.body).createChild({
10221             tag: 'ul',
10222             cls: 'typeahead typeahead-long dropdown-menu',
10223             style: 'display:none'
10224         });
10225         
10226         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10227         
10228     },
10229
10230     // private
10231     initTrigger : function(){
10232        
10233     },
10234
10235     // private
10236     onDestroy : function(){
10237         if(this.trigger){
10238             this.trigger.removeAllListeners();
10239           //  this.trigger.remove();
10240         }
10241         //if(this.wrap){
10242         //    this.wrap.remove();
10243         //}
10244         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10245     },
10246
10247     // private
10248     onFocus : function(){
10249         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10250         /*
10251         if(!this.mimicing){
10252             this.wrap.addClass('x-trigger-wrap-focus');
10253             this.mimicing = true;
10254             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10255             if(this.monitorTab){
10256                 this.el.on("keydown", this.checkTab, this);
10257             }
10258         }
10259         */
10260     },
10261
10262     // private
10263     checkTab : function(e){
10264         if(e.getKey() == e.TAB){
10265             this.triggerBlur();
10266         }
10267     },
10268
10269     // private
10270     onBlur : function(){
10271         // do nothing
10272     },
10273
10274     // private
10275     mimicBlur : function(e, t){
10276         /*
10277         if(!this.wrap.contains(t) && this.validateBlur()){
10278             this.triggerBlur();
10279         }
10280         */
10281     },
10282
10283     // private
10284     triggerBlur : function(){
10285         this.mimicing = false;
10286         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10287         if(this.monitorTab){
10288             this.el.un("keydown", this.checkTab, this);
10289         }
10290         //this.wrap.removeClass('x-trigger-wrap-focus');
10291         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10292     },
10293
10294     // private
10295     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10296     validateBlur : function(e, t){
10297         return true;
10298     },
10299
10300     // private
10301     onDisable : function(){
10302         this.inputEl().dom.disabled = true;
10303         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10304         //if(this.wrap){
10305         //    this.wrap.addClass('x-item-disabled');
10306         //}
10307     },
10308
10309     // private
10310     onEnable : function(){
10311         this.inputEl().dom.disabled = false;
10312         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10313         //if(this.wrap){
10314         //    this.el.removeClass('x-item-disabled');
10315         //}
10316     },
10317
10318     // private
10319     onShow : function(){
10320         var ae = this.getActionEl();
10321         
10322         if(ae){
10323             ae.dom.style.display = '';
10324             ae.dom.style.visibility = 'visible';
10325         }
10326     },
10327
10328     // private
10329     
10330     onHide : function(){
10331         var ae = this.getActionEl();
10332         ae.dom.style.display = 'none';
10333     },
10334
10335     /**
10336      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10337      * by an implementing function.
10338      * @method
10339      * @param {EventObject} e
10340      */
10341     onTriggerClick : Roo.emptyFn
10342 });
10343  /*
10344  * Based on:
10345  * Ext JS Library 1.1.1
10346  * Copyright(c) 2006-2007, Ext JS, LLC.
10347  *
10348  * Originally Released Under LGPL - original licence link has changed is not relivant.
10349  *
10350  * Fork - LGPL
10351  * <script type="text/javascript">
10352  */
10353
10354
10355 /**
10356  * @class Roo.data.SortTypes
10357  * @singleton
10358  * Defines the default sorting (casting?) comparison functions used when sorting data.
10359  */
10360 Roo.data.SortTypes = {
10361     /**
10362      * Default sort that does nothing
10363      * @param {Mixed} s The value being converted
10364      * @return {Mixed} The comparison value
10365      */
10366     none : function(s){
10367         return s;
10368     },
10369     
10370     /**
10371      * The regular expression used to strip tags
10372      * @type {RegExp}
10373      * @property
10374      */
10375     stripTagsRE : /<\/?[^>]+>/gi,
10376     
10377     /**
10378      * Strips all HTML tags to sort on text only
10379      * @param {Mixed} s The value being converted
10380      * @return {String} The comparison value
10381      */
10382     asText : function(s){
10383         return String(s).replace(this.stripTagsRE, "");
10384     },
10385     
10386     /**
10387      * Strips all HTML tags to sort on text only - Case insensitive
10388      * @param {Mixed} s The value being converted
10389      * @return {String} The comparison value
10390      */
10391     asUCText : function(s){
10392         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10393     },
10394     
10395     /**
10396      * Case insensitive string
10397      * @param {Mixed} s The value being converted
10398      * @return {String} The comparison value
10399      */
10400     asUCString : function(s) {
10401         return String(s).toUpperCase();
10402     },
10403     
10404     /**
10405      * Date sorting
10406      * @param {Mixed} s The value being converted
10407      * @return {Number} The comparison value
10408      */
10409     asDate : function(s) {
10410         if(!s){
10411             return 0;
10412         }
10413         if(s instanceof Date){
10414             return s.getTime();
10415         }
10416         return Date.parse(String(s));
10417     },
10418     
10419     /**
10420      * Float sorting
10421      * @param {Mixed} s The value being converted
10422      * @return {Float} The comparison value
10423      */
10424     asFloat : function(s) {
10425         var val = parseFloat(String(s).replace(/,/g, ""));
10426         if(isNaN(val)) {
10427             val = 0;
10428         }
10429         return val;
10430     },
10431     
10432     /**
10433      * Integer sorting
10434      * @param {Mixed} s The value being converted
10435      * @return {Number} The comparison value
10436      */
10437     asInt : function(s) {
10438         var val = parseInt(String(s).replace(/,/g, ""));
10439         if(isNaN(val)) {
10440             val = 0;
10441         }
10442         return val;
10443     }
10444 };/*
10445  * Based on:
10446  * Ext JS Library 1.1.1
10447  * Copyright(c) 2006-2007, Ext JS, LLC.
10448  *
10449  * Originally Released Under LGPL - original licence link has changed is not relivant.
10450  *
10451  * Fork - LGPL
10452  * <script type="text/javascript">
10453  */
10454
10455 /**
10456 * @class Roo.data.Record
10457  * Instances of this class encapsulate both record <em>definition</em> information, and record
10458  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10459  * to access Records cached in an {@link Roo.data.Store} object.<br>
10460  * <p>
10461  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10462  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10463  * objects.<br>
10464  * <p>
10465  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10466  * @constructor
10467  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10468  * {@link #create}. The parameters are the same.
10469  * @param {Array} data An associative Array of data values keyed by the field name.
10470  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10471  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10472  * not specified an integer id is generated.
10473  */
10474 Roo.data.Record = function(data, id){
10475     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10476     this.data = data;
10477 };
10478
10479 /**
10480  * Generate a constructor for a specific record layout.
10481  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10482  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10483  * Each field definition object may contain the following properties: <ul>
10484  * <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,
10485  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10486  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10487  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10488  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10489  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10490  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10491  * this may be omitted.</p></li>
10492  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10493  * <ul><li>auto (Default, implies no conversion)</li>
10494  * <li>string</li>
10495  * <li>int</li>
10496  * <li>float</li>
10497  * <li>boolean</li>
10498  * <li>date</li></ul></p></li>
10499  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10500  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10501  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10502  * by the Reader into an object that will be stored in the Record. It is passed the
10503  * following parameters:<ul>
10504  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10505  * </ul></p></li>
10506  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10507  * </ul>
10508  * <br>usage:<br><pre><code>
10509 var TopicRecord = Roo.data.Record.create(
10510     {name: 'title', mapping: 'topic_title'},
10511     {name: 'author', mapping: 'username'},
10512     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10513     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10514     {name: 'lastPoster', mapping: 'user2'},
10515     {name: 'excerpt', mapping: 'post_text'}
10516 );
10517
10518 var myNewRecord = new TopicRecord({
10519     title: 'Do my job please',
10520     author: 'noobie',
10521     totalPosts: 1,
10522     lastPost: new Date(),
10523     lastPoster: 'Animal',
10524     excerpt: 'No way dude!'
10525 });
10526 myStore.add(myNewRecord);
10527 </code></pre>
10528  * @method create
10529  * @static
10530  */
10531 Roo.data.Record.create = function(o){
10532     var f = function(){
10533         f.superclass.constructor.apply(this, arguments);
10534     };
10535     Roo.extend(f, Roo.data.Record);
10536     var p = f.prototype;
10537     p.fields = new Roo.util.MixedCollection(false, function(field){
10538         return field.name;
10539     });
10540     for(var i = 0, len = o.length; i < len; i++){
10541         p.fields.add(new Roo.data.Field(o[i]));
10542     }
10543     f.getField = function(name){
10544         return p.fields.get(name);  
10545     };
10546     return f;
10547 };
10548
10549 Roo.data.Record.AUTO_ID = 1000;
10550 Roo.data.Record.EDIT = 'edit';
10551 Roo.data.Record.REJECT = 'reject';
10552 Roo.data.Record.COMMIT = 'commit';
10553
10554 Roo.data.Record.prototype = {
10555     /**
10556      * Readonly flag - true if this record has been modified.
10557      * @type Boolean
10558      */
10559     dirty : false,
10560     editing : false,
10561     error: null,
10562     modified: null,
10563
10564     // private
10565     join : function(store){
10566         this.store = store;
10567     },
10568
10569     /**
10570      * Set the named field to the specified value.
10571      * @param {String} name The name of the field to set.
10572      * @param {Object} value The value to set the field to.
10573      */
10574     set : function(name, value){
10575         if(this.data[name] == value){
10576             return;
10577         }
10578         this.dirty = true;
10579         if(!this.modified){
10580             this.modified = {};
10581         }
10582         if(typeof this.modified[name] == 'undefined'){
10583             this.modified[name] = this.data[name];
10584         }
10585         this.data[name] = value;
10586         if(!this.editing && this.store){
10587             this.store.afterEdit(this);
10588         }       
10589     },
10590
10591     /**
10592      * Get the value of the named field.
10593      * @param {String} name The name of the field to get the value of.
10594      * @return {Object} The value of the field.
10595      */
10596     get : function(name){
10597         return this.data[name]; 
10598     },
10599
10600     // private
10601     beginEdit : function(){
10602         this.editing = true;
10603         this.modified = {}; 
10604     },
10605
10606     // private
10607     cancelEdit : function(){
10608         this.editing = false;
10609         delete this.modified;
10610     },
10611
10612     // private
10613     endEdit : function(){
10614         this.editing = false;
10615         if(this.dirty && this.store){
10616             this.store.afterEdit(this);
10617         }
10618     },
10619
10620     /**
10621      * Usually called by the {@link Roo.data.Store} which owns the Record.
10622      * Rejects all changes made to the Record since either creation, or the last commit operation.
10623      * Modified fields are reverted to their original values.
10624      * <p>
10625      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10626      * of reject operations.
10627      */
10628     reject : function(){
10629         var m = this.modified;
10630         for(var n in m){
10631             if(typeof m[n] != "function"){
10632                 this.data[n] = m[n];
10633             }
10634         }
10635         this.dirty = false;
10636         delete this.modified;
10637         this.editing = false;
10638         if(this.store){
10639             this.store.afterReject(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Commits all changes made to the Record since either creation, or the last commit operation.
10646      * <p>
10647      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10648      * of commit operations.
10649      */
10650     commit : function(){
10651         this.dirty = false;
10652         delete this.modified;
10653         this.editing = false;
10654         if(this.store){
10655             this.store.afterCommit(this);
10656         }
10657     },
10658
10659     // private
10660     hasError : function(){
10661         return this.error != null;
10662     },
10663
10664     // private
10665     clearError : function(){
10666         this.error = null;
10667     },
10668
10669     /**
10670      * Creates a copy of this record.
10671      * @param {String} id (optional) A new record id if you don't want to use this record's id
10672      * @return {Record}
10673      */
10674     copy : function(newId) {
10675         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10676     }
10677 };/*
10678  * Based on:
10679  * Ext JS Library 1.1.1
10680  * Copyright(c) 2006-2007, Ext JS, LLC.
10681  *
10682  * Originally Released Under LGPL - original licence link has changed is not relivant.
10683  *
10684  * Fork - LGPL
10685  * <script type="text/javascript">
10686  */
10687
10688
10689
10690 /**
10691  * @class Roo.data.Store
10692  * @extends Roo.util.Observable
10693  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10694  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10695  * <p>
10696  * 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
10697  * has no knowledge of the format of the data returned by the Proxy.<br>
10698  * <p>
10699  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10700  * instances from the data object. These records are cached and made available through accessor functions.
10701  * @constructor
10702  * Creates a new Store.
10703  * @param {Object} config A config object containing the objects needed for the Store to access data,
10704  * and read the data into Records.
10705  */
10706 Roo.data.Store = function(config){
10707     this.data = new Roo.util.MixedCollection(false);
10708     this.data.getKey = function(o){
10709         return o.id;
10710     };
10711     this.baseParams = {};
10712     // private
10713     this.paramNames = {
10714         "start" : "start",
10715         "limit" : "limit",
10716         "sort" : "sort",
10717         "dir" : "dir",
10718         "multisort" : "_multisort"
10719     };
10720
10721     if(config && config.data){
10722         this.inlineData = config.data;
10723         delete config.data;
10724     }
10725
10726     Roo.apply(this, config);
10727     
10728     if(this.reader){ // reader passed
10729         this.reader = Roo.factory(this.reader, Roo.data);
10730         this.reader.xmodule = this.xmodule || false;
10731         if(!this.recordType){
10732             this.recordType = this.reader.recordType;
10733         }
10734         if(this.reader.onMetaChange){
10735             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10736         }
10737     }
10738
10739     if(this.recordType){
10740         this.fields = this.recordType.prototype.fields;
10741     }
10742     this.modified = [];
10743
10744     this.addEvents({
10745         /**
10746          * @event datachanged
10747          * Fires when the data cache has changed, and a widget which is using this Store
10748          * as a Record cache should refresh its view.
10749          * @param {Store} this
10750          */
10751         datachanged : true,
10752         /**
10753          * @event metachange
10754          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10755          * @param {Store} this
10756          * @param {Object} meta The JSON metadata
10757          */
10758         metachange : true,
10759         /**
10760          * @event add
10761          * Fires when Records have been added to the Store
10762          * @param {Store} this
10763          * @param {Roo.data.Record[]} records The array of Records added
10764          * @param {Number} index The index at which the record(s) were added
10765          */
10766         add : true,
10767         /**
10768          * @event remove
10769          * Fires when a Record has been removed from the Store
10770          * @param {Store} this
10771          * @param {Roo.data.Record} record The Record that was removed
10772          * @param {Number} index The index at which the record was removed
10773          */
10774         remove : true,
10775         /**
10776          * @event update
10777          * Fires when a Record has been updated
10778          * @param {Store} this
10779          * @param {Roo.data.Record} record The Record that was updated
10780          * @param {String} operation The update operation being performed.  Value may be one of:
10781          * <pre><code>
10782  Roo.data.Record.EDIT
10783  Roo.data.Record.REJECT
10784  Roo.data.Record.COMMIT
10785          * </code></pre>
10786          */
10787         update : true,
10788         /**
10789          * @event clear
10790          * Fires when the data cache has been cleared.
10791          * @param {Store} this
10792          */
10793         clear : true,
10794         /**
10795          * @event beforeload
10796          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10797          * the load action will be canceled.
10798          * @param {Store} this
10799          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10800          */
10801         beforeload : true,
10802         /**
10803          * @event beforeloadadd
10804          * Fires after a new set of Records has been loaded.
10805          * @param {Store} this
10806          * @param {Roo.data.Record[]} records The Records that were loaded
10807          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10808          */
10809         beforeloadadd : true,
10810         /**
10811          * @event load
10812          * Fires after a new set of Records has been loaded, before they are added to the store.
10813          * @param {Store} this
10814          * @param {Roo.data.Record[]} records The Records that were loaded
10815          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10816          * @params {Object} return from reader
10817          */
10818         load : true,
10819         /**
10820          * @event loadexception
10821          * Fires if an exception occurs in the Proxy during loading.
10822          * Called with the signature of the Proxy's "loadexception" event.
10823          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10824          * 
10825          * @param {Proxy} 
10826          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10827          * @param {Object} load options 
10828          * @param {Object} jsonData from your request (normally this contains the Exception)
10829          */
10830         loadexception : true
10831     });
10832     
10833     if(this.proxy){
10834         this.proxy = Roo.factory(this.proxy, Roo.data);
10835         this.proxy.xmodule = this.xmodule || false;
10836         this.relayEvents(this.proxy,  ["loadexception"]);
10837     }
10838     this.sortToggle = {};
10839     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10840
10841     Roo.data.Store.superclass.constructor.call(this);
10842
10843     if(this.inlineData){
10844         this.loadData(this.inlineData);
10845         delete this.inlineData;
10846     }
10847 };
10848
10849 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10850      /**
10851     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10852     * without a remote query - used by combo/forms at present.
10853     */
10854     
10855     /**
10856     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10857     */
10858     /**
10859     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10860     */
10861     /**
10862     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10863     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10864     */
10865     /**
10866     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10867     * on any HTTP request
10868     */
10869     /**
10870     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10871     */
10872     /**
10873     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10874     */
10875     multiSort: false,
10876     /**
10877     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10878     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10879     */
10880     remoteSort : false,
10881
10882     /**
10883     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10884      * loaded or when a record is removed. (defaults to false).
10885     */
10886     pruneModifiedRecords : false,
10887
10888     // private
10889     lastOptions : null,
10890
10891     /**
10892      * Add Records to the Store and fires the add event.
10893      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10894      */
10895     add : function(records){
10896         records = [].concat(records);
10897         for(var i = 0, len = records.length; i < len; i++){
10898             records[i].join(this);
10899         }
10900         var index = this.data.length;
10901         this.data.addAll(records);
10902         this.fireEvent("add", this, records, index);
10903     },
10904
10905     /**
10906      * Remove a Record from the Store and fires the remove event.
10907      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10908      */
10909     remove : function(record){
10910         var index = this.data.indexOf(record);
10911         this.data.removeAt(index);
10912         if(this.pruneModifiedRecords){
10913             this.modified.remove(record);
10914         }
10915         this.fireEvent("remove", this, record, index);
10916     },
10917
10918     /**
10919      * Remove all Records from the Store and fires the clear event.
10920      */
10921     removeAll : function(){
10922         this.data.clear();
10923         if(this.pruneModifiedRecords){
10924             this.modified = [];
10925         }
10926         this.fireEvent("clear", this);
10927     },
10928
10929     /**
10930      * Inserts Records to the Store at the given index and fires the add event.
10931      * @param {Number} index The start index at which to insert the passed Records.
10932      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10933      */
10934     insert : function(index, records){
10935         records = [].concat(records);
10936         for(var i = 0, len = records.length; i < len; i++){
10937             this.data.insert(index, records[i]);
10938             records[i].join(this);
10939         }
10940         this.fireEvent("add", this, records, index);
10941     },
10942
10943     /**
10944      * Get the index within the cache of the passed Record.
10945      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10946      * @return {Number} The index of the passed Record. Returns -1 if not found.
10947      */
10948     indexOf : function(record){
10949         return this.data.indexOf(record);
10950     },
10951
10952     /**
10953      * Get the index within the cache of the Record with the passed id.
10954      * @param {String} id The id of the Record to find.
10955      * @return {Number} The index of the Record. Returns -1 if not found.
10956      */
10957     indexOfId : function(id){
10958         return this.data.indexOfKey(id);
10959     },
10960
10961     /**
10962      * Get the Record with the specified id.
10963      * @param {String} id The id of the Record to find.
10964      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10965      */
10966     getById : function(id){
10967         return this.data.key(id);
10968     },
10969
10970     /**
10971      * Get the Record at the specified index.
10972      * @param {Number} index The index of the Record to find.
10973      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10974      */
10975     getAt : function(index){
10976         return this.data.itemAt(index);
10977     },
10978
10979     /**
10980      * Returns a range of Records between specified indices.
10981      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10982      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10983      * @return {Roo.data.Record[]} An array of Records
10984      */
10985     getRange : function(start, end){
10986         return this.data.getRange(start, end);
10987     },
10988
10989     // private
10990     storeOptions : function(o){
10991         o = Roo.apply({}, o);
10992         delete o.callback;
10993         delete o.scope;
10994         this.lastOptions = o;
10995     },
10996
10997     /**
10998      * Loads the Record cache from the configured Proxy using the configured Reader.
10999      * <p>
11000      * If using remote paging, then the first load call must specify the <em>start</em>
11001      * and <em>limit</em> properties in the options.params property to establish the initial
11002      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11003      * <p>
11004      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11005      * and this call will return before the new data has been loaded. Perform any post-processing
11006      * in a callback function, or in a "load" event handler.</strong>
11007      * <p>
11008      * @param {Object} options An object containing properties which control loading options:<ul>
11009      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11010      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11011      * passed the following arguments:<ul>
11012      * <li>r : Roo.data.Record[]</li>
11013      * <li>options: Options object from the load call</li>
11014      * <li>success: Boolean success indicator</li></ul></li>
11015      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11016      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11017      * </ul>
11018      */
11019     load : function(options){
11020         options = options || {};
11021         if(this.fireEvent("beforeload", this, options) !== false){
11022             this.storeOptions(options);
11023             var p = Roo.apply(options.params || {}, this.baseParams);
11024             // if meta was not loaded from remote source.. try requesting it.
11025             if (!this.reader.metaFromRemote) {
11026                 p._requestMeta = 1;
11027             }
11028             if(this.sortInfo && this.remoteSort){
11029                 var pn = this.paramNames;
11030                 p[pn["sort"]] = this.sortInfo.field;
11031                 p[pn["dir"]] = this.sortInfo.direction;
11032             }
11033             if (this.multiSort) {
11034                 var pn = this.paramNames;
11035                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11036             }
11037             
11038             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11039         }
11040     },
11041
11042     /**
11043      * Reloads the Record cache from the configured Proxy using the configured Reader and
11044      * the options from the last load operation performed.
11045      * @param {Object} options (optional) An object containing properties which may override the options
11046      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11047      * the most recently used options are reused).
11048      */
11049     reload : function(options){
11050         this.load(Roo.applyIf(options||{}, this.lastOptions));
11051     },
11052
11053     // private
11054     // Called as a callback by the Reader during a load operation.
11055     loadRecords : function(o, options, success){
11056         if(!o || success === false){
11057             if(success !== false){
11058                 this.fireEvent("load", this, [], options, o);
11059             }
11060             if(options.callback){
11061                 options.callback.call(options.scope || this, [], options, false);
11062             }
11063             return;
11064         }
11065         // if data returned failure - throw an exception.
11066         if (o.success === false) {
11067             // show a message if no listener is registered.
11068             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11069                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11070             }
11071             // loadmask wil be hooked into this..
11072             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11073             return;
11074         }
11075         var r = o.records, t = o.totalRecords || r.length;
11076         
11077         this.fireEvent("beforeloadadd", this, r, options, o);
11078         
11079         if(!options || options.add !== true){
11080             if(this.pruneModifiedRecords){
11081                 this.modified = [];
11082             }
11083             for(var i = 0, len = r.length; i < len; i++){
11084                 r[i].join(this);
11085             }
11086             if(this.snapshot){
11087                 this.data = this.snapshot;
11088                 delete this.snapshot;
11089             }
11090             this.data.clear();
11091             this.data.addAll(r);
11092             this.totalLength = t;
11093             this.applySort();
11094             this.fireEvent("datachanged", this);
11095         }else{
11096             this.totalLength = Math.max(t, this.data.length+r.length);
11097             this.add(r);
11098         }
11099         
11100         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11101                 
11102             var e = new Roo.data.Record({});
11103
11104             e.set(this.parent.displayField, this.parent.emptyTitle);
11105             e.set(this.parent.valueField, '');
11106
11107             this.insert(0, e);
11108         }
11109             
11110         this.fireEvent("load", this, r, options, o);
11111         if(options.callback){
11112             options.callback.call(options.scope || this, r, options, true);
11113         }
11114     },
11115
11116
11117     /**
11118      * Loads data from a passed data block. A Reader which understands the format of the data
11119      * must have been configured in the constructor.
11120      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11121      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11122      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11123      */
11124     loadData : function(o, append){
11125         var r = this.reader.readRecords(o);
11126         this.loadRecords(r, {add: append}, true);
11127     },
11128
11129     /**
11130      * Gets the number of cached records.
11131      * <p>
11132      * <em>If using paging, this may not be the total size of the dataset. If the data object
11133      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11134      * the data set size</em>
11135      */
11136     getCount : function(){
11137         return this.data.length || 0;
11138     },
11139
11140     /**
11141      * Gets the total number of records in the dataset as returned by the server.
11142      * <p>
11143      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11144      * the dataset size</em>
11145      */
11146     getTotalCount : function(){
11147         return this.totalLength || 0;
11148     },
11149
11150     /**
11151      * Returns the sort state of the Store as an object with two properties:
11152      * <pre><code>
11153  field {String} The name of the field by which the Records are sorted
11154  direction {String} The sort order, "ASC" or "DESC"
11155      * </code></pre>
11156      */
11157     getSortState : function(){
11158         return this.sortInfo;
11159     },
11160
11161     // private
11162     applySort : function(){
11163         if(this.sortInfo && !this.remoteSort){
11164             var s = this.sortInfo, f = s.field;
11165             var st = this.fields.get(f).sortType;
11166             var fn = function(r1, r2){
11167                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11168                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11169             };
11170             this.data.sort(s.direction, fn);
11171             if(this.snapshot && this.snapshot != this.data){
11172                 this.snapshot.sort(s.direction, fn);
11173             }
11174         }
11175     },
11176
11177     /**
11178      * Sets the default sort column and order to be used by the next load operation.
11179      * @param {String} fieldName The name of the field to sort by.
11180      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11181      */
11182     setDefaultSort : function(field, dir){
11183         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11184     },
11185
11186     /**
11187      * Sort the Records.
11188      * If remote sorting is used, the sort is performed on the server, and the cache is
11189      * reloaded. If local sorting is used, the cache is sorted internally.
11190      * @param {String} fieldName The name of the field to sort by.
11191      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11192      */
11193     sort : function(fieldName, dir){
11194         var f = this.fields.get(fieldName);
11195         if(!dir){
11196             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11197             
11198             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11199                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11200             }else{
11201                 dir = f.sortDir;
11202             }
11203         }
11204         this.sortToggle[f.name] = dir;
11205         this.sortInfo = {field: f.name, direction: dir};
11206         if(!this.remoteSort){
11207             this.applySort();
11208             this.fireEvent("datachanged", this);
11209         }else{
11210             this.load(this.lastOptions);
11211         }
11212     },
11213
11214     /**
11215      * Calls the specified function for each of the Records in the cache.
11216      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11217      * Returning <em>false</em> aborts and exits the iteration.
11218      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11219      */
11220     each : function(fn, scope){
11221         this.data.each(fn, scope);
11222     },
11223
11224     /**
11225      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11226      * (e.g., during paging).
11227      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11228      */
11229     getModifiedRecords : function(){
11230         return this.modified;
11231     },
11232
11233     // private
11234     createFilterFn : function(property, value, anyMatch){
11235         if(!value.exec){ // not a regex
11236             value = String(value);
11237             if(value.length == 0){
11238                 return false;
11239             }
11240             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11241         }
11242         return function(r){
11243             return value.test(r.data[property]);
11244         };
11245     },
11246
11247     /**
11248      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11249      * @param {String} property A field on your records
11250      * @param {Number} start The record index to start at (defaults to 0)
11251      * @param {Number} end The last record index to include (defaults to length - 1)
11252      * @return {Number} The sum
11253      */
11254     sum : function(property, start, end){
11255         var rs = this.data.items, v = 0;
11256         start = start || 0;
11257         end = (end || end === 0) ? end : rs.length-1;
11258
11259         for(var i = start; i <= end; i++){
11260             v += (rs[i].data[property] || 0);
11261         }
11262         return v;
11263     },
11264
11265     /**
11266      * Filter the records by a specified property.
11267      * @param {String} field A field on your records
11268      * @param {String/RegExp} value Either a string that the field
11269      * should start with or a RegExp to test against the field
11270      * @param {Boolean} anyMatch True to match any part not just the beginning
11271      */
11272     filter : function(property, value, anyMatch){
11273         var fn = this.createFilterFn(property, value, anyMatch);
11274         return fn ? this.filterBy(fn) : this.clearFilter();
11275     },
11276
11277     /**
11278      * Filter by a function. The specified function will be called with each
11279      * record in this data source. If the function returns true the record is included,
11280      * otherwise it is filtered.
11281      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11282      * @param {Object} scope (optional) The scope of the function (defaults to this)
11283      */
11284     filterBy : function(fn, scope){
11285         this.snapshot = this.snapshot || this.data;
11286         this.data = this.queryBy(fn, scope||this);
11287         this.fireEvent("datachanged", this);
11288     },
11289
11290     /**
11291      * Query the records by a specified property.
11292      * @param {String} field A field on your records
11293      * @param {String/RegExp} value Either a string that the field
11294      * should start with or a RegExp to test against the field
11295      * @param {Boolean} anyMatch True to match any part not just the beginning
11296      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11297      */
11298     query : function(property, value, anyMatch){
11299         var fn = this.createFilterFn(property, value, anyMatch);
11300         return fn ? this.queryBy(fn) : this.data.clone();
11301     },
11302
11303     /**
11304      * Query by a function. The specified function will be called with each
11305      * record in this data source. If the function returns true the record is included
11306      * in the results.
11307      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11308      * @param {Object} scope (optional) The scope of the function (defaults to this)
11309       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11310      **/
11311     queryBy : function(fn, scope){
11312         var data = this.snapshot || this.data;
11313         return data.filterBy(fn, scope||this);
11314     },
11315
11316     /**
11317      * Collects unique values for a particular dataIndex from this store.
11318      * @param {String} dataIndex The property to collect
11319      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11320      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11321      * @return {Array} An array of the unique values
11322      **/
11323     collect : function(dataIndex, allowNull, bypassFilter){
11324         var d = (bypassFilter === true && this.snapshot) ?
11325                 this.snapshot.items : this.data.items;
11326         var v, sv, r = [], l = {};
11327         for(var i = 0, len = d.length; i < len; i++){
11328             v = d[i].data[dataIndex];
11329             sv = String(v);
11330             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11331                 l[sv] = true;
11332                 r[r.length] = v;
11333             }
11334         }
11335         return r;
11336     },
11337
11338     /**
11339      * Revert to a view of the Record cache with no filtering applied.
11340      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11341      */
11342     clearFilter : function(suppressEvent){
11343         if(this.snapshot && this.snapshot != this.data){
11344             this.data = this.snapshot;
11345             delete this.snapshot;
11346             if(suppressEvent !== true){
11347                 this.fireEvent("datachanged", this);
11348             }
11349         }
11350     },
11351
11352     // private
11353     afterEdit : function(record){
11354         if(this.modified.indexOf(record) == -1){
11355             this.modified.push(record);
11356         }
11357         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11358     },
11359     
11360     // private
11361     afterReject : function(record){
11362         this.modified.remove(record);
11363         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11364     },
11365
11366     // private
11367     afterCommit : function(record){
11368         this.modified.remove(record);
11369         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11370     },
11371
11372     /**
11373      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11374      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11375      */
11376     commitChanges : function(){
11377         var m = this.modified.slice(0);
11378         this.modified = [];
11379         for(var i = 0, len = m.length; i < len; i++){
11380             m[i].commit();
11381         }
11382     },
11383
11384     /**
11385      * Cancel outstanding changes on all changed records.
11386      */
11387     rejectChanges : function(){
11388         var m = this.modified.slice(0);
11389         this.modified = [];
11390         for(var i = 0, len = m.length; i < len; i++){
11391             m[i].reject();
11392         }
11393     },
11394
11395     onMetaChange : function(meta, rtype, o){
11396         this.recordType = rtype;
11397         this.fields = rtype.prototype.fields;
11398         delete this.snapshot;
11399         this.sortInfo = meta.sortInfo || this.sortInfo;
11400         this.modified = [];
11401         this.fireEvent('metachange', this, this.reader.meta);
11402     },
11403     
11404     moveIndex : function(data, type)
11405     {
11406         var index = this.indexOf(data);
11407         
11408         var newIndex = index + type;
11409         
11410         this.remove(data);
11411         
11412         this.insert(newIndex, data);
11413         
11414     }
11415 });/*
11416  * Based on:
11417  * Ext JS Library 1.1.1
11418  * Copyright(c) 2006-2007, Ext JS, LLC.
11419  *
11420  * Originally Released Under LGPL - original licence link has changed is not relivant.
11421  *
11422  * Fork - LGPL
11423  * <script type="text/javascript">
11424  */
11425
11426 /**
11427  * @class Roo.data.SimpleStore
11428  * @extends Roo.data.Store
11429  * Small helper class to make creating Stores from Array data easier.
11430  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11431  * @cfg {Array} fields An array of field definition objects, or field name strings.
11432  * @cfg {Array} data The multi-dimensional array of data
11433  * @constructor
11434  * @param {Object} config
11435  */
11436 Roo.data.SimpleStore = function(config){
11437     Roo.data.SimpleStore.superclass.constructor.call(this, {
11438         isLocal : true,
11439         reader: new Roo.data.ArrayReader({
11440                 id: config.id
11441             },
11442             Roo.data.Record.create(config.fields)
11443         ),
11444         proxy : new Roo.data.MemoryProxy(config.data)
11445     });
11446     this.load();
11447 };
11448 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11449  * Based on:
11450  * Ext JS Library 1.1.1
11451  * Copyright(c) 2006-2007, Ext JS, LLC.
11452  *
11453  * Originally Released Under LGPL - original licence link has changed is not relivant.
11454  *
11455  * Fork - LGPL
11456  * <script type="text/javascript">
11457  */
11458
11459 /**
11460 /**
11461  * @extends Roo.data.Store
11462  * @class Roo.data.JsonStore
11463  * Small helper class to make creating Stores for JSON data easier. <br/>
11464 <pre><code>
11465 var store = new Roo.data.JsonStore({
11466     url: 'get-images.php',
11467     root: 'images',
11468     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11469 });
11470 </code></pre>
11471  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11472  * JsonReader and HttpProxy (unless inline data is provided).</b>
11473  * @cfg {Array} fields An array of field definition objects, or field name strings.
11474  * @constructor
11475  * @param {Object} config
11476  */
11477 Roo.data.JsonStore = function(c){
11478     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11479         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11480         reader: new Roo.data.JsonReader(c, c.fields)
11481     }));
11482 };
11483 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11484  * Based on:
11485  * Ext JS Library 1.1.1
11486  * Copyright(c) 2006-2007, Ext JS, LLC.
11487  *
11488  * Originally Released Under LGPL - original licence link has changed is not relivant.
11489  *
11490  * Fork - LGPL
11491  * <script type="text/javascript">
11492  */
11493
11494  
11495 Roo.data.Field = function(config){
11496     if(typeof config == "string"){
11497         config = {name: config};
11498     }
11499     Roo.apply(this, config);
11500     
11501     if(!this.type){
11502         this.type = "auto";
11503     }
11504     
11505     var st = Roo.data.SortTypes;
11506     // named sortTypes are supported, here we look them up
11507     if(typeof this.sortType == "string"){
11508         this.sortType = st[this.sortType];
11509     }
11510     
11511     // set default sortType for strings and dates
11512     if(!this.sortType){
11513         switch(this.type){
11514             case "string":
11515                 this.sortType = st.asUCString;
11516                 break;
11517             case "date":
11518                 this.sortType = st.asDate;
11519                 break;
11520             default:
11521                 this.sortType = st.none;
11522         }
11523     }
11524
11525     // define once
11526     var stripRe = /[\$,%]/g;
11527
11528     // prebuilt conversion function for this field, instead of
11529     // switching every time we're reading a value
11530     if(!this.convert){
11531         var cv, dateFormat = this.dateFormat;
11532         switch(this.type){
11533             case "":
11534             case "auto":
11535             case undefined:
11536                 cv = function(v){ return v; };
11537                 break;
11538             case "string":
11539                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11540                 break;
11541             case "int":
11542                 cv = function(v){
11543                     return v !== undefined && v !== null && v !== '' ?
11544                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11545                     };
11546                 break;
11547             case "float":
11548                 cv = function(v){
11549                     return v !== undefined && v !== null && v !== '' ?
11550                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11551                     };
11552                 break;
11553             case "bool":
11554             case "boolean":
11555                 cv = function(v){ return v === true || v === "true" || v == 1; };
11556                 break;
11557             case "date":
11558                 cv = function(v){
11559                     if(!v){
11560                         return '';
11561                     }
11562                     if(v instanceof Date){
11563                         return v;
11564                     }
11565                     if(dateFormat){
11566                         if(dateFormat == "timestamp"){
11567                             return new Date(v*1000);
11568                         }
11569                         return Date.parseDate(v, dateFormat);
11570                     }
11571                     var parsed = Date.parse(v);
11572                     return parsed ? new Date(parsed) : null;
11573                 };
11574              break;
11575             
11576         }
11577         this.convert = cv;
11578     }
11579 };
11580
11581 Roo.data.Field.prototype = {
11582     dateFormat: null,
11583     defaultValue: "",
11584     mapping: null,
11585     sortType : null,
11586     sortDir : "ASC"
11587 };/*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597  
11598 // Base class for reading structured data from a data source.  This class is intended to be
11599 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11600
11601 /**
11602  * @class Roo.data.DataReader
11603  * Base class for reading structured data from a data source.  This class is intended to be
11604  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11605  */
11606
11607 Roo.data.DataReader = function(meta, recordType){
11608     
11609     this.meta = meta;
11610     
11611     this.recordType = recordType instanceof Array ? 
11612         Roo.data.Record.create(recordType) : recordType;
11613 };
11614
11615 Roo.data.DataReader.prototype = {
11616      /**
11617      * Create an empty record
11618      * @param {Object} data (optional) - overlay some values
11619      * @return {Roo.data.Record} record created.
11620      */
11621     newRow :  function(d) {
11622         var da =  {};
11623         this.recordType.prototype.fields.each(function(c) {
11624             switch( c.type) {
11625                 case 'int' : da[c.name] = 0; break;
11626                 case 'date' : da[c.name] = new Date(); break;
11627                 case 'float' : da[c.name] = 0.0; break;
11628                 case 'boolean' : da[c.name] = false; break;
11629                 default : da[c.name] = ""; break;
11630             }
11631             
11632         });
11633         return new this.recordType(Roo.apply(da, d));
11634     }
11635     
11636 };/*
11637  * Based on:
11638  * Ext JS Library 1.1.1
11639  * Copyright(c) 2006-2007, Ext JS, LLC.
11640  *
11641  * Originally Released Under LGPL - original licence link has changed is not relivant.
11642  *
11643  * Fork - LGPL
11644  * <script type="text/javascript">
11645  */
11646
11647 /**
11648  * @class Roo.data.DataProxy
11649  * @extends Roo.data.Observable
11650  * This class is an abstract base class for implementations which provide retrieval of
11651  * unformatted data objects.<br>
11652  * <p>
11653  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11654  * (of the appropriate type which knows how to parse the data object) to provide a block of
11655  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11656  * <p>
11657  * Custom implementations must implement the load method as described in
11658  * {@link Roo.data.HttpProxy#load}.
11659  */
11660 Roo.data.DataProxy = function(){
11661     this.addEvents({
11662         /**
11663          * @event beforeload
11664          * Fires before a network request is made to retrieve a data object.
11665          * @param {Object} This DataProxy object.
11666          * @param {Object} params The params parameter to the load function.
11667          */
11668         beforeload : true,
11669         /**
11670          * @event load
11671          * Fires before the load method's callback is called.
11672          * @param {Object} This DataProxy object.
11673          * @param {Object} o The data object.
11674          * @param {Object} arg The callback argument object passed to the load function.
11675          */
11676         load : true,
11677         /**
11678          * @event loadexception
11679          * Fires if an Exception occurs during data retrieval.
11680          * @param {Object} This DataProxy object.
11681          * @param {Object} o The data object.
11682          * @param {Object} arg The callback argument object passed to the load function.
11683          * @param {Object} e The Exception.
11684          */
11685         loadexception : true
11686     });
11687     Roo.data.DataProxy.superclass.constructor.call(this);
11688 };
11689
11690 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11691
11692     /**
11693      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11694      */
11695 /*
11696  * Based on:
11697  * Ext JS Library 1.1.1
11698  * Copyright(c) 2006-2007, Ext JS, LLC.
11699  *
11700  * Originally Released Under LGPL - original licence link has changed is not relivant.
11701  *
11702  * Fork - LGPL
11703  * <script type="text/javascript">
11704  */
11705 /**
11706  * @class Roo.data.MemoryProxy
11707  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11708  * to the Reader when its load method is called.
11709  * @constructor
11710  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11711  */
11712 Roo.data.MemoryProxy = function(data){
11713     if (data.data) {
11714         data = data.data;
11715     }
11716     Roo.data.MemoryProxy.superclass.constructor.call(this);
11717     this.data = data;
11718 };
11719
11720 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11721     
11722     /**
11723      * Load data from the requested source (in this case an in-memory
11724      * data object passed to the constructor), read the data object into
11725      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11726      * process that block using the passed callback.
11727      * @param {Object} params This parameter is not used by the MemoryProxy class.
11728      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11729      * object into a block of Roo.data.Records.
11730      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11731      * The function must be passed <ul>
11732      * <li>The Record block object</li>
11733      * <li>The "arg" argument from the load function</li>
11734      * <li>A boolean success indicator</li>
11735      * </ul>
11736      * @param {Object} scope The scope in which to call the callback
11737      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11738      */
11739     load : function(params, reader, callback, scope, arg){
11740         params = params || {};
11741         var result;
11742         try {
11743             result = reader.readRecords(this.data);
11744         }catch(e){
11745             this.fireEvent("loadexception", this, arg, null, e);
11746             callback.call(scope, null, arg, false);
11747             return;
11748         }
11749         callback.call(scope, result, arg, true);
11750     },
11751     
11752     // private
11753     update : function(params, records){
11754         
11755     }
11756 });/*
11757  * Based on:
11758  * Ext JS Library 1.1.1
11759  * Copyright(c) 2006-2007, Ext JS, LLC.
11760  *
11761  * Originally Released Under LGPL - original licence link has changed is not relivant.
11762  *
11763  * Fork - LGPL
11764  * <script type="text/javascript">
11765  */
11766 /**
11767  * @class Roo.data.HttpProxy
11768  * @extends Roo.data.DataProxy
11769  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11770  * configured to reference a certain URL.<br><br>
11771  * <p>
11772  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11773  * from which the running page was served.<br><br>
11774  * <p>
11775  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11776  * <p>
11777  * Be aware that to enable the browser to parse an XML document, the server must set
11778  * the Content-Type header in the HTTP response to "text/xml".
11779  * @constructor
11780  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11781  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11782  * will be used to make the request.
11783  */
11784 Roo.data.HttpProxy = function(conn){
11785     Roo.data.HttpProxy.superclass.constructor.call(this);
11786     // is conn a conn config or a real conn?
11787     this.conn = conn;
11788     this.useAjax = !conn || !conn.events;
11789   
11790 };
11791
11792 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11793     // thse are take from connection...
11794     
11795     /**
11796      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11797      */
11798     /**
11799      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11800      * extra parameters to each request made by this object. (defaults to undefined)
11801      */
11802     /**
11803      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11804      *  to each request made by this object. (defaults to undefined)
11805      */
11806     /**
11807      * @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)
11808      */
11809     /**
11810      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11811      */
11812      /**
11813      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11814      * @type Boolean
11815      */
11816   
11817
11818     /**
11819      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11820      * @type Boolean
11821      */
11822     /**
11823      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11824      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11825      * a finer-grained basis than the DataProxy events.
11826      */
11827     getConnection : function(){
11828         return this.useAjax ? Roo.Ajax : this.conn;
11829     },
11830
11831     /**
11832      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11833      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11834      * process that block using the passed callback.
11835      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11836      * for the request to the remote server.
11837      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11838      * object into a block of Roo.data.Records.
11839      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11840      * The function must be passed <ul>
11841      * <li>The Record block object</li>
11842      * <li>The "arg" argument from the load function</li>
11843      * <li>A boolean success indicator</li>
11844      * </ul>
11845      * @param {Object} scope The scope in which to call the callback
11846      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11847      */
11848     load : function(params, reader, callback, scope, arg){
11849         if(this.fireEvent("beforeload", this, params) !== false){
11850             var  o = {
11851                 params : params || {},
11852                 request: {
11853                     callback : callback,
11854                     scope : scope,
11855                     arg : arg
11856                 },
11857                 reader: reader,
11858                 callback : this.loadResponse,
11859                 scope: this
11860             };
11861             if(this.useAjax){
11862                 Roo.applyIf(o, this.conn);
11863                 if(this.activeRequest){
11864                     Roo.Ajax.abort(this.activeRequest);
11865                 }
11866                 this.activeRequest = Roo.Ajax.request(o);
11867             }else{
11868                 this.conn.request(o);
11869             }
11870         }else{
11871             callback.call(scope||this, null, arg, false);
11872         }
11873     },
11874
11875     // private
11876     loadResponse : function(o, success, response){
11877         delete this.activeRequest;
11878         if(!success){
11879             this.fireEvent("loadexception", this, o, response);
11880             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11881             return;
11882         }
11883         var result;
11884         try {
11885             result = o.reader.read(response);
11886         }catch(e){
11887             this.fireEvent("loadexception", this, o, response, e);
11888             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11889             return;
11890         }
11891         
11892         this.fireEvent("load", this, o, o.request.arg);
11893         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11894     },
11895
11896     // private
11897     update : function(dataSet){
11898
11899     },
11900
11901     // private
11902     updateResponse : function(dataSet){
11903
11904     }
11905 });/*
11906  * Based on:
11907  * Ext JS Library 1.1.1
11908  * Copyright(c) 2006-2007, Ext JS, LLC.
11909  *
11910  * Originally Released Under LGPL - original licence link has changed is not relivant.
11911  *
11912  * Fork - LGPL
11913  * <script type="text/javascript">
11914  */
11915
11916 /**
11917  * @class Roo.data.ScriptTagProxy
11918  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11919  * other than the originating domain of the running page.<br><br>
11920  * <p>
11921  * <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
11922  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11923  * <p>
11924  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11925  * source code that is used as the source inside a &lt;script> tag.<br><br>
11926  * <p>
11927  * In order for the browser to process the returned data, the server must wrap the data object
11928  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11929  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11930  * depending on whether the callback name was passed:
11931  * <p>
11932  * <pre><code>
11933 boolean scriptTag = false;
11934 String cb = request.getParameter("callback");
11935 if (cb != null) {
11936     scriptTag = true;
11937     response.setContentType("text/javascript");
11938 } else {
11939     response.setContentType("application/x-json");
11940 }
11941 Writer out = response.getWriter();
11942 if (scriptTag) {
11943     out.write(cb + "(");
11944 }
11945 out.print(dataBlock.toJsonString());
11946 if (scriptTag) {
11947     out.write(");");
11948 }
11949 </pre></code>
11950  *
11951  * @constructor
11952  * @param {Object} config A configuration object.
11953  */
11954 Roo.data.ScriptTagProxy = function(config){
11955     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11956     Roo.apply(this, config);
11957     this.head = document.getElementsByTagName("head")[0];
11958 };
11959
11960 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11961
11962 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11963     /**
11964      * @cfg {String} url The URL from which to request the data object.
11965      */
11966     /**
11967      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11968      */
11969     timeout : 30000,
11970     /**
11971      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11972      * the server the name of the callback function set up by the load call to process the returned data object.
11973      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11974      * javascript output which calls this named function passing the data object as its only parameter.
11975      */
11976     callbackParam : "callback",
11977     /**
11978      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11979      * name to the request.
11980      */
11981     nocache : true,
11982
11983     /**
11984      * Load data from the configured URL, read the data object into
11985      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11986      * process that block using the passed callback.
11987      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11988      * for the request to the remote server.
11989      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11990      * object into a block of Roo.data.Records.
11991      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11992      * The function must be passed <ul>
11993      * <li>The Record block object</li>
11994      * <li>The "arg" argument from the load function</li>
11995      * <li>A boolean success indicator</li>
11996      * </ul>
11997      * @param {Object} scope The scope in which to call the callback
11998      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11999      */
12000     load : function(params, reader, callback, scope, arg){
12001         if(this.fireEvent("beforeload", this, params) !== false){
12002
12003             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12004
12005             var url = this.url;
12006             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12007             if(this.nocache){
12008                 url += "&_dc=" + (new Date().getTime());
12009             }
12010             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12011             var trans = {
12012                 id : transId,
12013                 cb : "stcCallback"+transId,
12014                 scriptId : "stcScript"+transId,
12015                 params : params,
12016                 arg : arg,
12017                 url : url,
12018                 callback : callback,
12019                 scope : scope,
12020                 reader : reader
12021             };
12022             var conn = this;
12023
12024             window[trans.cb] = function(o){
12025                 conn.handleResponse(o, trans);
12026             };
12027
12028             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12029
12030             if(this.autoAbort !== false){
12031                 this.abort();
12032             }
12033
12034             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12035
12036             var script = document.createElement("script");
12037             script.setAttribute("src", url);
12038             script.setAttribute("type", "text/javascript");
12039             script.setAttribute("id", trans.scriptId);
12040             this.head.appendChild(script);
12041
12042             this.trans = trans;
12043         }else{
12044             callback.call(scope||this, null, arg, false);
12045         }
12046     },
12047
12048     // private
12049     isLoading : function(){
12050         return this.trans ? true : false;
12051     },
12052
12053     /**
12054      * Abort the current server request.
12055      */
12056     abort : function(){
12057         if(this.isLoading()){
12058             this.destroyTrans(this.trans);
12059         }
12060     },
12061
12062     // private
12063     destroyTrans : function(trans, isLoaded){
12064         this.head.removeChild(document.getElementById(trans.scriptId));
12065         clearTimeout(trans.timeoutId);
12066         if(isLoaded){
12067             window[trans.cb] = undefined;
12068             try{
12069                 delete window[trans.cb];
12070             }catch(e){}
12071         }else{
12072             // if hasn't been loaded, wait for load to remove it to prevent script error
12073             window[trans.cb] = function(){
12074                 window[trans.cb] = undefined;
12075                 try{
12076                     delete window[trans.cb];
12077                 }catch(e){}
12078             };
12079         }
12080     },
12081
12082     // private
12083     handleResponse : function(o, trans){
12084         this.trans = false;
12085         this.destroyTrans(trans, true);
12086         var result;
12087         try {
12088             result = trans.reader.readRecords(o);
12089         }catch(e){
12090             this.fireEvent("loadexception", this, o, trans.arg, e);
12091             trans.callback.call(trans.scope||window, null, trans.arg, false);
12092             return;
12093         }
12094         this.fireEvent("load", this, o, trans.arg);
12095         trans.callback.call(trans.scope||window, result, trans.arg, true);
12096     },
12097
12098     // private
12099     handleFailure : function(trans){
12100         this.trans = false;
12101         this.destroyTrans(trans, false);
12102         this.fireEvent("loadexception", this, null, trans.arg);
12103         trans.callback.call(trans.scope||window, null, trans.arg, false);
12104     }
12105 });/*
12106  * Based on:
12107  * Ext JS Library 1.1.1
12108  * Copyright(c) 2006-2007, Ext JS, LLC.
12109  *
12110  * Originally Released Under LGPL - original licence link has changed is not relivant.
12111  *
12112  * Fork - LGPL
12113  * <script type="text/javascript">
12114  */
12115
12116 /**
12117  * @class Roo.data.JsonReader
12118  * @extends Roo.data.DataReader
12119  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12120  * based on mappings in a provided Roo.data.Record constructor.
12121  * 
12122  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12123  * in the reply previously. 
12124  * 
12125  * <p>
12126  * Example code:
12127  * <pre><code>
12128 var RecordDef = Roo.data.Record.create([
12129     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12130     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12131 ]);
12132 var myReader = new Roo.data.JsonReader({
12133     totalProperty: "results",    // The property which contains the total dataset size (optional)
12134     root: "rows",                // The property which contains an Array of row objects
12135     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12136 }, RecordDef);
12137 </code></pre>
12138  * <p>
12139  * This would consume a JSON file like this:
12140  * <pre><code>
12141 { 'results': 2, 'rows': [
12142     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12143     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12144 }
12145 </code></pre>
12146  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12147  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12148  * paged from the remote server.
12149  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12150  * @cfg {String} root name of the property which contains the Array of row objects.
12151  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12152  * @cfg {Array} fields Array of field definition objects
12153  * @constructor
12154  * Create a new JsonReader
12155  * @param {Object} meta Metadata configuration options
12156  * @param {Object} recordType Either an Array of field definition objects,
12157  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12158  */
12159 Roo.data.JsonReader = function(meta, recordType){
12160     
12161     meta = meta || {};
12162     // set some defaults:
12163     Roo.applyIf(meta, {
12164         totalProperty: 'total',
12165         successProperty : 'success',
12166         root : 'data',
12167         id : 'id'
12168     });
12169     
12170     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12171 };
12172 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12173     
12174     /**
12175      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12176      * Used by Store query builder to append _requestMeta to params.
12177      * 
12178      */
12179     metaFromRemote : false,
12180     /**
12181      * This method is only used by a DataProxy which has retrieved data from a remote server.
12182      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12183      * @return {Object} data A data block which is used by an Roo.data.Store object as
12184      * a cache of Roo.data.Records.
12185      */
12186     read : function(response){
12187         var json = response.responseText;
12188        
12189         var o = /* eval:var:o */ eval("("+json+")");
12190         if(!o) {
12191             throw {message: "JsonReader.read: Json object not found"};
12192         }
12193         
12194         if(o.metaData){
12195             
12196             delete this.ef;
12197             this.metaFromRemote = true;
12198             this.meta = o.metaData;
12199             this.recordType = Roo.data.Record.create(o.metaData.fields);
12200             this.onMetaChange(this.meta, this.recordType, o);
12201         }
12202         return this.readRecords(o);
12203     },
12204
12205     // private function a store will implement
12206     onMetaChange : function(meta, recordType, o){
12207
12208     },
12209
12210     /**
12211          * @ignore
12212          */
12213     simpleAccess: function(obj, subsc) {
12214         return obj[subsc];
12215     },
12216
12217         /**
12218          * @ignore
12219          */
12220     getJsonAccessor: function(){
12221         var re = /[\[\.]/;
12222         return function(expr) {
12223             try {
12224                 return(re.test(expr))
12225                     ? new Function("obj", "return obj." + expr)
12226                     : function(obj){
12227                         return obj[expr];
12228                     };
12229             } catch(e){}
12230             return Roo.emptyFn;
12231         };
12232     }(),
12233
12234     /**
12235      * Create a data block containing Roo.data.Records from an XML document.
12236      * @param {Object} o An object which contains an Array of row objects in the property specified
12237      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12238      * which contains the total size of the dataset.
12239      * @return {Object} data A data block which is used by an Roo.data.Store object as
12240      * a cache of Roo.data.Records.
12241      */
12242     readRecords : function(o){
12243         /**
12244          * After any data loads, the raw JSON data is available for further custom processing.
12245          * @type Object
12246          */
12247         this.o = o;
12248         var s = this.meta, Record = this.recordType,
12249             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12250
12251 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12252         if (!this.ef) {
12253             if(s.totalProperty) {
12254                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12255                 }
12256                 if(s.successProperty) {
12257                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12258                 }
12259                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12260                 if (s.id) {
12261                         var g = this.getJsonAccessor(s.id);
12262                         this.getId = function(rec) {
12263                                 var r = g(rec);  
12264                                 return (r === undefined || r === "") ? null : r;
12265                         };
12266                 } else {
12267                         this.getId = function(){return null;};
12268                 }
12269             this.ef = [];
12270             for(var jj = 0; jj < fl; jj++){
12271                 f = fi[jj];
12272                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12273                 this.ef[jj] = this.getJsonAccessor(map);
12274             }
12275         }
12276
12277         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12278         if(s.totalProperty){
12279             var vt = parseInt(this.getTotal(o), 10);
12280             if(!isNaN(vt)){
12281                 totalRecords = vt;
12282             }
12283         }
12284         if(s.successProperty){
12285             var vs = this.getSuccess(o);
12286             if(vs === false || vs === 'false'){
12287                 success = false;
12288             }
12289         }
12290         var records = [];
12291         for(var i = 0; i < c; i++){
12292                 var n = root[i];
12293             var values = {};
12294             var id = this.getId(n);
12295             for(var j = 0; j < fl; j++){
12296                 f = fi[j];
12297             var v = this.ef[j](n);
12298             if (!f.convert) {
12299                 Roo.log('missing convert for ' + f.name);
12300                 Roo.log(f);
12301                 continue;
12302             }
12303             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12304             }
12305             var record = new Record(values, id);
12306             record.json = n;
12307             records[i] = record;
12308         }
12309         return {
12310             raw : o,
12311             success : success,
12312             records : records,
12313             totalRecords : totalRecords
12314         };
12315     }
12316 });/*
12317  * Based on:
12318  * Ext JS Library 1.1.1
12319  * Copyright(c) 2006-2007, Ext JS, LLC.
12320  *
12321  * Originally Released Under LGPL - original licence link has changed is not relivant.
12322  *
12323  * Fork - LGPL
12324  * <script type="text/javascript">
12325  */
12326
12327 /**
12328  * @class Roo.data.ArrayReader
12329  * @extends Roo.data.DataReader
12330  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12331  * Each element of that Array represents a row of data fields. The
12332  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12333  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12334  * <p>
12335  * Example code:.
12336  * <pre><code>
12337 var RecordDef = Roo.data.Record.create([
12338     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12339     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12340 ]);
12341 var myReader = new Roo.data.ArrayReader({
12342     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12343 }, RecordDef);
12344 </code></pre>
12345  * <p>
12346  * This would consume an Array like this:
12347  * <pre><code>
12348 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12349   </code></pre>
12350  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12351  * @constructor
12352  * Create a new JsonReader
12353  * @param {Object} meta Metadata configuration options.
12354  * @param {Object} recordType Either an Array of field definition objects
12355  * as specified to {@link Roo.data.Record#create},
12356  * or an {@link Roo.data.Record} object
12357  * created using {@link Roo.data.Record#create}.
12358  */
12359 Roo.data.ArrayReader = function(meta, recordType){
12360     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12361 };
12362
12363 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12364     /**
12365      * Create a data block containing Roo.data.Records from an XML document.
12366      * @param {Object} o An Array of row objects which represents the dataset.
12367      * @return {Object} data A data block which is used by an Roo.data.Store object as
12368      * a cache of Roo.data.Records.
12369      */
12370     readRecords : function(o){
12371         var sid = this.meta ? this.meta.id : null;
12372         var recordType = this.recordType, fields = recordType.prototype.fields;
12373         var records = [];
12374         var root = o;
12375             for(var i = 0; i < root.length; i++){
12376                     var n = root[i];
12377                 var values = {};
12378                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12380                 var f = fields.items[j];
12381                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12382                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12383                 v = f.convert(v);
12384                 values[f.name] = v;
12385             }
12386                 var record = new recordType(values, id);
12387                 record.json = n;
12388                 records[records.length] = record;
12389             }
12390             return {
12391                 records : records,
12392                 totalRecords : records.length
12393             };
12394     }
12395 });/*
12396  * - LGPL
12397  * * 
12398  */
12399
12400 /**
12401  * @class Roo.bootstrap.ComboBox
12402  * @extends Roo.bootstrap.TriggerField
12403  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12404  * @cfg {Boolean} append (true|false) default false
12405  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12406  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12407  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12408  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12409  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12410  * @cfg {Boolean} animate default true
12411  * @cfg {Boolean} emptyResultText only for touch device
12412  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12413  * @cfg {String} emptyTitle default ''
12414  * @constructor
12415  * Create a new ComboBox.
12416  * @param {Object} config Configuration options
12417  */
12418 Roo.bootstrap.ComboBox = function(config){
12419     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12420     this.addEvents({
12421         /**
12422          * @event expand
12423          * Fires when the dropdown list is expanded
12424         * @param {Roo.bootstrap.ComboBox} combo This combo box
12425         */
12426         'expand' : true,
12427         /**
12428          * @event collapse
12429          * Fires when the dropdown list is collapsed
12430         * @param {Roo.bootstrap.ComboBox} combo This combo box
12431         */
12432         'collapse' : true,
12433         /**
12434          * @event beforeselect
12435          * Fires before a list item is selected. Return false to cancel the selection.
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         * @param {Roo.data.Record} record The data record returned from the underlying store
12438         * @param {Number} index The index of the selected item in the dropdown list
12439         */
12440         'beforeselect' : true,
12441         /**
12442          * @event select
12443          * Fires when a list item is selected
12444         * @param {Roo.bootstrap.ComboBox} combo This combo box
12445         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12446         * @param {Number} index The index of the selected item in the dropdown list
12447         */
12448         'select' : true,
12449         /**
12450          * @event beforequery
12451          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12452          * The event object passed has these properties:
12453         * @param {Roo.bootstrap.ComboBox} combo This combo box
12454         * @param {String} query The query
12455         * @param {Boolean} forceAll true to force "all" query
12456         * @param {Boolean} cancel true to cancel the query
12457         * @param {Object} e The query event object
12458         */
12459         'beforequery': true,
12460          /**
12461          * @event add
12462          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12463         * @param {Roo.bootstrap.ComboBox} combo This combo box
12464         */
12465         'add' : true,
12466         /**
12467          * @event edit
12468          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12469         * @param {Roo.bootstrap.ComboBox} combo This combo box
12470         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12471         */
12472         'edit' : true,
12473         /**
12474          * @event remove
12475          * Fires when the remove value from the combobox array
12476         * @param {Roo.bootstrap.ComboBox} combo This combo box
12477         */
12478         'remove' : true,
12479         /**
12480          * @event afterremove
12481          * Fires when the remove value from the combobox array
12482         * @param {Roo.bootstrap.ComboBox} combo This combo box
12483         */
12484         'afterremove' : true,
12485         /**
12486          * @event specialfilter
12487          * Fires when specialfilter
12488             * @param {Roo.bootstrap.ComboBox} combo This combo box
12489             */
12490         'specialfilter' : true,
12491         /**
12492          * @event tick
12493          * Fires when tick the element
12494             * @param {Roo.bootstrap.ComboBox} combo This combo box
12495             */
12496         'tick' : true,
12497         /**
12498          * @event touchviewdisplay
12499          * Fires when touch view require special display (default is using displayField)
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             * @param {Object} cfg set html .
12502             */
12503         'touchviewdisplay' : true
12504         
12505     });
12506     
12507     this.item = [];
12508     this.tickItems = [];
12509     
12510     this.selectedIndex = -1;
12511     if(this.mode == 'local'){
12512         if(config.queryDelay === undefined){
12513             this.queryDelay = 10;
12514         }
12515         if(config.minChars === undefined){
12516             this.minChars = 0;
12517         }
12518     }
12519 };
12520
12521 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12522      
12523     /**
12524      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12525      * rendering into an Roo.Editor, defaults to false)
12526      */
12527     /**
12528      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12529      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12530      */
12531     /**
12532      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12533      */
12534     /**
12535      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12536      * the dropdown list (defaults to undefined, with no header element)
12537      */
12538
12539      /**
12540      * @cfg {String/Roo.Template} tpl The template to use to render the output
12541      */
12542      
12543      /**
12544      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12545      */
12546     listWidth: undefined,
12547     /**
12548      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12549      * mode = 'remote' or 'text' if mode = 'local')
12550      */
12551     displayField: undefined,
12552     
12553     /**
12554      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12555      * mode = 'remote' or 'value' if mode = 'local'). 
12556      * Note: use of a valueField requires the user make a selection
12557      * in order for a value to be mapped.
12558      */
12559     valueField: undefined,
12560     /**
12561      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12562      */
12563     modalTitle : '',
12564     
12565     /**
12566      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12567      * field's data value (defaults to the underlying DOM element's name)
12568      */
12569     hiddenName: undefined,
12570     /**
12571      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12572      */
12573     listClass: '',
12574     /**
12575      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12576      */
12577     selectedClass: 'active',
12578     
12579     /**
12580      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12581      */
12582     shadow:'sides',
12583     /**
12584      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12585      * anchor positions (defaults to 'tl-bl')
12586      */
12587     listAlign: 'tl-bl?',
12588     /**
12589      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12590      */
12591     maxHeight: 300,
12592     /**
12593      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12594      * query specified by the allQuery config option (defaults to 'query')
12595      */
12596     triggerAction: 'query',
12597     /**
12598      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12599      * (defaults to 4, does not apply if editable = false)
12600      */
12601     minChars : 4,
12602     /**
12603      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12604      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12605      */
12606     typeAhead: false,
12607     /**
12608      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12609      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12610      */
12611     queryDelay: 500,
12612     /**
12613      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12614      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12615      */
12616     pageSize: 0,
12617     /**
12618      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12619      * when editable = true (defaults to false)
12620      */
12621     selectOnFocus:false,
12622     /**
12623      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12624      */
12625     queryParam: 'query',
12626     /**
12627      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12628      * when mode = 'remote' (defaults to 'Loading...')
12629      */
12630     loadingText: 'Loading...',
12631     /**
12632      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12633      */
12634     resizable: false,
12635     /**
12636      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12637      */
12638     handleHeight : 8,
12639     /**
12640      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12641      * traditional select (defaults to true)
12642      */
12643     editable: true,
12644     /**
12645      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12646      */
12647     allQuery: '',
12648     /**
12649      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12650      */
12651     mode: 'remote',
12652     /**
12653      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12654      * listWidth has a higher value)
12655      */
12656     minListWidth : 70,
12657     /**
12658      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12659      * allow the user to set arbitrary text into the field (defaults to false)
12660      */
12661     forceSelection:false,
12662     /**
12663      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12664      * if typeAhead = true (defaults to 250)
12665      */
12666     typeAheadDelay : 250,
12667     /**
12668      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12669      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12670      */
12671     valueNotFoundText : undefined,
12672     /**
12673      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12674      */
12675     blockFocus : false,
12676     
12677     /**
12678      * @cfg {Boolean} disableClear Disable showing of clear button.
12679      */
12680     disableClear : false,
12681     /**
12682      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12683      */
12684     alwaysQuery : false,
12685     
12686     /**
12687      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12688      */
12689     multiple : false,
12690     
12691     /**
12692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12693      */
12694     invalidClass : "has-warning",
12695     
12696     /**
12697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12698      */
12699     validClass : "has-success",
12700     
12701     /**
12702      * @cfg {Boolean} specialFilter (true|false) special filter default false
12703      */
12704     specialFilter : false,
12705     
12706     /**
12707      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12708      */
12709     mobileTouchView : true,
12710     
12711     /**
12712      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12713      */
12714     useNativeIOS : false,
12715     
12716     ios_options : false,
12717     
12718     //private
12719     addicon : false,
12720     editicon: false,
12721     
12722     page: 0,
12723     hasQuery: false,
12724     append: false,
12725     loadNext: false,
12726     autoFocus : true,
12727     tickable : false,
12728     btnPosition : 'right',
12729     triggerList : true,
12730     showToggleBtn : true,
12731     animate : true,
12732     emptyResultText: 'Empty',
12733     triggerText : 'Select',
12734     emptyTitle : '',
12735     
12736     // element that contains real text value.. (when hidden is used..)
12737     
12738     getAutoCreate : function()
12739     {   
12740         var cfg = false;
12741         //render
12742         /*
12743          * Render classic select for iso
12744          */
12745         
12746         if(Roo.isIOS && this.useNativeIOS){
12747             cfg = this.getAutoCreateNativeIOS();
12748             return cfg;
12749         }
12750         
12751         /*
12752          * Touch Devices
12753          */
12754         
12755         if(Roo.isTouch && this.mobileTouchView){
12756             cfg = this.getAutoCreateTouchView();
12757             return cfg;;
12758         }
12759         
12760         /*
12761          *  Normal ComboBox
12762          */
12763         if(!this.tickable){
12764             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12765             return cfg;
12766         }
12767         
12768         /*
12769          *  ComboBox with tickable selections
12770          */
12771              
12772         var align = this.labelAlign || this.parentLabelAlign();
12773         
12774         cfg = {
12775             cls : 'form-group roo-combobox-tickable' //input-group
12776         };
12777         
12778         var btn_text_select = '';
12779         var btn_text_done = '';
12780         var btn_text_cancel = '';
12781         
12782         if (this.btn_text_show) {
12783             btn_text_select = 'Select';
12784             btn_text_done = 'Done';
12785             btn_text_cancel = 'Cancel'; 
12786         }
12787         
12788         var buttons = {
12789             tag : 'div',
12790             cls : 'tickable-buttons',
12791             cn : [
12792                 {
12793                     tag : 'button',
12794                     type : 'button',
12795                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12796                     //html : this.triggerText
12797                     html: btn_text_select
12798                 },
12799                 {
12800                     tag : 'button',
12801                     type : 'button',
12802                     name : 'ok',
12803                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12804                     //html : 'Done'
12805                     html: btn_text_done
12806                 },
12807                 {
12808                     tag : 'button',
12809                     type : 'button',
12810                     name : 'cancel',
12811                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12812                     //html : 'Cancel'
12813                     html: btn_text_cancel
12814                 }
12815             ]
12816         };
12817         
12818         if(this.editable){
12819             buttons.cn.unshift({
12820                 tag: 'input',
12821                 cls: 'roo-select2-search-field-input'
12822             });
12823         }
12824         
12825         var _this = this;
12826         
12827         Roo.each(buttons.cn, function(c){
12828             if (_this.size) {
12829                 c.cls += ' btn-' + _this.size;
12830             }
12831
12832             if (_this.disabled) {
12833                 c.disabled = true;
12834             }
12835         });
12836         
12837         var box = {
12838             tag: 'div',
12839             cn: [
12840                 {
12841                     tag: 'input',
12842                     type : 'hidden',
12843                     cls: 'form-hidden-field'
12844                 },
12845                 {
12846                     tag: 'ul',
12847                     cls: 'roo-select2-choices',
12848                     cn:[
12849                         {
12850                             tag: 'li',
12851                             cls: 'roo-select2-search-field',
12852                             cn: [
12853                                 buttons
12854                             ]
12855                         }
12856                     ]
12857                 }
12858             ]
12859         };
12860         
12861         var combobox = {
12862             cls: 'roo-select2-container input-group roo-select2-container-multi',
12863             cn: [
12864                 box
12865 //                {
12866 //                    tag: 'ul',
12867 //                    cls: 'typeahead typeahead-long dropdown-menu',
12868 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12869 //                }
12870             ]
12871         };
12872         
12873         if(this.hasFeedback && !this.allowBlank){
12874             
12875             var feedback = {
12876                 tag: 'span',
12877                 cls: 'glyphicon form-control-feedback'
12878             };
12879
12880             combobox.cn.push(feedback);
12881         }
12882         
12883         
12884         if (align ==='left' && this.fieldLabel.length) {
12885             
12886             cfg.cls += ' roo-form-group-label-left';
12887             
12888             cfg.cn = [
12889                 {
12890                     tag : 'i',
12891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12892                     tooltip : 'This field is required'
12893                 },
12894                 {
12895                     tag: 'label',
12896                     'for' :  id,
12897                     cls : 'control-label',
12898                     html : this.fieldLabel
12899
12900                 },
12901                 {
12902                     cls : "", 
12903                     cn: [
12904                         combobox
12905                     ]
12906                 }
12907
12908             ];
12909             
12910             var labelCfg = cfg.cn[1];
12911             var contentCfg = cfg.cn[2];
12912             
12913
12914             if(this.indicatorpos == 'right'){
12915                 
12916                 cfg.cn = [
12917                     {
12918                         tag: 'label',
12919                         'for' :  id,
12920                         cls : 'control-label',
12921                         cn : [
12922                             {
12923                                 tag : 'span',
12924                                 html : this.fieldLabel
12925                             },
12926                             {
12927                                 tag : 'i',
12928                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12929                                 tooltip : 'This field is required'
12930                             }
12931                         ]
12932                     },
12933                     {
12934                         cls : "",
12935                         cn: [
12936                             combobox
12937                         ]
12938                     }
12939
12940                 ];
12941                 
12942                 
12943                 
12944                 labelCfg = cfg.cn[0];
12945                 contentCfg = cfg.cn[1];
12946             
12947             }
12948             
12949             if(this.labelWidth > 12){
12950                 labelCfg.style = "width: " + this.labelWidth + 'px';
12951             }
12952             
12953             if(this.labelWidth < 13 && this.labelmd == 0){
12954                 this.labelmd = this.labelWidth;
12955             }
12956             
12957             if(this.labellg > 0){
12958                 labelCfg.cls += ' col-lg-' + this.labellg;
12959                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12960             }
12961             
12962             if(this.labelmd > 0){
12963                 labelCfg.cls += ' col-md-' + this.labelmd;
12964                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12965             }
12966             
12967             if(this.labelsm > 0){
12968                 labelCfg.cls += ' col-sm-' + this.labelsm;
12969                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12970             }
12971             
12972             if(this.labelxs > 0){
12973                 labelCfg.cls += ' col-xs-' + this.labelxs;
12974                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12975             }
12976                 
12977                 
12978         } else if ( this.fieldLabel.length) {
12979 //                Roo.log(" label");
12980                  cfg.cn = [
12981                     {
12982                         tag : 'i',
12983                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12984                         tooltip : 'This field is required'
12985                     },
12986                     {
12987                         tag: 'label',
12988                         //cls : 'input-group-addon',
12989                         html : this.fieldLabel
12990                     },
12991                     combobox
12992                 ];
12993                 
12994                 if(this.indicatorpos == 'right'){
12995                     cfg.cn = [
12996                         {
12997                             tag: 'label',
12998                             //cls : 'input-group-addon',
12999                             html : this.fieldLabel
13000                         },
13001                         {
13002                             tag : 'i',
13003                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13004                             tooltip : 'This field is required'
13005                         },
13006                         combobox
13007                     ];
13008                     
13009                 }
13010
13011         } else {
13012             
13013 //                Roo.log(" no label && no align");
13014                 cfg = combobox
13015                      
13016                 
13017         }
13018          
13019         var settings=this;
13020         ['xs','sm','md','lg'].map(function(size){
13021             if (settings[size]) {
13022                 cfg.cls += ' col-' + size + '-' + settings[size];
13023             }
13024         });
13025         
13026         return cfg;
13027         
13028     },
13029     
13030     _initEventsCalled : false,
13031     
13032     // private
13033     initEvents: function()
13034     {   
13035         if (this._initEventsCalled) { // as we call render... prevent looping...
13036             return;
13037         }
13038         this._initEventsCalled = true;
13039         
13040         if (!this.store) {
13041             throw "can not find store for combo";
13042         }
13043         
13044         this.indicator = this.indicatorEl();
13045         
13046         this.store = Roo.factory(this.store, Roo.data);
13047         this.store.parent = this;
13048         
13049         // if we are building from html. then this element is so complex, that we can not really
13050         // use the rendered HTML.
13051         // so we have to trash and replace the previous code.
13052         if (Roo.XComponent.build_from_html) {
13053             // remove this element....
13054             var e = this.el.dom, k=0;
13055             while (e ) { e = e.previousSibling;  ++k;}
13056
13057             this.el.remove();
13058             
13059             this.el=false;
13060             this.rendered = false;
13061             
13062             this.render(this.parent().getChildContainer(true), k);
13063         }
13064         
13065         if(Roo.isIOS && this.useNativeIOS){
13066             this.initIOSView();
13067             return;
13068         }
13069         
13070         /*
13071          * Touch Devices
13072          */
13073         
13074         if(Roo.isTouch && this.mobileTouchView){
13075             this.initTouchView();
13076             return;
13077         }
13078         
13079         if(this.tickable){
13080             this.initTickableEvents();
13081             return;
13082         }
13083         
13084         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13085         
13086         if(this.hiddenName){
13087             
13088             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13089             
13090             this.hiddenField.dom.value =
13091                 this.hiddenValue !== undefined ? this.hiddenValue :
13092                 this.value !== undefined ? this.value : '';
13093
13094             // prevent input submission
13095             this.el.dom.removeAttribute('name');
13096             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13097              
13098              
13099         }
13100         //if(Roo.isGecko){
13101         //    this.el.dom.setAttribute('autocomplete', 'off');
13102         //}
13103         
13104         var cls = 'x-combo-list';
13105         
13106         //this.list = new Roo.Layer({
13107         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13108         //});
13109         
13110         var _this = this;
13111         
13112         (function(){
13113             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13114             _this.list.setWidth(lw);
13115         }).defer(100);
13116         
13117         this.list.on('mouseover', this.onViewOver, this);
13118         this.list.on('mousemove', this.onViewMove, this);
13119         this.list.on('scroll', this.onViewScroll, this);
13120         
13121         /*
13122         this.list.swallowEvent('mousewheel');
13123         this.assetHeight = 0;
13124
13125         if(this.title){
13126             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13127             this.assetHeight += this.header.getHeight();
13128         }
13129
13130         this.innerList = this.list.createChild({cls:cls+'-inner'});
13131         this.innerList.on('mouseover', this.onViewOver, this);
13132         this.innerList.on('mousemove', this.onViewMove, this);
13133         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13134         
13135         if(this.allowBlank && !this.pageSize && !this.disableClear){
13136             this.footer = this.list.createChild({cls:cls+'-ft'});
13137             this.pageTb = new Roo.Toolbar(this.footer);
13138            
13139         }
13140         if(this.pageSize){
13141             this.footer = this.list.createChild({cls:cls+'-ft'});
13142             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13143                     {pageSize: this.pageSize});
13144             
13145         }
13146         
13147         if (this.pageTb && this.allowBlank && !this.disableClear) {
13148             var _this = this;
13149             this.pageTb.add(new Roo.Toolbar.Fill(), {
13150                 cls: 'x-btn-icon x-btn-clear',
13151                 text: '&#160;',
13152                 handler: function()
13153                 {
13154                     _this.collapse();
13155                     _this.clearValue();
13156                     _this.onSelect(false, -1);
13157                 }
13158             });
13159         }
13160         if (this.footer) {
13161             this.assetHeight += this.footer.getHeight();
13162         }
13163         */
13164             
13165         if(!this.tpl){
13166             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13167         }
13168
13169         this.view = new Roo.View(this.list, this.tpl, {
13170             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13171         });
13172         //this.view.wrapEl.setDisplayed(false);
13173         this.view.on('click', this.onViewClick, this);
13174         
13175         
13176         this.store.on('beforeload', this.onBeforeLoad, this);
13177         this.store.on('load', this.onLoad, this);
13178         this.store.on('loadexception', this.onLoadException, this);
13179         /*
13180         if(this.resizable){
13181             this.resizer = new Roo.Resizable(this.list,  {
13182                pinned:true, handles:'se'
13183             });
13184             this.resizer.on('resize', function(r, w, h){
13185                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13186                 this.listWidth = w;
13187                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13188                 this.restrictHeight();
13189             }, this);
13190             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13191         }
13192         */
13193         if(!this.editable){
13194             this.editable = true;
13195             this.setEditable(false);
13196         }
13197         
13198         /*
13199         
13200         if (typeof(this.events.add.listeners) != 'undefined') {
13201             
13202             this.addicon = this.wrap.createChild(
13203                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13204        
13205             this.addicon.on('click', function(e) {
13206                 this.fireEvent('add', this);
13207             }, this);
13208         }
13209         if (typeof(this.events.edit.listeners) != 'undefined') {
13210             
13211             this.editicon = this.wrap.createChild(
13212                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13213             if (this.addicon) {
13214                 this.editicon.setStyle('margin-left', '40px');
13215             }
13216             this.editicon.on('click', function(e) {
13217                 
13218                 // we fire even  if inothing is selected..
13219                 this.fireEvent('edit', this, this.lastData );
13220                 
13221             }, this);
13222         }
13223         */
13224         
13225         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13226             "up" : function(e){
13227                 this.inKeyMode = true;
13228                 this.selectPrev();
13229             },
13230
13231             "down" : function(e){
13232                 if(!this.isExpanded()){
13233                     this.onTriggerClick();
13234                 }else{
13235                     this.inKeyMode = true;
13236                     this.selectNext();
13237                 }
13238             },
13239
13240             "enter" : function(e){
13241 //                this.onViewClick();
13242                 //return true;
13243                 this.collapse();
13244                 
13245                 if(this.fireEvent("specialkey", this, e)){
13246                     this.onViewClick(false);
13247                 }
13248                 
13249                 return true;
13250             },
13251
13252             "esc" : function(e){
13253                 this.collapse();
13254             },
13255
13256             "tab" : function(e){
13257                 this.collapse();
13258                 
13259                 if(this.fireEvent("specialkey", this, e)){
13260                     this.onViewClick(false);
13261                 }
13262                 
13263                 return true;
13264             },
13265
13266             scope : this,
13267
13268             doRelay : function(foo, bar, hname){
13269                 if(hname == 'down' || this.scope.isExpanded()){
13270                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13271                 }
13272                 return true;
13273             },
13274
13275             forceKeyDown: true
13276         });
13277         
13278         
13279         this.queryDelay = Math.max(this.queryDelay || 10,
13280                 this.mode == 'local' ? 10 : 250);
13281         
13282         
13283         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13284         
13285         if(this.typeAhead){
13286             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13287         }
13288         if(this.editable !== false){
13289             this.inputEl().on("keyup", this.onKeyUp, this);
13290         }
13291         if(this.forceSelection){
13292             this.inputEl().on('blur', this.doForce, this);
13293         }
13294         
13295         if(this.multiple){
13296             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13297             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13298         }
13299     },
13300     
13301     initTickableEvents: function()
13302     {   
13303         this.createList();
13304         
13305         if(this.hiddenName){
13306             
13307             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13308             
13309             this.hiddenField.dom.value =
13310                 this.hiddenValue !== undefined ? this.hiddenValue :
13311                 this.value !== undefined ? this.value : '';
13312
13313             // prevent input submission
13314             this.el.dom.removeAttribute('name');
13315             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13316              
13317              
13318         }
13319         
13320 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13321         
13322         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13323         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13324         if(this.triggerList){
13325             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13326         }
13327          
13328         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13329         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13330         
13331         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13332         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13333         
13334         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13335         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13336         
13337         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13338         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13339         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13340         
13341         this.okBtn.hide();
13342         this.cancelBtn.hide();
13343         
13344         var _this = this;
13345         
13346         (function(){
13347             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13348             _this.list.setWidth(lw);
13349         }).defer(100);
13350         
13351         this.list.on('mouseover', this.onViewOver, this);
13352         this.list.on('mousemove', this.onViewMove, this);
13353         
13354         this.list.on('scroll', this.onViewScroll, this);
13355         
13356         if(!this.tpl){
13357             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>';
13358         }
13359
13360         this.view = new Roo.View(this.list, this.tpl, {
13361             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13362         });
13363         
13364         //this.view.wrapEl.setDisplayed(false);
13365         this.view.on('click', this.onViewClick, this);
13366         
13367         
13368         
13369         this.store.on('beforeload', this.onBeforeLoad, this);
13370         this.store.on('load', this.onLoad, this);
13371         this.store.on('loadexception', this.onLoadException, this);
13372         
13373         if(this.editable){
13374             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13375                 "up" : function(e){
13376                     this.inKeyMode = true;
13377                     this.selectPrev();
13378                 },
13379
13380                 "down" : function(e){
13381                     this.inKeyMode = true;
13382                     this.selectNext();
13383                 },
13384
13385                 "enter" : function(e){
13386                     if(this.fireEvent("specialkey", this, e)){
13387                         this.onViewClick(false);
13388                     }
13389                     
13390                     return true;
13391                 },
13392
13393                 "esc" : function(e){
13394                     this.onTickableFooterButtonClick(e, false, false);
13395                 },
13396
13397                 "tab" : function(e){
13398                     this.fireEvent("specialkey", this, e);
13399                     
13400                     this.onTickableFooterButtonClick(e, false, false);
13401                     
13402                     return true;
13403                 },
13404
13405                 scope : this,
13406
13407                 doRelay : function(e, fn, key){
13408                     if(this.scope.isExpanded()){
13409                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13410                     }
13411                     return true;
13412                 },
13413
13414                 forceKeyDown: true
13415             });
13416         }
13417         
13418         this.queryDelay = Math.max(this.queryDelay || 10,
13419                 this.mode == 'local' ? 10 : 250);
13420         
13421         
13422         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13423         
13424         if(this.typeAhead){
13425             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13426         }
13427         
13428         if(this.editable !== false){
13429             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13430         }
13431         
13432         this.indicator = this.indicatorEl();
13433         
13434         if(this.indicator){
13435             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13436             this.indicator.hide();
13437         }
13438         
13439     },
13440
13441     onDestroy : function(){
13442         if(this.view){
13443             this.view.setStore(null);
13444             this.view.el.removeAllListeners();
13445             this.view.el.remove();
13446             this.view.purgeListeners();
13447         }
13448         if(this.list){
13449             this.list.dom.innerHTML  = '';
13450         }
13451         
13452         if(this.store){
13453             this.store.un('beforeload', this.onBeforeLoad, this);
13454             this.store.un('load', this.onLoad, this);
13455             this.store.un('loadexception', this.onLoadException, this);
13456         }
13457         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13458     },
13459
13460     // private
13461     fireKey : function(e){
13462         if(e.isNavKeyPress() && !this.list.isVisible()){
13463             this.fireEvent("specialkey", this, e);
13464         }
13465     },
13466
13467     // private
13468     onResize: function(w, h){
13469 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13470 //        
13471 //        if(typeof w != 'number'){
13472 //            // we do not handle it!?!?
13473 //            return;
13474 //        }
13475 //        var tw = this.trigger.getWidth();
13476 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13477 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13478 //        var x = w - tw;
13479 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13480 //            
13481 //        //this.trigger.setStyle('left', x+'px');
13482 //        
13483 //        if(this.list && this.listWidth === undefined){
13484 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13485 //            this.list.setWidth(lw);
13486 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13487 //        }
13488         
13489     
13490         
13491     },
13492
13493     /**
13494      * Allow or prevent the user from directly editing the field text.  If false is passed,
13495      * the user will only be able to select from the items defined in the dropdown list.  This method
13496      * is the runtime equivalent of setting the 'editable' config option at config time.
13497      * @param {Boolean} value True to allow the user to directly edit the field text
13498      */
13499     setEditable : function(value){
13500         if(value == this.editable){
13501             return;
13502         }
13503         this.editable = value;
13504         if(!value){
13505             this.inputEl().dom.setAttribute('readOnly', true);
13506             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13507             this.inputEl().addClass('x-combo-noedit');
13508         }else{
13509             this.inputEl().dom.setAttribute('readOnly', false);
13510             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13511             this.inputEl().removeClass('x-combo-noedit');
13512         }
13513     },
13514
13515     // private
13516     
13517     onBeforeLoad : function(combo,opts){
13518         if(!this.hasFocus){
13519             return;
13520         }
13521          if (!opts.add) {
13522             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13523          }
13524         this.restrictHeight();
13525         this.selectedIndex = -1;
13526     },
13527
13528     // private
13529     onLoad : function(){
13530         
13531         this.hasQuery = false;
13532         
13533         if(!this.hasFocus){
13534             return;
13535         }
13536         
13537         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13538             this.loading.hide();
13539         }
13540         
13541         if(this.store.getCount() > 0){
13542             
13543             this.expand();
13544             this.restrictHeight();
13545             if(this.lastQuery == this.allQuery){
13546                 if(this.editable && !this.tickable){
13547                     this.inputEl().dom.select();
13548                 }
13549                 
13550                 if(
13551                     !this.selectByValue(this.value, true) &&
13552                     this.autoFocus && 
13553                     (
13554                         !this.store.lastOptions ||
13555                         typeof(this.store.lastOptions.add) == 'undefined' || 
13556                         this.store.lastOptions.add != true
13557                     )
13558                 ){
13559                     this.select(0, true);
13560                 }
13561             }else{
13562                 if(this.autoFocus){
13563                     this.selectNext();
13564                 }
13565                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13566                     this.taTask.delay(this.typeAheadDelay);
13567                 }
13568             }
13569         }else{
13570             this.onEmptyResults();
13571         }
13572         
13573         //this.el.focus();
13574     },
13575     // private
13576     onLoadException : function()
13577     {
13578         this.hasQuery = false;
13579         
13580         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13581             this.loading.hide();
13582         }
13583         
13584         if(this.tickable && this.editable){
13585             return;
13586         }
13587         
13588         this.collapse();
13589         // only causes errors at present
13590         //Roo.log(this.store.reader.jsonData);
13591         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13592             // fixme
13593             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13594         //}
13595         
13596         
13597     },
13598     // private
13599     onTypeAhead : function(){
13600         if(this.store.getCount() > 0){
13601             var r = this.store.getAt(0);
13602             var newValue = r.data[this.displayField];
13603             var len = newValue.length;
13604             var selStart = this.getRawValue().length;
13605             
13606             if(selStart != len){
13607                 this.setRawValue(newValue);
13608                 this.selectText(selStart, newValue.length);
13609             }
13610         }
13611     },
13612
13613     // private
13614     onSelect : function(record, index){
13615         
13616         if(this.fireEvent('beforeselect', this, record, index) !== false){
13617         
13618             this.setFromData(index > -1 ? record.data : false);
13619             
13620             this.collapse();
13621             this.fireEvent('select', this, record, index);
13622         }
13623     },
13624
13625     /**
13626      * Returns the currently selected field value or empty string if no value is set.
13627      * @return {String} value The selected value
13628      */
13629     getValue : function()
13630     {
13631         if(Roo.isIOS && this.useNativeIOS){
13632             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13633         }
13634         
13635         if(this.multiple){
13636             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13637         }
13638         
13639         if(this.valueField){
13640             return typeof this.value != 'undefined' ? this.value : '';
13641         }else{
13642             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13643         }
13644     },
13645     
13646     getRawValue : function()
13647     {
13648         if(Roo.isIOS && this.useNativeIOS){
13649             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13650         }
13651         
13652         var v = this.inputEl().getValue();
13653         
13654         return v;
13655     },
13656
13657     /**
13658      * Clears any text/value currently set in the field
13659      */
13660     clearValue : function(){
13661         
13662         if(this.hiddenField){
13663             this.hiddenField.dom.value = '';
13664         }
13665         this.value = '';
13666         this.setRawValue('');
13667         this.lastSelectionText = '';
13668         this.lastData = false;
13669         
13670         var close = this.closeTriggerEl();
13671         
13672         if(close){
13673             close.hide();
13674         }
13675         
13676         this.validate();
13677         
13678     },
13679
13680     /**
13681      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13682      * will be displayed in the field.  If the value does not match the data value of an existing item,
13683      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13684      * Otherwise the field will be blank (although the value will still be set).
13685      * @param {String} value The value to match
13686      */
13687     setValue : function(v)
13688     {
13689         if(Roo.isIOS && this.useNativeIOS){
13690             this.setIOSValue(v);
13691             return;
13692         }
13693         
13694         if(this.multiple){
13695             this.syncValue();
13696             return;
13697         }
13698         
13699         var text = v;
13700         if(this.valueField){
13701             var r = this.findRecord(this.valueField, v);
13702             if(r){
13703                 text = r.data[this.displayField];
13704             }else if(this.valueNotFoundText !== undefined){
13705                 text = this.valueNotFoundText;
13706             }
13707         }
13708         this.lastSelectionText = text;
13709         if(this.hiddenField){
13710             this.hiddenField.dom.value = v;
13711         }
13712         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13713         this.value = v;
13714         
13715         var close = this.closeTriggerEl();
13716         
13717         if(close){
13718             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13719         }
13720         
13721         this.validate();
13722     },
13723     /**
13724      * @property {Object} the last set data for the element
13725      */
13726     
13727     lastData : false,
13728     /**
13729      * Sets the value of the field based on a object which is related to the record format for the store.
13730      * @param {Object} value the value to set as. or false on reset?
13731      */
13732     setFromData : function(o){
13733         
13734         if(this.multiple){
13735             this.addItem(o);
13736             return;
13737         }
13738             
13739         var dv = ''; // display value
13740         var vv = ''; // value value..
13741         this.lastData = o;
13742         if (this.displayField) {
13743             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13744         } else {
13745             // this is an error condition!!!
13746             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13747         }
13748         
13749         if(this.valueField){
13750             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13751         }
13752         
13753         var close = this.closeTriggerEl();
13754         
13755         if(close){
13756             if(dv.length || vv * 1 > 0){
13757                 close.show() ;
13758                 this.blockFocus=true;
13759             } else {
13760                 close.hide();
13761             }             
13762         }
13763         
13764         if(this.hiddenField){
13765             this.hiddenField.dom.value = vv;
13766             
13767             this.lastSelectionText = dv;
13768             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13769             this.value = vv;
13770             return;
13771         }
13772         // no hidden field.. - we store the value in 'value', but still display
13773         // display field!!!!
13774         this.lastSelectionText = dv;
13775         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13776         this.value = vv;
13777         
13778         
13779         
13780     },
13781     // private
13782     reset : function(){
13783         // overridden so that last data is reset..
13784         
13785         if(this.multiple){
13786             this.clearItem();
13787             return;
13788         }
13789         
13790         this.setValue(this.originalValue);
13791         //this.clearInvalid();
13792         this.lastData = false;
13793         if (this.view) {
13794             this.view.clearSelections();
13795         }
13796         
13797         this.validate();
13798     },
13799     // private
13800     findRecord : function(prop, value){
13801         var record;
13802         if(this.store.getCount() > 0){
13803             this.store.each(function(r){
13804                 if(r.data[prop] == value){
13805                     record = r;
13806                     return false;
13807                 }
13808                 return true;
13809             });
13810         }
13811         return record;
13812     },
13813     
13814     getName: function()
13815     {
13816         // returns hidden if it's set..
13817         if (!this.rendered) {return ''};
13818         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13819         
13820     },
13821     // private
13822     onViewMove : function(e, t){
13823         this.inKeyMode = false;
13824     },
13825
13826     // private
13827     onViewOver : function(e, t){
13828         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13829             return;
13830         }
13831         var item = this.view.findItemFromChild(t);
13832         
13833         if(item){
13834             var index = this.view.indexOf(item);
13835             this.select(index, false);
13836         }
13837     },
13838
13839     // private
13840     onViewClick : function(view, doFocus, el, e)
13841     {
13842         var index = this.view.getSelectedIndexes()[0];
13843         
13844         var r = this.store.getAt(index);
13845         
13846         if(this.tickable){
13847             
13848             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13849                 return;
13850             }
13851             
13852             var rm = false;
13853             var _this = this;
13854             
13855             Roo.each(this.tickItems, function(v,k){
13856                 
13857                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13858                     Roo.log(v);
13859                     _this.tickItems.splice(k, 1);
13860                     
13861                     if(typeof(e) == 'undefined' && view == false){
13862                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13863                     }
13864                     
13865                     rm = true;
13866                     return;
13867                 }
13868             });
13869             
13870             if(rm){
13871                 return;
13872             }
13873             
13874             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13875                 this.tickItems.push(r.data);
13876             }
13877             
13878             if(typeof(e) == 'undefined' && view == false){
13879                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13880             }
13881                     
13882             return;
13883         }
13884         
13885         if(r){
13886             this.onSelect(r, index);
13887         }
13888         if(doFocus !== false && !this.blockFocus){
13889             this.inputEl().focus();
13890         }
13891     },
13892
13893     // private
13894     restrictHeight : function(){
13895         //this.innerList.dom.style.height = '';
13896         //var inner = this.innerList.dom;
13897         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13898         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13899         //this.list.beginUpdate();
13900         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13901         this.list.alignTo(this.inputEl(), this.listAlign);
13902         this.list.alignTo(this.inputEl(), this.listAlign);
13903         //this.list.endUpdate();
13904     },
13905
13906     // private
13907     onEmptyResults : function(){
13908         
13909         if(this.tickable && this.editable){
13910             this.restrictHeight();
13911             return;
13912         }
13913         
13914         this.collapse();
13915     },
13916
13917     /**
13918      * Returns true if the dropdown list is expanded, else false.
13919      */
13920     isExpanded : function(){
13921         return this.list.isVisible();
13922     },
13923
13924     /**
13925      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13926      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13927      * @param {String} value The data value of the item to select
13928      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13929      * selected item if it is not currently in view (defaults to true)
13930      * @return {Boolean} True if the value matched an item in the list, else false
13931      */
13932     selectByValue : function(v, scrollIntoView){
13933         if(v !== undefined && v !== null){
13934             var r = this.findRecord(this.valueField || this.displayField, v);
13935             if(r){
13936                 this.select(this.store.indexOf(r), scrollIntoView);
13937                 return true;
13938             }
13939         }
13940         return false;
13941     },
13942
13943     /**
13944      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13946      * @param {Number} index The zero-based index of the list item to select
13947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13948      * selected item if it is not currently in view (defaults to true)
13949      */
13950     select : function(index, scrollIntoView){
13951         this.selectedIndex = index;
13952         this.view.select(index);
13953         if(scrollIntoView !== false){
13954             var el = this.view.getNode(index);
13955             /*
13956              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13957              */
13958             if(el){
13959                 this.list.scrollChildIntoView(el, false);
13960             }
13961         }
13962     },
13963
13964     // private
13965     selectNext : function(){
13966         var ct = this.store.getCount();
13967         if(ct > 0){
13968             if(this.selectedIndex == -1){
13969                 this.select(0);
13970             }else if(this.selectedIndex < ct-1){
13971                 this.select(this.selectedIndex+1);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectPrev : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex != 0){
13983                 this.select(this.selectedIndex-1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     onKeyUp : function(e){
13990         if(this.editable !== false && !e.isSpecialKey()){
13991             this.lastKey = e.getKey();
13992             this.dqTask.delay(this.queryDelay);
13993         }
13994     },
13995
13996     // private
13997     validateBlur : function(){
13998         return !this.list || !this.list.isVisible();   
13999     },
14000
14001     // private
14002     initQuery : function(){
14003         
14004         var v = this.getRawValue();
14005         
14006         if(this.tickable && this.editable){
14007             v = this.tickableInputEl().getValue();
14008         }
14009         
14010         this.doQuery(v);
14011     },
14012
14013     // private
14014     doForce : function(){
14015         if(this.inputEl().dom.value.length > 0){
14016             this.inputEl().dom.value =
14017                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14018              
14019         }
14020     },
14021
14022     /**
14023      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14024      * query allowing the query action to be canceled if needed.
14025      * @param {String} query The SQL query to execute
14026      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14027      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14028      * saved in the current store (defaults to false)
14029      */
14030     doQuery : function(q, forceAll){
14031         
14032         if(q === undefined || q === null){
14033             q = '';
14034         }
14035         var qe = {
14036             query: q,
14037             forceAll: forceAll,
14038             combo: this,
14039             cancel:false
14040         };
14041         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14042             return false;
14043         }
14044         q = qe.query;
14045         
14046         forceAll = qe.forceAll;
14047         if(forceAll === true || (q.length >= this.minChars)){
14048             
14049             this.hasQuery = true;
14050             
14051             if(this.lastQuery != q || this.alwaysQuery){
14052                 this.lastQuery = q;
14053                 if(this.mode == 'local'){
14054                     this.selectedIndex = -1;
14055                     if(forceAll){
14056                         this.store.clearFilter();
14057                     }else{
14058                         
14059                         if(this.specialFilter){
14060                             this.fireEvent('specialfilter', this);
14061                             this.onLoad();
14062                             return;
14063                         }
14064                         
14065                         this.store.filter(this.displayField, q);
14066                     }
14067                     
14068                     this.store.fireEvent("datachanged", this.store);
14069                     
14070                     this.onLoad();
14071                     
14072                     
14073                 }else{
14074                     
14075                     this.store.baseParams[this.queryParam] = q;
14076                     
14077                     var options = {params : this.getParams(q)};
14078                     
14079                     if(this.loadNext){
14080                         options.add = true;
14081                         options.params.start = this.page * this.pageSize;
14082                     }
14083                     
14084                     this.store.load(options);
14085                     
14086                     /*
14087                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14088                      *  we should expand the list on onLoad
14089                      *  so command out it
14090                      */
14091 //                    this.expand();
14092                 }
14093             }else{
14094                 this.selectedIndex = -1;
14095                 this.onLoad();   
14096             }
14097         }
14098         
14099         this.loadNext = false;
14100     },
14101     
14102     // private
14103     getParams : function(q){
14104         var p = {};
14105         //p[this.queryParam] = q;
14106         
14107         if(this.pageSize){
14108             p.start = 0;
14109             p.limit = this.pageSize;
14110         }
14111         return p;
14112     },
14113
14114     /**
14115      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14116      */
14117     collapse : function(){
14118         if(!this.isExpanded()){
14119             return;
14120         }
14121         
14122         this.list.hide();
14123         
14124         this.hasFocus = false;
14125         
14126         if(this.tickable){
14127             this.okBtn.hide();
14128             this.cancelBtn.hide();
14129             this.trigger.show();
14130             
14131             if(this.editable){
14132                 this.tickableInputEl().dom.value = '';
14133                 this.tickableInputEl().blur();
14134             }
14135             
14136         }
14137         
14138         Roo.get(document).un('mousedown', this.collapseIf, this);
14139         Roo.get(document).un('mousewheel', this.collapseIf, this);
14140         if (!this.editable) {
14141             Roo.get(document).un('keydown', this.listKeyPress, this);
14142         }
14143         this.fireEvent('collapse', this);
14144         
14145         this.validate();
14146     },
14147
14148     // private
14149     collapseIf : function(e){
14150         var in_combo  = e.within(this.el);
14151         var in_list =  e.within(this.list);
14152         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14153         
14154         if (in_combo || in_list || is_list) {
14155             //e.stopPropagation();
14156             return;
14157         }
14158         
14159         if(this.tickable){
14160             this.onTickableFooterButtonClick(e, false, false);
14161         }
14162
14163         this.collapse();
14164         
14165     },
14166
14167     /**
14168      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14169      */
14170     expand : function(){
14171        
14172         if(this.isExpanded() || !this.hasFocus){
14173             return;
14174         }
14175         
14176         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14177         this.list.setWidth(lw);
14178         
14179         Roo.log('expand');
14180         
14181         this.list.show();
14182         
14183         this.restrictHeight();
14184         
14185         if(this.tickable){
14186             
14187             this.tickItems = Roo.apply([], this.item);
14188             
14189             this.okBtn.show();
14190             this.cancelBtn.show();
14191             this.trigger.hide();
14192             
14193             if(this.editable){
14194                 this.tickableInputEl().focus();
14195             }
14196             
14197         }
14198         
14199         Roo.get(document).on('mousedown', this.collapseIf, this);
14200         Roo.get(document).on('mousewheel', this.collapseIf, this);
14201         if (!this.editable) {
14202             Roo.get(document).on('keydown', this.listKeyPress, this);
14203         }
14204         
14205         this.fireEvent('expand', this);
14206     },
14207
14208     // private
14209     // Implements the default empty TriggerField.onTriggerClick function
14210     onTriggerClick : function(e)
14211     {
14212         Roo.log('trigger click');
14213         
14214         if(this.disabled || !this.triggerList){
14215             return;
14216         }
14217         
14218         this.page = 0;
14219         this.loadNext = false;
14220         
14221         if(this.isExpanded()){
14222             this.collapse();
14223             if (!this.blockFocus) {
14224                 this.inputEl().focus();
14225             }
14226             
14227         }else {
14228             this.hasFocus = true;
14229             if(this.triggerAction == 'all') {
14230                 this.doQuery(this.allQuery, true);
14231             } else {
14232                 this.doQuery(this.getRawValue());
14233             }
14234             if (!this.blockFocus) {
14235                 this.inputEl().focus();
14236             }
14237         }
14238     },
14239     
14240     onTickableTriggerClick : function(e)
14241     {
14242         if(this.disabled){
14243             return;
14244         }
14245         
14246         this.page = 0;
14247         this.loadNext = false;
14248         this.hasFocus = true;
14249         
14250         if(this.triggerAction == 'all') {
14251             this.doQuery(this.allQuery, true);
14252         } else {
14253             this.doQuery(this.getRawValue());
14254         }
14255     },
14256     
14257     onSearchFieldClick : function(e)
14258     {
14259         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14260             this.onTickableFooterButtonClick(e, false, false);
14261             return;
14262         }
14263         
14264         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14265             return;
14266         }
14267         
14268         this.page = 0;
14269         this.loadNext = false;
14270         this.hasFocus = true;
14271         
14272         if(this.triggerAction == 'all') {
14273             this.doQuery(this.allQuery, true);
14274         } else {
14275             this.doQuery(this.getRawValue());
14276         }
14277     },
14278     
14279     listKeyPress : function(e)
14280     {
14281         //Roo.log('listkeypress');
14282         // scroll to first matching element based on key pres..
14283         if (e.isSpecialKey()) {
14284             return false;
14285         }
14286         var k = String.fromCharCode(e.getKey()).toUpperCase();
14287         //Roo.log(k);
14288         var match  = false;
14289         var csel = this.view.getSelectedNodes();
14290         var cselitem = false;
14291         if (csel.length) {
14292             var ix = this.view.indexOf(csel[0]);
14293             cselitem  = this.store.getAt(ix);
14294             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14295                 cselitem = false;
14296             }
14297             
14298         }
14299         
14300         this.store.each(function(v) { 
14301             if (cselitem) {
14302                 // start at existing selection.
14303                 if (cselitem.id == v.id) {
14304                     cselitem = false;
14305                 }
14306                 return true;
14307             }
14308                 
14309             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14310                 match = this.store.indexOf(v);
14311                 return false;
14312             }
14313             return true;
14314         }, this);
14315         
14316         if (match === false) {
14317             return true; // no more action?
14318         }
14319         // scroll to?
14320         this.view.select(match);
14321         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14322         sn.scrollIntoView(sn.dom.parentNode, false);
14323     },
14324     
14325     onViewScroll : function(e, t){
14326         
14327         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){
14328             return;
14329         }
14330         
14331         this.hasQuery = true;
14332         
14333         this.loading = this.list.select('.loading', true).first();
14334         
14335         if(this.loading === null){
14336             this.list.createChild({
14337                 tag: 'div',
14338                 cls: 'loading roo-select2-more-results roo-select2-active',
14339                 html: 'Loading more results...'
14340             });
14341             
14342             this.loading = this.list.select('.loading', true).first();
14343             
14344             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14345             
14346             this.loading.hide();
14347         }
14348         
14349         this.loading.show();
14350         
14351         var _combo = this;
14352         
14353         this.page++;
14354         this.loadNext = true;
14355         
14356         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14357         
14358         return;
14359     },
14360     
14361     addItem : function(o)
14362     {   
14363         var dv = ''; // display value
14364         
14365         if (this.displayField) {
14366             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14367         } else {
14368             // this is an error condition!!!
14369             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14370         }
14371         
14372         if(!dv.length){
14373             return;
14374         }
14375         
14376         var choice = this.choices.createChild({
14377             tag: 'li',
14378             cls: 'roo-select2-search-choice',
14379             cn: [
14380                 {
14381                     tag: 'div',
14382                     html: dv
14383                 },
14384                 {
14385                     tag: 'a',
14386                     href: '#',
14387                     cls: 'roo-select2-search-choice-close fa fa-times',
14388                     tabindex: '-1'
14389                 }
14390             ]
14391             
14392         }, this.searchField);
14393         
14394         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14395         
14396         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14397         
14398         this.item.push(o);
14399         
14400         this.lastData = o;
14401         
14402         this.syncValue();
14403         
14404         this.inputEl().dom.value = '';
14405         
14406         this.validate();
14407     },
14408     
14409     onRemoveItem : function(e, _self, o)
14410     {
14411         e.preventDefault();
14412         
14413         this.lastItem = Roo.apply([], this.item);
14414         
14415         var index = this.item.indexOf(o.data) * 1;
14416         
14417         if( index < 0){
14418             Roo.log('not this item?!');
14419             return;
14420         }
14421         
14422         this.item.splice(index, 1);
14423         o.item.remove();
14424         
14425         this.syncValue();
14426         
14427         this.fireEvent('remove', this, e);
14428         
14429         this.validate();
14430         
14431     },
14432     
14433     syncValue : function()
14434     {
14435         if(!this.item.length){
14436             this.clearValue();
14437             return;
14438         }
14439             
14440         var value = [];
14441         var _this = this;
14442         Roo.each(this.item, function(i){
14443             if(_this.valueField){
14444                 value.push(i[_this.valueField]);
14445                 return;
14446             }
14447
14448             value.push(i);
14449         });
14450
14451         this.value = value.join(',');
14452
14453         if(this.hiddenField){
14454             this.hiddenField.dom.value = this.value;
14455         }
14456         
14457         this.store.fireEvent("datachanged", this.store);
14458         
14459         this.validate();
14460     },
14461     
14462     clearItem : function()
14463     {
14464         if(!this.multiple){
14465             return;
14466         }
14467         
14468         this.item = [];
14469         
14470         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14471            c.remove();
14472         });
14473         
14474         this.syncValue();
14475         
14476         this.validate();
14477         
14478         if(this.tickable && !Roo.isTouch){
14479             this.view.refresh();
14480         }
14481     },
14482     
14483     inputEl: function ()
14484     {
14485         if(Roo.isIOS && this.useNativeIOS){
14486             return this.el.select('select.roo-ios-select', true).first();
14487         }
14488         
14489         if(Roo.isTouch && this.mobileTouchView){
14490             return this.el.select('input.form-control',true).first();
14491         }
14492         
14493         if(this.tickable){
14494             return this.searchField;
14495         }
14496         
14497         return this.el.select('input.form-control',true).first();
14498     },
14499     
14500     onTickableFooterButtonClick : function(e, btn, el)
14501     {
14502         e.preventDefault();
14503         
14504         this.lastItem = Roo.apply([], this.item);
14505         
14506         if(btn && btn.name == 'cancel'){
14507             this.tickItems = Roo.apply([], this.item);
14508             this.collapse();
14509             return;
14510         }
14511         
14512         this.clearItem();
14513         
14514         var _this = this;
14515         
14516         Roo.each(this.tickItems, function(o){
14517             _this.addItem(o);
14518         });
14519         
14520         this.collapse();
14521         
14522     },
14523     
14524     validate : function()
14525     {
14526         var v = this.getRawValue();
14527         
14528         if(this.multiple){
14529             v = this.getValue();
14530         }
14531         
14532         if(this.disabled || this.allowBlank || v.length){
14533             this.markValid();
14534             return true;
14535         }
14536         
14537         this.markInvalid();
14538         return false;
14539     },
14540     
14541     tickableInputEl : function()
14542     {
14543         if(!this.tickable || !this.editable){
14544             return this.inputEl();
14545         }
14546         
14547         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14548     },
14549     
14550     
14551     getAutoCreateTouchView : function()
14552     {
14553         var id = Roo.id();
14554         
14555         var cfg = {
14556             cls: 'form-group' //input-group
14557         };
14558         
14559         var input =  {
14560             tag: 'input',
14561             id : id,
14562             type : this.inputType,
14563             cls : 'form-control x-combo-noedit',
14564             autocomplete: 'new-password',
14565             placeholder : this.placeholder || '',
14566             readonly : true
14567         };
14568         
14569         if (this.name) {
14570             input.name = this.name;
14571         }
14572         
14573         if (this.size) {
14574             input.cls += ' input-' + this.size;
14575         }
14576         
14577         if (this.disabled) {
14578             input.disabled = true;
14579         }
14580         
14581         var inputblock = {
14582             cls : '',
14583             cn : [
14584                 input
14585             ]
14586         };
14587         
14588         if(this.before){
14589             inputblock.cls += ' input-group';
14590             
14591             inputblock.cn.unshift({
14592                 tag :'span',
14593                 cls : 'input-group-addon',
14594                 html : this.before
14595             });
14596         }
14597         
14598         if(this.removable && !this.multiple){
14599             inputblock.cls += ' roo-removable';
14600             
14601             inputblock.cn.push({
14602                 tag: 'button',
14603                 html : 'x',
14604                 cls : 'roo-combo-removable-btn close'
14605             });
14606         }
14607
14608         if(this.hasFeedback && !this.allowBlank){
14609             
14610             inputblock.cls += ' has-feedback';
14611             
14612             inputblock.cn.push({
14613                 tag: 'span',
14614                 cls: 'glyphicon form-control-feedback'
14615             });
14616             
14617         }
14618         
14619         if (this.after) {
14620             
14621             inputblock.cls += (this.before) ? '' : ' input-group';
14622             
14623             inputblock.cn.push({
14624                 tag :'span',
14625                 cls : 'input-group-addon',
14626                 html : this.after
14627             });
14628         }
14629
14630         var box = {
14631             tag: 'div',
14632             cn: [
14633                 {
14634                     tag: 'input',
14635                     type : 'hidden',
14636                     cls: 'form-hidden-field'
14637                 },
14638                 inputblock
14639             ]
14640             
14641         };
14642         
14643         if(this.multiple){
14644             box = {
14645                 tag: 'div',
14646                 cn: [
14647                     {
14648                         tag: 'input',
14649                         type : 'hidden',
14650                         cls: 'form-hidden-field'
14651                     },
14652                     {
14653                         tag: 'ul',
14654                         cls: 'roo-select2-choices',
14655                         cn:[
14656                             {
14657                                 tag: 'li',
14658                                 cls: 'roo-select2-search-field',
14659                                 cn: [
14660
14661                                     inputblock
14662                                 ]
14663                             }
14664                         ]
14665                     }
14666                 ]
14667             }
14668         };
14669         
14670         var combobox = {
14671             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14672             cn: [
14673                 box
14674             ]
14675         };
14676         
14677         if(!this.multiple && this.showToggleBtn){
14678             
14679             var caret = {
14680                         tag: 'span',
14681                         cls: 'caret'
14682             };
14683             
14684             if (this.caret != false) {
14685                 caret = {
14686                      tag: 'i',
14687                      cls: 'fa fa-' + this.caret
14688                 };
14689                 
14690             }
14691             
14692             combobox.cn.push({
14693                 tag :'span',
14694                 cls : 'input-group-addon btn dropdown-toggle',
14695                 cn : [
14696                     caret,
14697                     {
14698                         tag: 'span',
14699                         cls: 'combobox-clear',
14700                         cn  : [
14701                             {
14702                                 tag : 'i',
14703                                 cls: 'icon-remove'
14704                             }
14705                         ]
14706                     }
14707                 ]
14708
14709             })
14710         }
14711         
14712         if(this.multiple){
14713             combobox.cls += ' roo-select2-container-multi';
14714         }
14715         
14716         var align = this.labelAlign || this.parentLabelAlign();
14717         
14718         if (align ==='left' && this.fieldLabel.length) {
14719
14720             cfg.cn = [
14721                 {
14722                    tag : 'i',
14723                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14724                    tooltip : 'This field is required'
14725                 },
14726                 {
14727                     tag: 'label',
14728                     cls : 'control-label',
14729                     html : this.fieldLabel
14730
14731                 },
14732                 {
14733                     cls : '', 
14734                     cn: [
14735                         combobox
14736                     ]
14737                 }
14738             ];
14739             
14740             var labelCfg = cfg.cn[1];
14741             var contentCfg = cfg.cn[2];
14742             
14743
14744             if(this.indicatorpos == 'right'){
14745                 cfg.cn = [
14746                     {
14747                         tag: 'label',
14748                         'for' :  id,
14749                         cls : 'control-label',
14750                         cn : [
14751                             {
14752                                 tag : 'span',
14753                                 html : this.fieldLabel
14754                             },
14755                             {
14756                                 tag : 'i',
14757                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14758                                 tooltip : 'This field is required'
14759                             }
14760                         ]
14761                     },
14762                     {
14763                         cls : "",
14764                         cn: [
14765                             combobox
14766                         ]
14767                     }
14768
14769                 ];
14770                 
14771                 labelCfg = cfg.cn[0];
14772                 contentCfg = cfg.cn[1];
14773             }
14774             
14775            
14776             
14777             if(this.labelWidth > 12){
14778                 labelCfg.style = "width: " + this.labelWidth + 'px';
14779             }
14780             
14781             if(this.labelWidth < 13 && this.labelmd == 0){
14782                 this.labelmd = this.labelWidth;
14783             }
14784             
14785             if(this.labellg > 0){
14786                 labelCfg.cls += ' col-lg-' + this.labellg;
14787                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14788             }
14789             
14790             if(this.labelmd > 0){
14791                 labelCfg.cls += ' col-md-' + this.labelmd;
14792                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14793             }
14794             
14795             if(this.labelsm > 0){
14796                 labelCfg.cls += ' col-sm-' + this.labelsm;
14797                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14798             }
14799             
14800             if(this.labelxs > 0){
14801                 labelCfg.cls += ' col-xs-' + this.labelxs;
14802                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14803             }
14804                 
14805                 
14806         } else if ( this.fieldLabel.length) {
14807             cfg.cn = [
14808                 {
14809                    tag : 'i',
14810                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14811                    tooltip : 'This field is required'
14812                 },
14813                 {
14814                     tag: 'label',
14815                     cls : 'control-label',
14816                     html : this.fieldLabel
14817
14818                 },
14819                 {
14820                     cls : '', 
14821                     cn: [
14822                         combobox
14823                     ]
14824                 }
14825             ];
14826             
14827             if(this.indicatorpos == 'right'){
14828                 cfg.cn = [
14829                     {
14830                         tag: 'label',
14831                         cls : 'control-label',
14832                         html : this.fieldLabel,
14833                         cn : [
14834                             {
14835                                tag : 'i',
14836                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14837                                tooltip : 'This field is required'
14838                             }
14839                         ]
14840                     },
14841                     {
14842                         cls : '', 
14843                         cn: [
14844                             combobox
14845                         ]
14846                     }
14847                 ];
14848             }
14849         } else {
14850             cfg.cn = combobox;    
14851         }
14852         
14853         
14854         var settings = this;
14855         
14856         ['xs','sm','md','lg'].map(function(size){
14857             if (settings[size]) {
14858                 cfg.cls += ' col-' + size + '-' + settings[size];
14859             }
14860         });
14861         
14862         return cfg;
14863     },
14864     
14865     initTouchView : function()
14866     {
14867         this.renderTouchView();
14868         
14869         this.touchViewEl.on('scroll', function(){
14870             this.el.dom.scrollTop = 0;
14871         }, this);
14872         
14873         this.originalValue = this.getValue();
14874         
14875         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14876         
14877         this.inputEl().on("click", this.showTouchView, this);
14878         if (this.triggerEl) {
14879             this.triggerEl.on("click", this.showTouchView, this);
14880         }
14881         
14882         
14883         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14884         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14885         
14886         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14887         
14888         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14889         this.store.on('load', this.onTouchViewLoad, this);
14890         this.store.on('loadexception', this.onTouchViewLoadException, this);
14891         
14892         if(this.hiddenName){
14893             
14894             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14895             
14896             this.hiddenField.dom.value =
14897                 this.hiddenValue !== undefined ? this.hiddenValue :
14898                 this.value !== undefined ? this.value : '';
14899         
14900             this.el.dom.removeAttribute('name');
14901             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14902         }
14903         
14904         if(this.multiple){
14905             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14906             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14907         }
14908         
14909         if(this.removable && !this.multiple){
14910             var close = this.closeTriggerEl();
14911             if(close){
14912                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14913                 close.on('click', this.removeBtnClick, this, close);
14914             }
14915         }
14916         /*
14917          * fix the bug in Safari iOS8
14918          */
14919         this.inputEl().on("focus", function(e){
14920             document.activeElement.blur();
14921         }, this);
14922         
14923         return;
14924         
14925         
14926     },
14927     
14928     renderTouchView : function()
14929     {
14930         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14931         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14932         
14933         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14934         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14935         
14936         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14937         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14938         this.touchViewBodyEl.setStyle('overflow', 'auto');
14939         
14940         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14941         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14942         
14943         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14944         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14945         
14946     },
14947     
14948     showTouchView : function()
14949     {
14950         if(this.disabled){
14951             return;
14952         }
14953         
14954         this.touchViewHeaderEl.hide();
14955
14956         if(this.modalTitle.length){
14957             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14958             this.touchViewHeaderEl.show();
14959         }
14960
14961         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14962         this.touchViewEl.show();
14963
14964         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14965         
14966         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14967         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14968
14969         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14970
14971         if(this.modalTitle.length){
14972             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14973         }
14974         
14975         this.touchViewBodyEl.setHeight(bodyHeight);
14976
14977         if(this.animate){
14978             var _this = this;
14979             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14980         }else{
14981             this.touchViewEl.addClass('in');
14982         }
14983
14984         this.doTouchViewQuery();
14985         
14986     },
14987     
14988     hideTouchView : function()
14989     {
14990         this.touchViewEl.removeClass('in');
14991
14992         if(this.animate){
14993             var _this = this;
14994             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14995         }else{
14996             this.touchViewEl.setStyle('display', 'none');
14997         }
14998         
14999     },
15000     
15001     setTouchViewValue : function()
15002     {
15003         if(this.multiple){
15004             this.clearItem();
15005         
15006             var _this = this;
15007
15008             Roo.each(this.tickItems, function(o){
15009                 this.addItem(o);
15010             }, this);
15011         }
15012         
15013         this.hideTouchView();
15014     },
15015     
15016     doTouchViewQuery : function()
15017     {
15018         var qe = {
15019             query: '',
15020             forceAll: true,
15021             combo: this,
15022             cancel:false
15023         };
15024         
15025         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15026             return false;
15027         }
15028         
15029         if(!this.alwaysQuery || this.mode == 'local'){
15030             this.onTouchViewLoad();
15031             return;
15032         }
15033         
15034         this.store.load();
15035     },
15036     
15037     onTouchViewBeforeLoad : function(combo,opts)
15038     {
15039         return;
15040     },
15041
15042     // private
15043     onTouchViewLoad : function()
15044     {
15045         if(this.store.getCount() < 1){
15046             this.onTouchViewEmptyResults();
15047             return;
15048         }
15049         
15050         this.clearTouchView();
15051         
15052         var rawValue = this.getRawValue();
15053         
15054         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15055         
15056         this.tickItems = [];
15057         
15058         this.store.data.each(function(d, rowIndex){
15059             var row = this.touchViewListGroup.createChild(template);
15060             
15061             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15062                 row.addClass(d.data.cls);
15063             }
15064             
15065             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15066                 var cfg = {
15067                     data : d.data,
15068                     html : d.data[this.displayField]
15069                 };
15070                 
15071                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15072                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15073                 }
15074             }
15075             row.removeClass('selected');
15076             if(!this.multiple && this.valueField &&
15077                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15078             {
15079                 // radio buttons..
15080                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15081                 row.addClass('selected');
15082             }
15083             
15084             if(this.multiple && this.valueField &&
15085                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15086             {
15087                 
15088                 // checkboxes...
15089                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15090                 this.tickItems.push(d.data);
15091             }
15092             
15093             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15094             
15095         }, this);
15096         
15097         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15098         
15099         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15100
15101         if(this.modalTitle.length){
15102             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15103         }
15104
15105         var listHeight = this.touchViewListGroup.getHeight();
15106         
15107         var _this = this;
15108         
15109         if(firstChecked && listHeight > bodyHeight){
15110             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15111         }
15112         
15113     },
15114     
15115     onTouchViewLoadException : function()
15116     {
15117         this.hideTouchView();
15118     },
15119     
15120     onTouchViewEmptyResults : function()
15121     {
15122         this.clearTouchView();
15123         
15124         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15125         
15126         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15127         
15128     },
15129     
15130     clearTouchView : function()
15131     {
15132         this.touchViewListGroup.dom.innerHTML = '';
15133     },
15134     
15135     onTouchViewClick : function(e, el, o)
15136     {
15137         e.preventDefault();
15138         
15139         var row = o.row;
15140         var rowIndex = o.rowIndex;
15141         
15142         var r = this.store.getAt(rowIndex);
15143         
15144         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15145             
15146             if(!this.multiple){
15147                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15148                     c.dom.removeAttribute('checked');
15149                 }, this);
15150
15151                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15152
15153                 this.setFromData(r.data);
15154
15155                 var close = this.closeTriggerEl();
15156
15157                 if(close){
15158                     close.show();
15159                 }
15160
15161                 this.hideTouchView();
15162
15163                 this.fireEvent('select', this, r, rowIndex);
15164
15165                 return;
15166             }
15167
15168             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15169                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15170                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15171                 return;
15172             }
15173
15174             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15175             this.addItem(r.data);
15176             this.tickItems.push(r.data);
15177         }
15178     },
15179     
15180     getAutoCreateNativeIOS : function()
15181     {
15182         var cfg = {
15183             cls: 'form-group' //input-group,
15184         };
15185         
15186         var combobox =  {
15187             tag: 'select',
15188             cls : 'roo-ios-select'
15189         };
15190         
15191         if (this.name) {
15192             combobox.name = this.name;
15193         }
15194         
15195         if (this.disabled) {
15196             combobox.disabled = true;
15197         }
15198         
15199         var settings = this;
15200         
15201         ['xs','sm','md','lg'].map(function(size){
15202             if (settings[size]) {
15203                 cfg.cls += ' col-' + size + '-' + settings[size];
15204             }
15205         });
15206         
15207         cfg.cn = combobox;
15208         
15209         return cfg;
15210         
15211     },
15212     
15213     initIOSView : function()
15214     {
15215         this.store.on('load', this.onIOSViewLoad, this);
15216         
15217         return;
15218     },
15219     
15220     onIOSViewLoad : function()
15221     {
15222         if(this.store.getCount() < 1){
15223             return;
15224         }
15225         
15226         this.clearIOSView();
15227         
15228         if(this.allowBlank) {
15229             
15230             var default_text = '-- SELECT --';
15231             
15232             var opt = this.inputEl().createChild({
15233                 tag: 'option',
15234                 value : 0,
15235                 html : default_text
15236             });
15237             
15238             var o = {};
15239             o[this.valueField] = 0;
15240             o[this.displayField] = default_text;
15241             
15242             this.ios_options.push({
15243                 data : o,
15244                 el : opt
15245             });
15246             
15247         }
15248         
15249         this.store.data.each(function(d, rowIndex){
15250             
15251             var html = '';
15252             
15253             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15254                 html = d.data[this.displayField];
15255             }
15256             
15257             var value = '';
15258             
15259             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15260                 value = d.data[this.valueField];
15261             }
15262             
15263             var option = {
15264                 tag: 'option',
15265                 value : value,
15266                 html : html
15267             };
15268             
15269             if(this.value == d.data[this.valueField]){
15270                 option['selected'] = true;
15271             }
15272             
15273             var opt = this.inputEl().createChild(option);
15274             
15275             this.ios_options.push({
15276                 data : d.data,
15277                 el : opt
15278             });
15279             
15280         }, this);
15281         
15282         this.inputEl().on('change', function(){
15283            this.fireEvent('select', this);
15284         }, this);
15285         
15286     },
15287     
15288     clearIOSView: function()
15289     {
15290         this.inputEl().dom.innerHTML = '';
15291         
15292         this.ios_options = [];
15293     },
15294     
15295     setIOSValue: function(v)
15296     {
15297         this.value = v;
15298         
15299         if(!this.ios_options){
15300             return;
15301         }
15302         
15303         Roo.each(this.ios_options, function(opts){
15304            
15305            opts.el.dom.removeAttribute('selected');
15306            
15307            if(opts.data[this.valueField] != v){
15308                return;
15309            }
15310            
15311            opts.el.dom.setAttribute('selected', true);
15312            
15313         }, this);
15314     }
15315
15316     /** 
15317     * @cfg {Boolean} grow 
15318     * @hide 
15319     */
15320     /** 
15321     * @cfg {Number} growMin 
15322     * @hide 
15323     */
15324     /** 
15325     * @cfg {Number} growMax 
15326     * @hide 
15327     */
15328     /**
15329      * @hide
15330      * @method autoSize
15331      */
15332 });
15333
15334 Roo.apply(Roo.bootstrap.ComboBox,  {
15335     
15336     header : {
15337         tag: 'div',
15338         cls: 'modal-header',
15339         cn: [
15340             {
15341                 tag: 'h4',
15342                 cls: 'modal-title'
15343             }
15344         ]
15345     },
15346     
15347     body : {
15348         tag: 'div',
15349         cls: 'modal-body',
15350         cn: [
15351             {
15352                 tag: 'ul',
15353                 cls: 'list-group'
15354             }
15355         ]
15356     },
15357     
15358     listItemRadio : {
15359         tag: 'li',
15360         cls: 'list-group-item',
15361         cn: [
15362             {
15363                 tag: 'span',
15364                 cls: 'roo-combobox-list-group-item-value'
15365             },
15366             {
15367                 tag: 'div',
15368                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15369                 cn: [
15370                     {
15371                         tag: 'input',
15372                         type: 'radio'
15373                     },
15374                     {
15375                         tag: 'label'
15376                     }
15377                 ]
15378             }
15379         ]
15380     },
15381     
15382     listItemCheckbox : {
15383         tag: 'li',
15384         cls: 'list-group-item',
15385         cn: [
15386             {
15387                 tag: 'span',
15388                 cls: 'roo-combobox-list-group-item-value'
15389             },
15390             {
15391                 tag: 'div',
15392                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15393                 cn: [
15394                     {
15395                         tag: 'input',
15396                         type: 'checkbox'
15397                     },
15398                     {
15399                         tag: 'label'
15400                     }
15401                 ]
15402             }
15403         ]
15404     },
15405     
15406     emptyResult : {
15407         tag: 'div',
15408         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15409     },
15410     
15411     footer : {
15412         tag: 'div',
15413         cls: 'modal-footer',
15414         cn: [
15415             {
15416                 tag: 'div',
15417                 cls: 'row',
15418                 cn: [
15419                     {
15420                         tag: 'div',
15421                         cls: 'col-xs-6 text-left',
15422                         cn: {
15423                             tag: 'button',
15424                             cls: 'btn btn-danger roo-touch-view-cancel',
15425                             html: 'Cancel'
15426                         }
15427                     },
15428                     {
15429                         tag: 'div',
15430                         cls: 'col-xs-6 text-right',
15431                         cn: {
15432                             tag: 'button',
15433                             cls: 'btn btn-success roo-touch-view-ok',
15434                             html: 'OK'
15435                         }
15436                     }
15437                 ]
15438             }
15439         ]
15440         
15441     }
15442 });
15443
15444 Roo.apply(Roo.bootstrap.ComboBox,  {
15445     
15446     touchViewTemplate : {
15447         tag: 'div',
15448         cls: 'modal fade roo-combobox-touch-view',
15449         cn: [
15450             {
15451                 tag: 'div',
15452                 cls: 'modal-dialog',
15453                 style : 'position:fixed', // we have to fix position....
15454                 cn: [
15455                     {
15456                         tag: 'div',
15457                         cls: 'modal-content',
15458                         cn: [
15459                             Roo.bootstrap.ComboBox.header,
15460                             Roo.bootstrap.ComboBox.body,
15461                             Roo.bootstrap.ComboBox.footer
15462                         ]
15463                     }
15464                 ]
15465             }
15466         ]
15467     }
15468 });/*
15469  * Based on:
15470  * Ext JS Library 1.1.1
15471  * Copyright(c) 2006-2007, Ext JS, LLC.
15472  *
15473  * Originally Released Under LGPL - original licence link has changed is not relivant.
15474  *
15475  * Fork - LGPL
15476  * <script type="text/javascript">
15477  */
15478
15479 /**
15480  * @class Roo.View
15481  * @extends Roo.util.Observable
15482  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15483  * This class also supports single and multi selection modes. <br>
15484  * Create a data model bound view:
15485  <pre><code>
15486  var store = new Roo.data.Store(...);
15487
15488  var view = new Roo.View({
15489     el : "my-element",
15490     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15491  
15492     singleSelect: true,
15493     selectedClass: "ydataview-selected",
15494     store: store
15495  });
15496
15497  // listen for node click?
15498  view.on("click", function(vw, index, node, e){
15499  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15500  });
15501
15502  // load XML data
15503  dataModel.load("foobar.xml");
15504  </code></pre>
15505  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15506  * <br><br>
15507  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15508  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15509  * 
15510  * Note: old style constructor is still suported (container, template, config)
15511  * 
15512  * @constructor
15513  * Create a new View
15514  * @param {Object} config The config object
15515  * 
15516  */
15517 Roo.View = function(config, depreciated_tpl, depreciated_config){
15518     
15519     this.parent = false;
15520     
15521     if (typeof(depreciated_tpl) == 'undefined') {
15522         // new way.. - universal constructor.
15523         Roo.apply(this, config);
15524         this.el  = Roo.get(this.el);
15525     } else {
15526         // old format..
15527         this.el  = Roo.get(config);
15528         this.tpl = depreciated_tpl;
15529         Roo.apply(this, depreciated_config);
15530     }
15531     this.wrapEl  = this.el.wrap().wrap();
15532     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15533     
15534     
15535     if(typeof(this.tpl) == "string"){
15536         this.tpl = new Roo.Template(this.tpl);
15537     } else {
15538         // support xtype ctors..
15539         this.tpl = new Roo.factory(this.tpl, Roo);
15540     }
15541     
15542     
15543     this.tpl.compile();
15544     
15545     /** @private */
15546     this.addEvents({
15547         /**
15548          * @event beforeclick
15549          * Fires before a click is processed. Returns false to cancel the default action.
15550          * @param {Roo.View} this
15551          * @param {Number} index The index of the target node
15552          * @param {HTMLElement} node The target node
15553          * @param {Roo.EventObject} e The raw event object
15554          */
15555             "beforeclick" : true,
15556         /**
15557          * @event click
15558          * Fires when a template node is clicked.
15559          * @param {Roo.View} this
15560          * @param {Number} index The index of the target node
15561          * @param {HTMLElement} node The target node
15562          * @param {Roo.EventObject} e The raw event object
15563          */
15564             "click" : true,
15565         /**
15566          * @event dblclick
15567          * Fires when a template node is double clicked.
15568          * @param {Roo.View} this
15569          * @param {Number} index The index of the target node
15570          * @param {HTMLElement} node The target node
15571          * @param {Roo.EventObject} e The raw event object
15572          */
15573             "dblclick" : true,
15574         /**
15575          * @event contextmenu
15576          * Fires when a template node is right clicked.
15577          * @param {Roo.View} this
15578          * @param {Number} index The index of the target node
15579          * @param {HTMLElement} node The target node
15580          * @param {Roo.EventObject} e The raw event object
15581          */
15582             "contextmenu" : true,
15583         /**
15584          * @event selectionchange
15585          * Fires when the selected nodes change.
15586          * @param {Roo.View} this
15587          * @param {Array} selections Array of the selected nodes
15588          */
15589             "selectionchange" : true,
15590     
15591         /**
15592          * @event beforeselect
15593          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15594          * @param {Roo.View} this
15595          * @param {HTMLElement} node The node to be selected
15596          * @param {Array} selections Array of currently selected nodes
15597          */
15598             "beforeselect" : true,
15599         /**
15600          * @event preparedata
15601          * Fires on every row to render, to allow you to change the data.
15602          * @param {Roo.View} this
15603          * @param {Object} data to be rendered (change this)
15604          */
15605           "preparedata" : true
15606           
15607           
15608         });
15609
15610
15611
15612     this.el.on({
15613         "click": this.onClick,
15614         "dblclick": this.onDblClick,
15615         "contextmenu": this.onContextMenu,
15616         scope:this
15617     });
15618
15619     this.selections = [];
15620     this.nodes = [];
15621     this.cmp = new Roo.CompositeElementLite([]);
15622     if(this.store){
15623         this.store = Roo.factory(this.store, Roo.data);
15624         this.setStore(this.store, true);
15625     }
15626     
15627     if ( this.footer && this.footer.xtype) {
15628            
15629          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15630         
15631         this.footer.dataSource = this.store;
15632         this.footer.container = fctr;
15633         this.footer = Roo.factory(this.footer, Roo);
15634         fctr.insertFirst(this.el);
15635         
15636         // this is a bit insane - as the paging toolbar seems to detach the el..
15637 //        dom.parentNode.parentNode.parentNode
15638          // they get detached?
15639     }
15640     
15641     
15642     Roo.View.superclass.constructor.call(this);
15643     
15644     
15645 };
15646
15647 Roo.extend(Roo.View, Roo.util.Observable, {
15648     
15649      /**
15650      * @cfg {Roo.data.Store} store Data store to load data from.
15651      */
15652     store : false,
15653     
15654     /**
15655      * @cfg {String|Roo.Element} el The container element.
15656      */
15657     el : '',
15658     
15659     /**
15660      * @cfg {String|Roo.Template} tpl The template used by this View 
15661      */
15662     tpl : false,
15663     /**
15664      * @cfg {String} dataName the named area of the template to use as the data area
15665      *                          Works with domtemplates roo-name="name"
15666      */
15667     dataName: false,
15668     /**
15669      * @cfg {String} selectedClass The css class to add to selected nodes
15670      */
15671     selectedClass : "x-view-selected",
15672      /**
15673      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15674      */
15675     emptyText : "",
15676     
15677     /**
15678      * @cfg {String} text to display on mask (default Loading)
15679      */
15680     mask : false,
15681     /**
15682      * @cfg {Boolean} multiSelect Allow multiple selection
15683      */
15684     multiSelect : false,
15685     /**
15686      * @cfg {Boolean} singleSelect Allow single selection
15687      */
15688     singleSelect:  false,
15689     
15690     /**
15691      * @cfg {Boolean} toggleSelect - selecting 
15692      */
15693     toggleSelect : false,
15694     
15695     /**
15696      * @cfg {Boolean} tickable - selecting 
15697      */
15698     tickable : false,
15699     
15700     /**
15701      * Returns the element this view is bound to.
15702      * @return {Roo.Element}
15703      */
15704     getEl : function(){
15705         return this.wrapEl;
15706     },
15707     
15708     
15709
15710     /**
15711      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15712      */
15713     refresh : function(){
15714         //Roo.log('refresh');
15715         var t = this.tpl;
15716         
15717         // if we are using something like 'domtemplate', then
15718         // the what gets used is:
15719         // t.applySubtemplate(NAME, data, wrapping data..)
15720         // the outer template then get' applied with
15721         //     the store 'extra data'
15722         // and the body get's added to the
15723         //      roo-name="data" node?
15724         //      <span class='roo-tpl-{name}'></span> ?????
15725         
15726         
15727         
15728         this.clearSelections();
15729         this.el.update("");
15730         var html = [];
15731         var records = this.store.getRange();
15732         if(records.length < 1) {
15733             
15734             // is this valid??  = should it render a template??
15735             
15736             this.el.update(this.emptyText);
15737             return;
15738         }
15739         var el = this.el;
15740         if (this.dataName) {
15741             this.el.update(t.apply(this.store.meta)); //????
15742             el = this.el.child('.roo-tpl-' + this.dataName);
15743         }
15744         
15745         for(var i = 0, len = records.length; i < len; i++){
15746             var data = this.prepareData(records[i].data, i, records[i]);
15747             this.fireEvent("preparedata", this, data, i, records[i]);
15748             
15749             var d = Roo.apply({}, data);
15750             
15751             if(this.tickable){
15752                 Roo.apply(d, {'roo-id' : Roo.id()});
15753                 
15754                 var _this = this;
15755             
15756                 Roo.each(this.parent.item, function(item){
15757                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15758                         return;
15759                     }
15760                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15761                 });
15762             }
15763             
15764             html[html.length] = Roo.util.Format.trim(
15765                 this.dataName ?
15766                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15767                     t.apply(d)
15768             );
15769         }
15770         
15771         
15772         
15773         el.update(html.join(""));
15774         this.nodes = el.dom.childNodes;
15775         this.updateIndexes(0);
15776     },
15777     
15778
15779     /**
15780      * Function to override to reformat the data that is sent to
15781      * the template for each node.
15782      * DEPRICATED - use the preparedata event handler.
15783      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15784      * a JSON object for an UpdateManager bound view).
15785      */
15786     prepareData : function(data, index, record)
15787     {
15788         this.fireEvent("preparedata", this, data, index, record);
15789         return data;
15790     },
15791
15792     onUpdate : function(ds, record){
15793         // Roo.log('on update');   
15794         this.clearSelections();
15795         var index = this.store.indexOf(record);
15796         var n = this.nodes[index];
15797         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15798         n.parentNode.removeChild(n);
15799         this.updateIndexes(index, index);
15800     },
15801
15802     
15803     
15804 // --------- FIXME     
15805     onAdd : function(ds, records, index)
15806     {
15807         //Roo.log(['on Add', ds, records, index] );        
15808         this.clearSelections();
15809         if(this.nodes.length == 0){
15810             this.refresh();
15811             return;
15812         }
15813         var n = this.nodes[index];
15814         for(var i = 0, len = records.length; i < len; i++){
15815             var d = this.prepareData(records[i].data, i, records[i]);
15816             if(n){
15817                 this.tpl.insertBefore(n, d);
15818             }else{
15819                 
15820                 this.tpl.append(this.el, d);
15821             }
15822         }
15823         this.updateIndexes(index);
15824     },
15825
15826     onRemove : function(ds, record, index){
15827        // Roo.log('onRemove');
15828         this.clearSelections();
15829         var el = this.dataName  ?
15830             this.el.child('.roo-tpl-' + this.dataName) :
15831             this.el; 
15832         
15833         el.dom.removeChild(this.nodes[index]);
15834         this.updateIndexes(index);
15835     },
15836
15837     /**
15838      * Refresh an individual node.
15839      * @param {Number} index
15840      */
15841     refreshNode : function(index){
15842         this.onUpdate(this.store, this.store.getAt(index));
15843     },
15844
15845     updateIndexes : function(startIndex, endIndex){
15846         var ns = this.nodes;
15847         startIndex = startIndex || 0;
15848         endIndex = endIndex || ns.length - 1;
15849         for(var i = startIndex; i <= endIndex; i++){
15850             ns[i].nodeIndex = i;
15851         }
15852     },
15853
15854     /**
15855      * Changes the data store this view uses and refresh the view.
15856      * @param {Store} store
15857      */
15858     setStore : function(store, initial){
15859         if(!initial && this.store){
15860             this.store.un("datachanged", this.refresh);
15861             this.store.un("add", this.onAdd);
15862             this.store.un("remove", this.onRemove);
15863             this.store.un("update", this.onUpdate);
15864             this.store.un("clear", this.refresh);
15865             this.store.un("beforeload", this.onBeforeLoad);
15866             this.store.un("load", this.onLoad);
15867             this.store.un("loadexception", this.onLoad);
15868         }
15869         if(store){
15870           
15871             store.on("datachanged", this.refresh, this);
15872             store.on("add", this.onAdd, this);
15873             store.on("remove", this.onRemove, this);
15874             store.on("update", this.onUpdate, this);
15875             store.on("clear", this.refresh, this);
15876             store.on("beforeload", this.onBeforeLoad, this);
15877             store.on("load", this.onLoad, this);
15878             store.on("loadexception", this.onLoad, this);
15879         }
15880         
15881         if(store){
15882             this.refresh();
15883         }
15884     },
15885     /**
15886      * onbeforeLoad - masks the loading area.
15887      *
15888      */
15889     onBeforeLoad : function(store,opts)
15890     {
15891          //Roo.log('onBeforeLoad');   
15892         if (!opts.add) {
15893             this.el.update("");
15894         }
15895         this.el.mask(this.mask ? this.mask : "Loading" ); 
15896     },
15897     onLoad : function ()
15898     {
15899         this.el.unmask();
15900     },
15901     
15902
15903     /**
15904      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15905      * @param {HTMLElement} node
15906      * @return {HTMLElement} The template node
15907      */
15908     findItemFromChild : function(node){
15909         var el = this.dataName  ?
15910             this.el.child('.roo-tpl-' + this.dataName,true) :
15911             this.el.dom; 
15912         
15913         if(!node || node.parentNode == el){
15914                     return node;
15915             }
15916             var p = node.parentNode;
15917             while(p && p != el){
15918             if(p.parentNode == el){
15919                 return p;
15920             }
15921             p = p.parentNode;
15922         }
15923             return null;
15924     },
15925
15926     /** @ignore */
15927     onClick : function(e){
15928         var item = this.findItemFromChild(e.getTarget());
15929         if(item){
15930             var index = this.indexOf(item);
15931             if(this.onItemClick(item, index, e) !== false){
15932                 this.fireEvent("click", this, index, item, e);
15933             }
15934         }else{
15935             this.clearSelections();
15936         }
15937     },
15938
15939     /** @ignore */
15940     onContextMenu : function(e){
15941         var item = this.findItemFromChild(e.getTarget());
15942         if(item){
15943             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15944         }
15945     },
15946
15947     /** @ignore */
15948     onDblClick : function(e){
15949         var item = this.findItemFromChild(e.getTarget());
15950         if(item){
15951             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15952         }
15953     },
15954
15955     onItemClick : function(item, index, e)
15956     {
15957         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15958             return false;
15959         }
15960         if (this.toggleSelect) {
15961             var m = this.isSelected(item) ? 'unselect' : 'select';
15962             //Roo.log(m);
15963             var _t = this;
15964             _t[m](item, true, false);
15965             return true;
15966         }
15967         if(this.multiSelect || this.singleSelect){
15968             if(this.multiSelect && e.shiftKey && this.lastSelection){
15969                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15970             }else{
15971                 this.select(item, this.multiSelect && e.ctrlKey);
15972                 this.lastSelection = item;
15973             }
15974             
15975             if(!this.tickable){
15976                 e.preventDefault();
15977             }
15978             
15979         }
15980         return true;
15981     },
15982
15983     /**
15984      * Get the number of selected nodes.
15985      * @return {Number}
15986      */
15987     getSelectionCount : function(){
15988         return this.selections.length;
15989     },
15990
15991     /**
15992      * Get the currently selected nodes.
15993      * @return {Array} An array of HTMLElements
15994      */
15995     getSelectedNodes : function(){
15996         return this.selections;
15997     },
15998
15999     /**
16000      * Get the indexes of the selected nodes.
16001      * @return {Array}
16002      */
16003     getSelectedIndexes : function(){
16004         var indexes = [], s = this.selections;
16005         for(var i = 0, len = s.length; i < len; i++){
16006             indexes.push(s[i].nodeIndex);
16007         }
16008         return indexes;
16009     },
16010
16011     /**
16012      * Clear all selections
16013      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16014      */
16015     clearSelections : function(suppressEvent){
16016         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16017             this.cmp.elements = this.selections;
16018             this.cmp.removeClass(this.selectedClass);
16019             this.selections = [];
16020             if(!suppressEvent){
16021                 this.fireEvent("selectionchange", this, this.selections);
16022             }
16023         }
16024     },
16025
16026     /**
16027      * Returns true if the passed node is selected
16028      * @param {HTMLElement/Number} node The node or node index
16029      * @return {Boolean}
16030      */
16031     isSelected : function(node){
16032         var s = this.selections;
16033         if(s.length < 1){
16034             return false;
16035         }
16036         node = this.getNode(node);
16037         return s.indexOf(node) !== -1;
16038     },
16039
16040     /**
16041      * Selects nodes.
16042      * @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
16043      * @param {Boolean} keepExisting (optional) true to keep existing selections
16044      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16045      */
16046     select : function(nodeInfo, keepExisting, suppressEvent){
16047         if(nodeInfo instanceof Array){
16048             if(!keepExisting){
16049                 this.clearSelections(true);
16050             }
16051             for(var i = 0, len = nodeInfo.length; i < len; i++){
16052                 this.select(nodeInfo[i], true, true);
16053             }
16054             return;
16055         } 
16056         var node = this.getNode(nodeInfo);
16057         if(!node || this.isSelected(node)){
16058             return; // already selected.
16059         }
16060         if(!keepExisting){
16061             this.clearSelections(true);
16062         }
16063         
16064         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16065             Roo.fly(node).addClass(this.selectedClass);
16066             this.selections.push(node);
16067             if(!suppressEvent){
16068                 this.fireEvent("selectionchange", this, this.selections);
16069             }
16070         }
16071         
16072         
16073     },
16074       /**
16075      * Unselects nodes.
16076      * @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
16077      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16078      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16079      */
16080     unselect : function(nodeInfo, keepExisting, suppressEvent)
16081     {
16082         if(nodeInfo instanceof Array){
16083             Roo.each(this.selections, function(s) {
16084                 this.unselect(s, nodeInfo);
16085             }, this);
16086             return;
16087         }
16088         var node = this.getNode(nodeInfo);
16089         if(!node || !this.isSelected(node)){
16090             //Roo.log("not selected");
16091             return; // not selected.
16092         }
16093         // fireevent???
16094         var ns = [];
16095         Roo.each(this.selections, function(s) {
16096             if (s == node ) {
16097                 Roo.fly(node).removeClass(this.selectedClass);
16098
16099                 return;
16100             }
16101             ns.push(s);
16102         },this);
16103         
16104         this.selections= ns;
16105         this.fireEvent("selectionchange", this, this.selections);
16106     },
16107
16108     /**
16109      * Gets a template node.
16110      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16111      * @return {HTMLElement} The node or null if it wasn't found
16112      */
16113     getNode : function(nodeInfo){
16114         if(typeof nodeInfo == "string"){
16115             return document.getElementById(nodeInfo);
16116         }else if(typeof nodeInfo == "number"){
16117             return this.nodes[nodeInfo];
16118         }
16119         return nodeInfo;
16120     },
16121
16122     /**
16123      * Gets a range template nodes.
16124      * @param {Number} startIndex
16125      * @param {Number} endIndex
16126      * @return {Array} An array of nodes
16127      */
16128     getNodes : function(start, end){
16129         var ns = this.nodes;
16130         start = start || 0;
16131         end = typeof end == "undefined" ? ns.length - 1 : end;
16132         var nodes = [];
16133         if(start <= end){
16134             for(var i = start; i <= end; i++){
16135                 nodes.push(ns[i]);
16136             }
16137         } else{
16138             for(var i = start; i >= end; i--){
16139                 nodes.push(ns[i]);
16140             }
16141         }
16142         return nodes;
16143     },
16144
16145     /**
16146      * Finds the index of the passed node
16147      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16148      * @return {Number} The index of the node or -1
16149      */
16150     indexOf : function(node){
16151         node = this.getNode(node);
16152         if(typeof node.nodeIndex == "number"){
16153             return node.nodeIndex;
16154         }
16155         var ns = this.nodes;
16156         for(var i = 0, len = ns.length; i < len; i++){
16157             if(ns[i] == node){
16158                 return i;
16159             }
16160         }
16161         return -1;
16162     }
16163 });
16164 /*
16165  * - LGPL
16166  *
16167  * based on jquery fullcalendar
16168  * 
16169  */
16170
16171 Roo.bootstrap = Roo.bootstrap || {};
16172 /**
16173  * @class Roo.bootstrap.Calendar
16174  * @extends Roo.bootstrap.Component
16175  * Bootstrap Calendar class
16176  * @cfg {Boolean} loadMask (true|false) default false
16177  * @cfg {Object} header generate the user specific header of the calendar, default false
16178
16179  * @constructor
16180  * Create a new Container
16181  * @param {Object} config The config object
16182  */
16183
16184
16185
16186 Roo.bootstrap.Calendar = function(config){
16187     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16188      this.addEvents({
16189         /**
16190              * @event select
16191              * Fires when a date is selected
16192              * @param {DatePicker} this
16193              * @param {Date} date The selected date
16194              */
16195         'select': true,
16196         /**
16197              * @event monthchange
16198              * Fires when the displayed month changes 
16199              * @param {DatePicker} this
16200              * @param {Date} date The selected month
16201              */
16202         'monthchange': true,
16203         /**
16204              * @event evententer
16205              * Fires when mouse over an event
16206              * @param {Calendar} this
16207              * @param {event} Event
16208              */
16209         'evententer': true,
16210         /**
16211              * @event eventleave
16212              * Fires when the mouse leaves an
16213              * @param {Calendar} this
16214              * @param {event}
16215              */
16216         'eventleave': true,
16217         /**
16218              * @event eventclick
16219              * Fires when the mouse click an
16220              * @param {Calendar} this
16221              * @param {event}
16222              */
16223         'eventclick': true
16224         
16225     });
16226
16227 };
16228
16229 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16230     
16231      /**
16232      * @cfg {Number} startDay
16233      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16234      */
16235     startDay : 0,
16236     
16237     loadMask : false,
16238     
16239     header : false,
16240       
16241     getAutoCreate : function(){
16242         
16243         
16244         var fc_button = function(name, corner, style, content ) {
16245             return Roo.apply({},{
16246                 tag : 'span',
16247                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16248                          (corner.length ?
16249                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16250                             ''
16251                         ),
16252                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16253                 unselectable: 'on'
16254             });
16255         };
16256         
16257         var header = {};
16258         
16259         if(!this.header){
16260             header = {
16261                 tag : 'table',
16262                 cls : 'fc-header',
16263                 style : 'width:100%',
16264                 cn : [
16265                     {
16266                         tag: 'tr',
16267                         cn : [
16268                             {
16269                                 tag : 'td',
16270                                 cls : 'fc-header-left',
16271                                 cn : [
16272                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16273                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16274                                     { tag: 'span', cls: 'fc-header-space' },
16275                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16276
16277
16278                                 ]
16279                             },
16280
16281                             {
16282                                 tag : 'td',
16283                                 cls : 'fc-header-center',
16284                                 cn : [
16285                                     {
16286                                         tag: 'span',
16287                                         cls: 'fc-header-title',
16288                                         cn : {
16289                                             tag: 'H2',
16290                                             html : 'month / year'
16291                                         }
16292                                     }
16293
16294                                 ]
16295                             },
16296                             {
16297                                 tag : 'td',
16298                                 cls : 'fc-header-right',
16299                                 cn : [
16300                               /*      fc_button('month', 'left', '', 'month' ),
16301                                     fc_button('week', '', '', 'week' ),
16302                                     fc_button('day', 'right', '', 'day' )
16303                                 */    
16304
16305                                 ]
16306                             }
16307
16308                         ]
16309                     }
16310                 ]
16311             };
16312         }
16313         
16314         header = this.header;
16315         
16316        
16317         var cal_heads = function() {
16318             var ret = [];
16319             // fixme - handle this.
16320             
16321             for (var i =0; i < Date.dayNames.length; i++) {
16322                 var d = Date.dayNames[i];
16323                 ret.push({
16324                     tag: 'th',
16325                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16326                     html : d.substring(0,3)
16327                 });
16328                 
16329             }
16330             ret[0].cls += ' fc-first';
16331             ret[6].cls += ' fc-last';
16332             return ret;
16333         };
16334         var cal_cell = function(n) {
16335             return  {
16336                 tag: 'td',
16337                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16338                 cn : [
16339                     {
16340                         cn : [
16341                             {
16342                                 cls: 'fc-day-number',
16343                                 html: 'D'
16344                             },
16345                             {
16346                                 cls: 'fc-day-content',
16347                              
16348                                 cn : [
16349                                      {
16350                                         style: 'position: relative;' // height: 17px;
16351                                     }
16352                                 ]
16353                             }
16354                             
16355                             
16356                         ]
16357                     }
16358                 ]
16359                 
16360             }
16361         };
16362         var cal_rows = function() {
16363             
16364             var ret = [];
16365             for (var r = 0; r < 6; r++) {
16366                 var row= {
16367                     tag : 'tr',
16368                     cls : 'fc-week',
16369                     cn : []
16370                 };
16371                 
16372                 for (var i =0; i < Date.dayNames.length; i++) {
16373                     var d = Date.dayNames[i];
16374                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16375
16376                 }
16377                 row.cn[0].cls+=' fc-first';
16378                 row.cn[0].cn[0].style = 'min-height:90px';
16379                 row.cn[6].cls+=' fc-last';
16380                 ret.push(row);
16381                 
16382             }
16383             ret[0].cls += ' fc-first';
16384             ret[4].cls += ' fc-prev-last';
16385             ret[5].cls += ' fc-last';
16386             return ret;
16387             
16388         };
16389         
16390         var cal_table = {
16391             tag: 'table',
16392             cls: 'fc-border-separate',
16393             style : 'width:100%',
16394             cellspacing  : 0,
16395             cn : [
16396                 { 
16397                     tag: 'thead',
16398                     cn : [
16399                         { 
16400                             tag: 'tr',
16401                             cls : 'fc-first fc-last',
16402                             cn : cal_heads()
16403                         }
16404                     ]
16405                 },
16406                 { 
16407                     tag: 'tbody',
16408                     cn : cal_rows()
16409                 }
16410                   
16411             ]
16412         };
16413          
16414          var cfg = {
16415             cls : 'fc fc-ltr',
16416             cn : [
16417                 header,
16418                 {
16419                     cls : 'fc-content',
16420                     style : "position: relative;",
16421                     cn : [
16422                         {
16423                             cls : 'fc-view fc-view-month fc-grid',
16424                             style : 'position: relative',
16425                             unselectable : 'on',
16426                             cn : [
16427                                 {
16428                                     cls : 'fc-event-container',
16429                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16430                                 },
16431                                 cal_table
16432                             ]
16433                         }
16434                     ]
16435     
16436                 }
16437            ] 
16438             
16439         };
16440         
16441          
16442         
16443         return cfg;
16444     },
16445     
16446     
16447     initEvents : function()
16448     {
16449         if(!this.store){
16450             throw "can not find store for calendar";
16451         }
16452         
16453         var mark = {
16454             tag: "div",
16455             cls:"x-dlg-mask",
16456             style: "text-align:center",
16457             cn: [
16458                 {
16459                     tag: "div",
16460                     style: "background-color:white;width:50%;margin:250 auto",
16461                     cn: [
16462                         {
16463                             tag: "img",
16464                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16465                         },
16466                         {
16467                             tag: "span",
16468                             html: "Loading"
16469                         }
16470                         
16471                     ]
16472                 }
16473             ]
16474         };
16475         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16476         
16477         var size = this.el.select('.fc-content', true).first().getSize();
16478         this.maskEl.setSize(size.width, size.height);
16479         this.maskEl.enableDisplayMode("block");
16480         if(!this.loadMask){
16481             this.maskEl.hide();
16482         }
16483         
16484         this.store = Roo.factory(this.store, Roo.data);
16485         this.store.on('load', this.onLoad, this);
16486         this.store.on('beforeload', this.onBeforeLoad, this);
16487         
16488         this.resize();
16489         
16490         this.cells = this.el.select('.fc-day',true);
16491         //Roo.log(this.cells);
16492         this.textNodes = this.el.query('.fc-day-number');
16493         this.cells.addClassOnOver('fc-state-hover');
16494         
16495         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16496         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16497         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16498         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16499         
16500         this.on('monthchange', this.onMonthChange, this);
16501         
16502         this.update(new Date().clearTime());
16503     },
16504     
16505     resize : function() {
16506         var sz  = this.el.getSize();
16507         
16508         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16509         this.el.select('.fc-day-content div',true).setHeight(34);
16510     },
16511     
16512     
16513     // private
16514     showPrevMonth : function(e){
16515         this.update(this.activeDate.add("mo", -1));
16516     },
16517     showToday : function(e){
16518         this.update(new Date().clearTime());
16519     },
16520     // private
16521     showNextMonth : function(e){
16522         this.update(this.activeDate.add("mo", 1));
16523     },
16524
16525     // private
16526     showPrevYear : function(){
16527         this.update(this.activeDate.add("y", -1));
16528     },
16529
16530     // private
16531     showNextYear : function(){
16532         this.update(this.activeDate.add("y", 1));
16533     },
16534
16535     
16536    // private
16537     update : function(date)
16538     {
16539         var vd = this.activeDate;
16540         this.activeDate = date;
16541 //        if(vd && this.el){
16542 //            var t = date.getTime();
16543 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16544 //                Roo.log('using add remove');
16545 //                
16546 //                this.fireEvent('monthchange', this, date);
16547 //                
16548 //                this.cells.removeClass("fc-state-highlight");
16549 //                this.cells.each(function(c){
16550 //                   if(c.dateValue == t){
16551 //                       c.addClass("fc-state-highlight");
16552 //                       setTimeout(function(){
16553 //                            try{c.dom.firstChild.focus();}catch(e){}
16554 //                       }, 50);
16555 //                       return false;
16556 //                   }
16557 //                   return true;
16558 //                });
16559 //                return;
16560 //            }
16561 //        }
16562         
16563         var days = date.getDaysInMonth();
16564         
16565         var firstOfMonth = date.getFirstDateOfMonth();
16566         var startingPos = firstOfMonth.getDay()-this.startDay;
16567         
16568         if(startingPos < this.startDay){
16569             startingPos += 7;
16570         }
16571         
16572         var pm = date.add(Date.MONTH, -1);
16573         var prevStart = pm.getDaysInMonth()-startingPos;
16574 //        
16575         this.cells = this.el.select('.fc-day',true);
16576         this.textNodes = this.el.query('.fc-day-number');
16577         this.cells.addClassOnOver('fc-state-hover');
16578         
16579         var cells = this.cells.elements;
16580         var textEls = this.textNodes;
16581         
16582         Roo.each(cells, function(cell){
16583             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16584         });
16585         
16586         days += startingPos;
16587
16588         // convert everything to numbers so it's fast
16589         var day = 86400000;
16590         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16591         //Roo.log(d);
16592         //Roo.log(pm);
16593         //Roo.log(prevStart);
16594         
16595         var today = new Date().clearTime().getTime();
16596         var sel = date.clearTime().getTime();
16597         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16598         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16599         var ddMatch = this.disabledDatesRE;
16600         var ddText = this.disabledDatesText;
16601         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16602         var ddaysText = this.disabledDaysText;
16603         var format = this.format;
16604         
16605         var setCellClass = function(cal, cell){
16606             cell.row = 0;
16607             cell.events = [];
16608             cell.more = [];
16609             //Roo.log('set Cell Class');
16610             cell.title = "";
16611             var t = d.getTime();
16612             
16613             //Roo.log(d);
16614             
16615             cell.dateValue = t;
16616             if(t == today){
16617                 cell.className += " fc-today";
16618                 cell.className += " fc-state-highlight";
16619                 cell.title = cal.todayText;
16620             }
16621             if(t == sel){
16622                 // disable highlight in other month..
16623                 //cell.className += " fc-state-highlight";
16624                 
16625             }
16626             // disabling
16627             if(t < min) {
16628                 cell.className = " fc-state-disabled";
16629                 cell.title = cal.minText;
16630                 return;
16631             }
16632             if(t > max) {
16633                 cell.className = " fc-state-disabled";
16634                 cell.title = cal.maxText;
16635                 return;
16636             }
16637             if(ddays){
16638                 if(ddays.indexOf(d.getDay()) != -1){
16639                     cell.title = ddaysText;
16640                     cell.className = " fc-state-disabled";
16641                 }
16642             }
16643             if(ddMatch && format){
16644                 var fvalue = d.dateFormat(format);
16645                 if(ddMatch.test(fvalue)){
16646                     cell.title = ddText.replace("%0", fvalue);
16647                     cell.className = " fc-state-disabled";
16648                 }
16649             }
16650             
16651             if (!cell.initialClassName) {
16652                 cell.initialClassName = cell.dom.className;
16653             }
16654             
16655             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16656         };
16657
16658         var i = 0;
16659         
16660         for(; i < startingPos; i++) {
16661             textEls[i].innerHTML = (++prevStart);
16662             d.setDate(d.getDate()+1);
16663             
16664             cells[i].className = "fc-past fc-other-month";
16665             setCellClass(this, cells[i]);
16666         }
16667         
16668         var intDay = 0;
16669         
16670         for(; i < days; i++){
16671             intDay = i - startingPos + 1;
16672             textEls[i].innerHTML = (intDay);
16673             d.setDate(d.getDate()+1);
16674             
16675             cells[i].className = ''; // "x-date-active";
16676             setCellClass(this, cells[i]);
16677         }
16678         var extraDays = 0;
16679         
16680         for(; i < 42; i++) {
16681             textEls[i].innerHTML = (++extraDays);
16682             d.setDate(d.getDate()+1);
16683             
16684             cells[i].className = "fc-future fc-other-month";
16685             setCellClass(this, cells[i]);
16686         }
16687         
16688         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16689         
16690         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16691         
16692         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16693         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16694         
16695         if(totalRows != 6){
16696             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16697             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16698         }
16699         
16700         this.fireEvent('monthchange', this, date);
16701         
16702         
16703         /*
16704         if(!this.internalRender){
16705             var main = this.el.dom.firstChild;
16706             var w = main.offsetWidth;
16707             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16708             Roo.fly(main).setWidth(w);
16709             this.internalRender = true;
16710             // opera does not respect the auto grow header center column
16711             // then, after it gets a width opera refuses to recalculate
16712             // without a second pass
16713             if(Roo.isOpera && !this.secondPass){
16714                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16715                 this.secondPass = true;
16716                 this.update.defer(10, this, [date]);
16717             }
16718         }
16719         */
16720         
16721     },
16722     
16723     findCell : function(dt) {
16724         dt = dt.clearTime().getTime();
16725         var ret = false;
16726         this.cells.each(function(c){
16727             //Roo.log("check " +c.dateValue + '?=' + dt);
16728             if(c.dateValue == dt){
16729                 ret = c;
16730                 return false;
16731             }
16732             return true;
16733         });
16734         
16735         return ret;
16736     },
16737     
16738     findCells : function(ev) {
16739         var s = ev.start.clone().clearTime().getTime();
16740        // Roo.log(s);
16741         var e= ev.end.clone().clearTime().getTime();
16742        // Roo.log(e);
16743         var ret = [];
16744         this.cells.each(function(c){
16745              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16746             
16747             if(c.dateValue > e){
16748                 return ;
16749             }
16750             if(c.dateValue < s){
16751                 return ;
16752             }
16753             ret.push(c);
16754         });
16755         
16756         return ret;    
16757     },
16758     
16759 //    findBestRow: function(cells)
16760 //    {
16761 //        var ret = 0;
16762 //        
16763 //        for (var i =0 ; i < cells.length;i++) {
16764 //            ret  = Math.max(cells[i].rows || 0,ret);
16765 //        }
16766 //        return ret;
16767 //        
16768 //    },
16769     
16770     
16771     addItem : function(ev)
16772     {
16773         // look for vertical location slot in
16774         var cells = this.findCells(ev);
16775         
16776 //        ev.row = this.findBestRow(cells);
16777         
16778         // work out the location.
16779         
16780         var crow = false;
16781         var rows = [];
16782         for(var i =0; i < cells.length; i++) {
16783             
16784             cells[i].row = cells[0].row;
16785             
16786             if(i == 0){
16787                 cells[i].row = cells[i].row + 1;
16788             }
16789             
16790             if (!crow) {
16791                 crow = {
16792                     start : cells[i],
16793                     end :  cells[i]
16794                 };
16795                 continue;
16796             }
16797             if (crow.start.getY() == cells[i].getY()) {
16798                 // on same row.
16799                 crow.end = cells[i];
16800                 continue;
16801             }
16802             // different row.
16803             rows.push(crow);
16804             crow = {
16805                 start: cells[i],
16806                 end : cells[i]
16807             };
16808             
16809         }
16810         
16811         rows.push(crow);
16812         ev.els = [];
16813         ev.rows = rows;
16814         ev.cells = cells;
16815         
16816         cells[0].events.push(ev);
16817         
16818         this.calevents.push(ev);
16819     },
16820     
16821     clearEvents: function() {
16822         
16823         if(!this.calevents){
16824             return;
16825         }
16826         
16827         Roo.each(this.cells.elements, function(c){
16828             c.row = 0;
16829             c.events = [];
16830             c.more = [];
16831         });
16832         
16833         Roo.each(this.calevents, function(e) {
16834             Roo.each(e.els, function(el) {
16835                 el.un('mouseenter' ,this.onEventEnter, this);
16836                 el.un('mouseleave' ,this.onEventLeave, this);
16837                 el.remove();
16838             },this);
16839         },this);
16840         
16841         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16842             e.remove();
16843         });
16844         
16845     },
16846     
16847     renderEvents: function()
16848     {   
16849         var _this = this;
16850         
16851         this.cells.each(function(c) {
16852             
16853             if(c.row < 5){
16854                 return;
16855             }
16856             
16857             var ev = c.events;
16858             
16859             var r = 4;
16860             if(c.row != c.events.length){
16861                 r = 4 - (4 - (c.row - c.events.length));
16862             }
16863             
16864             c.events = ev.slice(0, r);
16865             c.more = ev.slice(r);
16866             
16867             if(c.more.length && c.more.length == 1){
16868                 c.events.push(c.more.pop());
16869             }
16870             
16871             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16872             
16873         });
16874             
16875         this.cells.each(function(c) {
16876             
16877             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16878             
16879             
16880             for (var e = 0; e < c.events.length; e++){
16881                 var ev = c.events[e];
16882                 var rows = ev.rows;
16883                 
16884                 for(var i = 0; i < rows.length; i++) {
16885                 
16886                     // how many rows should it span..
16887
16888                     var  cfg = {
16889                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16890                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16891
16892                         unselectable : "on",
16893                         cn : [
16894                             {
16895                                 cls: 'fc-event-inner',
16896                                 cn : [
16897     //                                {
16898     //                                  tag:'span',
16899     //                                  cls: 'fc-event-time',
16900     //                                  html : cells.length > 1 ? '' : ev.time
16901     //                                },
16902                                     {
16903                                       tag:'span',
16904                                       cls: 'fc-event-title',
16905                                       html : String.format('{0}', ev.title)
16906                                     }
16907
16908
16909                                 ]
16910                             },
16911                             {
16912                                 cls: 'ui-resizable-handle ui-resizable-e',
16913                                 html : '&nbsp;&nbsp;&nbsp'
16914                             }
16915
16916                         ]
16917                     };
16918
16919                     if (i == 0) {
16920                         cfg.cls += ' fc-event-start';
16921                     }
16922                     if ((i+1) == rows.length) {
16923                         cfg.cls += ' fc-event-end';
16924                     }
16925
16926                     var ctr = _this.el.select('.fc-event-container',true).first();
16927                     var cg = ctr.createChild(cfg);
16928
16929                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16930                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16931
16932                     var r = (c.more.length) ? 1 : 0;
16933                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16934                     cg.setWidth(ebox.right - sbox.x -2);
16935
16936                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16937                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16938                     cg.on('click', _this.onEventClick, _this, ev);
16939
16940                     ev.els.push(cg);
16941                     
16942                 }
16943                 
16944             }
16945             
16946             
16947             if(c.more.length){
16948                 var  cfg = {
16949                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16950                     style : 'position: absolute',
16951                     unselectable : "on",
16952                     cn : [
16953                         {
16954                             cls: 'fc-event-inner',
16955                             cn : [
16956                                 {
16957                                   tag:'span',
16958                                   cls: 'fc-event-title',
16959                                   html : 'More'
16960                                 }
16961
16962
16963                             ]
16964                         },
16965                         {
16966                             cls: 'ui-resizable-handle ui-resizable-e',
16967                             html : '&nbsp;&nbsp;&nbsp'
16968                         }
16969
16970                     ]
16971                 };
16972
16973                 var ctr = _this.el.select('.fc-event-container',true).first();
16974                 var cg = ctr.createChild(cfg);
16975
16976                 var sbox = c.select('.fc-day-content',true).first().getBox();
16977                 var ebox = c.select('.fc-day-content',true).first().getBox();
16978                 //Roo.log(cg);
16979                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16980                 cg.setWidth(ebox.right - sbox.x -2);
16981
16982                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16983                 
16984             }
16985             
16986         });
16987         
16988         
16989         
16990     },
16991     
16992     onEventEnter: function (e, el,event,d) {
16993         this.fireEvent('evententer', this, el, event);
16994     },
16995     
16996     onEventLeave: function (e, el,event,d) {
16997         this.fireEvent('eventleave', this, el, event);
16998     },
16999     
17000     onEventClick: function (e, el,event,d) {
17001         this.fireEvent('eventclick', this, el, event);
17002     },
17003     
17004     onMonthChange: function () {
17005         this.store.load();
17006     },
17007     
17008     onMoreEventClick: function(e, el, more)
17009     {
17010         var _this = this;
17011         
17012         this.calpopover.placement = 'right';
17013         this.calpopover.setTitle('More');
17014         
17015         this.calpopover.setContent('');
17016         
17017         var ctr = this.calpopover.el.select('.popover-content', true).first();
17018         
17019         Roo.each(more, function(m){
17020             var cfg = {
17021                 cls : 'fc-event-hori fc-event-draggable',
17022                 html : m.title
17023             };
17024             var cg = ctr.createChild(cfg);
17025             
17026             cg.on('click', _this.onEventClick, _this, m);
17027         });
17028         
17029         this.calpopover.show(el);
17030         
17031         
17032     },
17033     
17034     onLoad: function () 
17035     {   
17036         this.calevents = [];
17037         var cal = this;
17038         
17039         if(this.store.getCount() > 0){
17040             this.store.data.each(function(d){
17041                cal.addItem({
17042                     id : d.data.id,
17043                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17044                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17045                     time : d.data.start_time,
17046                     title : d.data.title,
17047                     description : d.data.description,
17048                     venue : d.data.venue
17049                 });
17050             });
17051         }
17052         
17053         this.renderEvents();
17054         
17055         if(this.calevents.length && this.loadMask){
17056             this.maskEl.hide();
17057         }
17058     },
17059     
17060     onBeforeLoad: function()
17061     {
17062         this.clearEvents();
17063         if(this.loadMask){
17064             this.maskEl.show();
17065         }
17066     }
17067 });
17068
17069  
17070  /*
17071  * - LGPL
17072  *
17073  * element
17074  * 
17075  */
17076
17077 /**
17078  * @class Roo.bootstrap.Popover
17079  * @extends Roo.bootstrap.Component
17080  * Bootstrap Popover class
17081  * @cfg {String} html contents of the popover   (or false to use children..)
17082  * @cfg {String} title of popover (or false to hide)
17083  * @cfg {String} placement how it is placed
17084  * @cfg {String} trigger click || hover (or false to trigger manually)
17085  * @cfg {String} over what (parent or false to trigger manually.)
17086  * @cfg {Number} delay - delay before showing
17087  
17088  * @constructor
17089  * Create a new Popover
17090  * @param {Object} config The config object
17091  */
17092
17093 Roo.bootstrap.Popover = function(config){
17094     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17095     
17096     this.addEvents({
17097         // raw events
17098          /**
17099          * @event show
17100          * After the popover show
17101          * 
17102          * @param {Roo.bootstrap.Popover} this
17103          */
17104         "show" : true,
17105         /**
17106          * @event hide
17107          * After the popover hide
17108          * 
17109          * @param {Roo.bootstrap.Popover} this
17110          */
17111         "hide" : true
17112     });
17113 };
17114
17115 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17116     
17117     title: 'Fill in a title',
17118     html: false,
17119     
17120     placement : 'right',
17121     trigger : 'hover', // hover
17122     
17123     delay : 0,
17124     
17125     over: 'parent',
17126     
17127     can_build_overlaid : false,
17128     
17129     getChildContainer : function()
17130     {
17131         return this.el.select('.popover-content',true).first();
17132     },
17133     
17134     getAutoCreate : function(){
17135          
17136         var cfg = {
17137            cls : 'popover roo-dynamic',
17138            style: 'display:block',
17139            cn : [
17140                 {
17141                     cls : 'arrow'
17142                 },
17143                 {
17144                     cls : 'popover-inner',
17145                     cn : [
17146                         {
17147                             tag: 'h3',
17148                             cls: 'popover-title',
17149                             html : this.title
17150                         },
17151                         {
17152                             cls : 'popover-content',
17153                             html : this.html
17154                         }
17155                     ]
17156                     
17157                 }
17158            ]
17159         };
17160         
17161         return cfg;
17162     },
17163     setTitle: function(str)
17164     {
17165         this.title = str;
17166         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17167     },
17168     setContent: function(str)
17169     {
17170         this.html = str;
17171         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17172     },
17173     // as it get's added to the bottom of the page.
17174     onRender : function(ct, position)
17175     {
17176         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17177         if(!this.el){
17178             var cfg = Roo.apply({},  this.getAutoCreate());
17179             cfg.id = Roo.id();
17180             
17181             if (this.cls) {
17182                 cfg.cls += ' ' + this.cls;
17183             }
17184             if (this.style) {
17185                 cfg.style = this.style;
17186             }
17187             //Roo.log("adding to ");
17188             this.el = Roo.get(document.body).createChild(cfg, position);
17189 //            Roo.log(this.el);
17190         }
17191         this.initEvents();
17192     },
17193     
17194     initEvents : function()
17195     {
17196         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17197         this.el.enableDisplayMode('block');
17198         this.el.hide();
17199         if (this.over === false) {
17200             return; 
17201         }
17202         if (this.triggers === false) {
17203             return;
17204         }
17205         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17206         var triggers = this.trigger ? this.trigger.split(' ') : [];
17207         Roo.each(triggers, function(trigger) {
17208         
17209             if (trigger == 'click') {
17210                 on_el.on('click', this.toggle, this);
17211             } else if (trigger != 'manual') {
17212                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17213                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17214       
17215                 on_el.on(eventIn  ,this.enter, this);
17216                 on_el.on(eventOut, this.leave, this);
17217             }
17218         }, this);
17219         
17220     },
17221     
17222     
17223     // private
17224     timeout : null,
17225     hoverState : null,
17226     
17227     toggle : function () {
17228         this.hoverState == 'in' ? this.leave() : this.enter();
17229     },
17230     
17231     enter : function () {
17232         
17233         clearTimeout(this.timeout);
17234     
17235         this.hoverState = 'in';
17236     
17237         if (!this.delay || !this.delay.show) {
17238             this.show();
17239             return;
17240         }
17241         var _t = this;
17242         this.timeout = setTimeout(function () {
17243             if (_t.hoverState == 'in') {
17244                 _t.show();
17245             }
17246         }, this.delay.show)
17247     },
17248     
17249     leave : function() {
17250         clearTimeout(this.timeout);
17251     
17252         this.hoverState = 'out';
17253     
17254         if (!this.delay || !this.delay.hide) {
17255             this.hide();
17256             return;
17257         }
17258         var _t = this;
17259         this.timeout = setTimeout(function () {
17260             if (_t.hoverState == 'out') {
17261                 _t.hide();
17262             }
17263         }, this.delay.hide)
17264     },
17265     
17266     show : function (on_el)
17267     {
17268         if (!on_el) {
17269             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17270         }
17271         
17272         // set content.
17273         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17274         if (this.html !== false) {
17275             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17276         }
17277         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17278         if (!this.title.length) {
17279             this.el.select('.popover-title',true).hide();
17280         }
17281         
17282         var placement = typeof this.placement == 'function' ?
17283             this.placement.call(this, this.el, on_el) :
17284             this.placement;
17285             
17286         var autoToken = /\s?auto?\s?/i;
17287         var autoPlace = autoToken.test(placement);
17288         if (autoPlace) {
17289             placement = placement.replace(autoToken, '') || 'top';
17290         }
17291         
17292         //this.el.detach()
17293         //this.el.setXY([0,0]);
17294         this.el.show();
17295         this.el.dom.style.display='block';
17296         this.el.addClass(placement);
17297         
17298         //this.el.appendTo(on_el);
17299         
17300         var p = this.getPosition();
17301         var box = this.el.getBox();
17302         
17303         if (autoPlace) {
17304             // fixme..
17305         }
17306         var align = Roo.bootstrap.Popover.alignment[placement];
17307         this.el.alignTo(on_el, align[0],align[1]);
17308         //var arrow = this.el.select('.arrow',true).first();
17309         //arrow.set(align[2], 
17310         
17311         this.el.addClass('in');
17312         
17313         
17314         if (this.el.hasClass('fade')) {
17315             // fade it?
17316         }
17317         
17318         this.hoverState = 'in';
17319         
17320         this.fireEvent('show', this);
17321         
17322     },
17323     hide : function()
17324     {
17325         this.el.setXY([0,0]);
17326         this.el.removeClass('in');
17327         this.el.hide();
17328         this.hoverState = null;
17329         
17330         this.fireEvent('hide', this);
17331     }
17332     
17333 });
17334
17335 Roo.bootstrap.Popover.alignment = {
17336     'left' : ['r-l', [-10,0], 'right'],
17337     'right' : ['l-r', [10,0], 'left'],
17338     'bottom' : ['t-b', [0,10], 'top'],
17339     'top' : [ 'b-t', [0,-10], 'bottom']
17340 };
17341
17342  /*
17343  * - LGPL
17344  *
17345  * Progress
17346  * 
17347  */
17348
17349 /**
17350  * @class Roo.bootstrap.Progress
17351  * @extends Roo.bootstrap.Component
17352  * Bootstrap Progress class
17353  * @cfg {Boolean} striped striped of the progress bar
17354  * @cfg {Boolean} active animated of the progress bar
17355  * 
17356  * 
17357  * @constructor
17358  * Create a new Progress
17359  * @param {Object} config The config object
17360  */
17361
17362 Roo.bootstrap.Progress = function(config){
17363     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17364 };
17365
17366 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17367     
17368     striped : false,
17369     active: false,
17370     
17371     getAutoCreate : function(){
17372         var cfg = {
17373             tag: 'div',
17374             cls: 'progress'
17375         };
17376         
17377         
17378         if(this.striped){
17379             cfg.cls += ' progress-striped';
17380         }
17381       
17382         if(this.active){
17383             cfg.cls += ' active';
17384         }
17385         
17386         
17387         return cfg;
17388     }
17389    
17390 });
17391
17392  
17393
17394  /*
17395  * - LGPL
17396  *
17397  * ProgressBar
17398  * 
17399  */
17400
17401 /**
17402  * @class Roo.bootstrap.ProgressBar
17403  * @extends Roo.bootstrap.Component
17404  * Bootstrap ProgressBar class
17405  * @cfg {Number} aria_valuenow aria-value now
17406  * @cfg {Number} aria_valuemin aria-value min
17407  * @cfg {Number} aria_valuemax aria-value max
17408  * @cfg {String} label label for the progress bar
17409  * @cfg {String} panel (success | info | warning | danger )
17410  * @cfg {String} role role of the progress bar
17411  * @cfg {String} sr_only text
17412  * 
17413  * 
17414  * @constructor
17415  * Create a new ProgressBar
17416  * @param {Object} config The config object
17417  */
17418
17419 Roo.bootstrap.ProgressBar = function(config){
17420     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17421 };
17422
17423 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17424     
17425     aria_valuenow : 0,
17426     aria_valuemin : 0,
17427     aria_valuemax : 100,
17428     label : false,
17429     panel : false,
17430     role : false,
17431     sr_only: false,
17432     
17433     getAutoCreate : function()
17434     {
17435         
17436         var cfg = {
17437             tag: 'div',
17438             cls: 'progress-bar',
17439             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17440         };
17441         
17442         if(this.sr_only){
17443             cfg.cn = {
17444                 tag: 'span',
17445                 cls: 'sr-only',
17446                 html: this.sr_only
17447             }
17448         }
17449         
17450         if(this.role){
17451             cfg.role = this.role;
17452         }
17453         
17454         if(this.aria_valuenow){
17455             cfg['aria-valuenow'] = this.aria_valuenow;
17456         }
17457         
17458         if(this.aria_valuemin){
17459             cfg['aria-valuemin'] = this.aria_valuemin;
17460         }
17461         
17462         if(this.aria_valuemax){
17463             cfg['aria-valuemax'] = this.aria_valuemax;
17464         }
17465         
17466         if(this.label && !this.sr_only){
17467             cfg.html = this.label;
17468         }
17469         
17470         if(this.panel){
17471             cfg.cls += ' progress-bar-' + this.panel;
17472         }
17473         
17474         return cfg;
17475     },
17476     
17477     update : function(aria_valuenow)
17478     {
17479         this.aria_valuenow = aria_valuenow;
17480         
17481         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17482     }
17483    
17484 });
17485
17486  
17487
17488  /*
17489  * - LGPL
17490  *
17491  * column
17492  * 
17493  */
17494
17495 /**
17496  * @class Roo.bootstrap.TabGroup
17497  * @extends Roo.bootstrap.Column
17498  * Bootstrap Column class
17499  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17500  * @cfg {Boolean} carousel true to make the group behave like a carousel
17501  * @cfg {Boolean} bullets show bullets for the panels
17502  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17503  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17504  * @cfg {Boolean} showarrow (true|false) show arrow default true
17505  * 
17506  * @constructor
17507  * Create a new TabGroup
17508  * @param {Object} config The config object
17509  */
17510
17511 Roo.bootstrap.TabGroup = function(config){
17512     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17513     if (!this.navId) {
17514         this.navId = Roo.id();
17515     }
17516     this.tabs = [];
17517     Roo.bootstrap.TabGroup.register(this);
17518     
17519 };
17520
17521 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17522     
17523     carousel : false,
17524     transition : false,
17525     bullets : 0,
17526     timer : 0,
17527     autoslide : false,
17528     slideFn : false,
17529     slideOnTouch : false,
17530     showarrow : true,
17531     
17532     getAutoCreate : function()
17533     {
17534         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17535         
17536         cfg.cls += ' tab-content';
17537         
17538         if (this.carousel) {
17539             cfg.cls += ' carousel slide';
17540             
17541             cfg.cn = [{
17542                cls : 'carousel-inner',
17543                cn : []
17544             }];
17545         
17546             if(this.bullets  && !Roo.isTouch){
17547                 
17548                 var bullets = {
17549                     cls : 'carousel-bullets',
17550                     cn : []
17551                 };
17552                
17553                 if(this.bullets_cls){
17554                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17555                 }
17556                 
17557                 bullets.cn.push({
17558                     cls : 'clear'
17559                 });
17560                 
17561                 cfg.cn[0].cn.push(bullets);
17562             }
17563             
17564             if(this.showarrow){
17565                 cfg.cn[0].cn.push({
17566                     tag : 'div',
17567                     class : 'carousel-arrow',
17568                     cn : [
17569                         {
17570                             tag : 'div',
17571                             class : 'carousel-prev',
17572                             cn : [
17573                                 {
17574                                     tag : 'i',
17575                                     class : 'fa fa-chevron-left'
17576                                 }
17577                             ]
17578                         },
17579                         {
17580                             tag : 'div',
17581                             class : 'carousel-next',
17582                             cn : [
17583                                 {
17584                                     tag : 'i',
17585                                     class : 'fa fa-chevron-right'
17586                                 }
17587                             ]
17588                         }
17589                     ]
17590                 });
17591             }
17592             
17593         }
17594         
17595         return cfg;
17596     },
17597     
17598     initEvents:  function()
17599     {
17600 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17601 //            this.el.on("touchstart", this.onTouchStart, this);
17602 //        }
17603         
17604         if(this.autoslide){
17605             var _this = this;
17606             
17607             this.slideFn = window.setInterval(function() {
17608                 _this.showPanelNext();
17609             }, this.timer);
17610         }
17611         
17612         if(this.showarrow){
17613             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17614             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17615         }
17616         
17617         
17618     },
17619     
17620 //    onTouchStart : function(e, el, o)
17621 //    {
17622 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17623 //            return;
17624 //        }
17625 //        
17626 //        this.showPanelNext();
17627 //    },
17628     
17629     
17630     getChildContainer : function()
17631     {
17632         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17633     },
17634     
17635     /**
17636     * register a Navigation item
17637     * @param {Roo.bootstrap.NavItem} the navitem to add
17638     */
17639     register : function(item)
17640     {
17641         this.tabs.push( item);
17642         item.navId = this.navId; // not really needed..
17643         this.addBullet();
17644     
17645     },
17646     
17647     getActivePanel : function()
17648     {
17649         var r = false;
17650         Roo.each(this.tabs, function(t) {
17651             if (t.active) {
17652                 r = t;
17653                 return false;
17654             }
17655             return null;
17656         });
17657         return r;
17658         
17659     },
17660     getPanelByName : function(n)
17661     {
17662         var r = false;
17663         Roo.each(this.tabs, function(t) {
17664             if (t.tabId == n) {
17665                 r = t;
17666                 return false;
17667             }
17668             return null;
17669         });
17670         return r;
17671     },
17672     indexOfPanel : function(p)
17673     {
17674         var r = false;
17675         Roo.each(this.tabs, function(t,i) {
17676             if (t.tabId == p.tabId) {
17677                 r = i;
17678                 return false;
17679             }
17680             return null;
17681         });
17682         return r;
17683     },
17684     /**
17685      * show a specific panel
17686      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17687      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17688      */
17689     showPanel : function (pan)
17690     {
17691         if(this.transition || typeof(pan) == 'undefined'){
17692             Roo.log("waiting for the transitionend");
17693             return;
17694         }
17695         
17696         if (typeof(pan) == 'number') {
17697             pan = this.tabs[pan];
17698         }
17699         
17700         if (typeof(pan) == 'string') {
17701             pan = this.getPanelByName(pan);
17702         }
17703         
17704         var cur = this.getActivePanel();
17705         
17706         if(!pan || !cur){
17707             Roo.log('pan or acitve pan is undefined');
17708             return false;
17709         }
17710         
17711         if (pan.tabId == this.getActivePanel().tabId) {
17712             return true;
17713         }
17714         
17715         if (false === cur.fireEvent('beforedeactivate')) {
17716             return false;
17717         }
17718         
17719         if(this.bullets > 0 && !Roo.isTouch){
17720             this.setActiveBullet(this.indexOfPanel(pan));
17721         }
17722         
17723         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17724             
17725             this.transition = true;
17726             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17727             var lr = dir == 'next' ? 'left' : 'right';
17728             pan.el.addClass(dir); // or prev
17729             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17730             cur.el.addClass(lr); // or right
17731             pan.el.addClass(lr);
17732             
17733             var _this = this;
17734             cur.el.on('transitionend', function() {
17735                 Roo.log("trans end?");
17736                 
17737                 pan.el.removeClass([lr,dir]);
17738                 pan.setActive(true);
17739                 
17740                 cur.el.removeClass([lr]);
17741                 cur.setActive(false);
17742                 
17743                 _this.transition = false;
17744                 
17745             }, this, { single:  true } );
17746             
17747             return true;
17748         }
17749         
17750         cur.setActive(false);
17751         pan.setActive(true);
17752         
17753         return true;
17754         
17755     },
17756     showPanelNext : function()
17757     {
17758         var i = this.indexOfPanel(this.getActivePanel());
17759         
17760         if (i >= this.tabs.length - 1 && !this.autoslide) {
17761             return;
17762         }
17763         
17764         if (i >= this.tabs.length - 1 && this.autoslide) {
17765             i = -1;
17766         }
17767         
17768         this.showPanel(this.tabs[i+1]);
17769     },
17770     
17771     showPanelPrev : function()
17772     {
17773         var i = this.indexOfPanel(this.getActivePanel());
17774         
17775         if (i  < 1 && !this.autoslide) {
17776             return;
17777         }
17778         
17779         if (i < 1 && this.autoslide) {
17780             i = this.tabs.length;
17781         }
17782         
17783         this.showPanel(this.tabs[i-1]);
17784     },
17785     
17786     
17787     addBullet: function()
17788     {
17789         if(!this.bullets || Roo.isTouch){
17790             return;
17791         }
17792         var ctr = this.el.select('.carousel-bullets',true).first();
17793         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17794         var bullet = ctr.createChild({
17795             cls : 'bullet bullet-' + i
17796         },ctr.dom.lastChild);
17797         
17798         
17799         var _this = this;
17800         
17801         bullet.on('click', (function(e, el, o, ii, t){
17802
17803             e.preventDefault();
17804
17805             this.showPanel(ii);
17806
17807             if(this.autoslide && this.slideFn){
17808                 clearInterval(this.slideFn);
17809                 this.slideFn = window.setInterval(function() {
17810                     _this.showPanelNext();
17811                 }, this.timer);
17812             }
17813
17814         }).createDelegate(this, [i, bullet], true));
17815                 
17816         
17817     },
17818      
17819     setActiveBullet : function(i)
17820     {
17821         if(Roo.isTouch){
17822             return;
17823         }
17824         
17825         Roo.each(this.el.select('.bullet', true).elements, function(el){
17826             el.removeClass('selected');
17827         });
17828
17829         var bullet = this.el.select('.bullet-' + i, true).first();
17830         
17831         if(!bullet){
17832             return;
17833         }
17834         
17835         bullet.addClass('selected');
17836     }
17837     
17838     
17839   
17840 });
17841
17842  
17843
17844  
17845  
17846 Roo.apply(Roo.bootstrap.TabGroup, {
17847     
17848     groups: {},
17849      /**
17850     * register a Navigation Group
17851     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17852     */
17853     register : function(navgrp)
17854     {
17855         this.groups[navgrp.navId] = navgrp;
17856         
17857     },
17858     /**
17859     * fetch a Navigation Group based on the navigation ID
17860     * if one does not exist , it will get created.
17861     * @param {string} the navgroup to add
17862     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17863     */
17864     get: function(navId) {
17865         if (typeof(this.groups[navId]) == 'undefined') {
17866             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17867         }
17868         return this.groups[navId] ;
17869     }
17870     
17871     
17872     
17873 });
17874
17875  /*
17876  * - LGPL
17877  *
17878  * TabPanel
17879  * 
17880  */
17881
17882 /**
17883  * @class Roo.bootstrap.TabPanel
17884  * @extends Roo.bootstrap.Component
17885  * Bootstrap TabPanel class
17886  * @cfg {Boolean} active panel active
17887  * @cfg {String} html panel content
17888  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17889  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17890  * @cfg {String} href click to link..
17891  * 
17892  * 
17893  * @constructor
17894  * Create a new TabPanel
17895  * @param {Object} config The config object
17896  */
17897
17898 Roo.bootstrap.TabPanel = function(config){
17899     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17900     this.addEvents({
17901         /**
17902              * @event changed
17903              * Fires when the active status changes
17904              * @param {Roo.bootstrap.TabPanel} this
17905              * @param {Boolean} state the new state
17906             
17907          */
17908         'changed': true,
17909         /**
17910              * @event beforedeactivate
17911              * Fires before a tab is de-activated - can be used to do validation on a form.
17912              * @param {Roo.bootstrap.TabPanel} this
17913              * @return {Boolean} false if there is an error
17914             
17915          */
17916         'beforedeactivate': true
17917      });
17918     
17919     this.tabId = this.tabId || Roo.id();
17920   
17921 };
17922
17923 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17924     
17925     active: false,
17926     html: false,
17927     tabId: false,
17928     navId : false,
17929     href : '',
17930     
17931     getAutoCreate : function(){
17932         var cfg = {
17933             tag: 'div',
17934             // item is needed for carousel - not sure if it has any effect otherwise
17935             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17936             html: this.html || ''
17937         };
17938         
17939         if(this.active){
17940             cfg.cls += ' active';
17941         }
17942         
17943         if(this.tabId){
17944             cfg.tabId = this.tabId;
17945         }
17946         
17947         
17948         return cfg;
17949     },
17950     
17951     initEvents:  function()
17952     {
17953         var p = this.parent();
17954         
17955         this.navId = this.navId || p.navId;
17956         
17957         if (typeof(this.navId) != 'undefined') {
17958             // not really needed.. but just in case.. parent should be a NavGroup.
17959             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17960             
17961             tg.register(this);
17962             
17963             var i = tg.tabs.length - 1;
17964             
17965             if(this.active && tg.bullets > 0 && i < tg.bullets){
17966                 tg.setActiveBullet(i);
17967             }
17968         }
17969         
17970         this.el.on('click', this.onClick, this);
17971         
17972         if(Roo.isTouch){
17973             this.el.on("touchstart", this.onTouchStart, this);
17974             this.el.on("touchmove", this.onTouchMove, this);
17975             this.el.on("touchend", this.onTouchEnd, this);
17976         }
17977         
17978     },
17979     
17980     onRender : function(ct, position)
17981     {
17982         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17983     },
17984     
17985     setActive : function(state)
17986     {
17987         Roo.log("panel - set active " + this.tabId + "=" + state);
17988         
17989         this.active = state;
17990         if (!state) {
17991             this.el.removeClass('active');
17992             
17993         } else  if (!this.el.hasClass('active')) {
17994             this.el.addClass('active');
17995         }
17996         
17997         this.fireEvent('changed', this, state);
17998     },
17999     
18000     onClick : function(e)
18001     {
18002         e.preventDefault();
18003         
18004         if(!this.href.length){
18005             return;
18006         }
18007         
18008         window.location.href = this.href;
18009     },
18010     
18011     startX : 0,
18012     startY : 0,
18013     endX : 0,
18014     endY : 0,
18015     swiping : false,
18016     
18017     onTouchStart : function(e)
18018     {
18019         this.swiping = false;
18020         
18021         this.startX = e.browserEvent.touches[0].clientX;
18022         this.startY = e.browserEvent.touches[0].clientY;
18023     },
18024     
18025     onTouchMove : function(e)
18026     {
18027         this.swiping = true;
18028         
18029         this.endX = e.browserEvent.touches[0].clientX;
18030         this.endY = e.browserEvent.touches[0].clientY;
18031     },
18032     
18033     onTouchEnd : function(e)
18034     {
18035         if(!this.swiping){
18036             this.onClick(e);
18037             return;
18038         }
18039         
18040         var tabGroup = this.parent();
18041         
18042         if(this.endX > this.startX){ // swiping right
18043             tabGroup.showPanelPrev();
18044             return;
18045         }
18046         
18047         if(this.startX > this.endX){ // swiping left
18048             tabGroup.showPanelNext();
18049             return;
18050         }
18051     }
18052     
18053     
18054 });
18055  
18056
18057  
18058
18059  /*
18060  * - LGPL
18061  *
18062  * DateField
18063  * 
18064  */
18065
18066 /**
18067  * @class Roo.bootstrap.DateField
18068  * @extends Roo.bootstrap.Input
18069  * Bootstrap DateField class
18070  * @cfg {Number} weekStart default 0
18071  * @cfg {String} viewMode default empty, (months|years)
18072  * @cfg {String} minViewMode default empty, (months|years)
18073  * @cfg {Number} startDate default -Infinity
18074  * @cfg {Number} endDate default Infinity
18075  * @cfg {Boolean} todayHighlight default false
18076  * @cfg {Boolean} todayBtn default false
18077  * @cfg {Boolean} calendarWeeks default false
18078  * @cfg {Object} daysOfWeekDisabled default empty
18079  * @cfg {Boolean} singleMode default false (true | false)
18080  * 
18081  * @cfg {Boolean} keyboardNavigation default true
18082  * @cfg {String} language default en
18083  * 
18084  * @constructor
18085  * Create a new DateField
18086  * @param {Object} config The config object
18087  */
18088
18089 Roo.bootstrap.DateField = function(config){
18090     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18091      this.addEvents({
18092             /**
18093              * @event show
18094              * Fires when this field show.
18095              * @param {Roo.bootstrap.DateField} this
18096              * @param {Mixed} date The date value
18097              */
18098             show : true,
18099             /**
18100              * @event show
18101              * Fires when this field hide.
18102              * @param {Roo.bootstrap.DateField} this
18103              * @param {Mixed} date The date value
18104              */
18105             hide : true,
18106             /**
18107              * @event select
18108              * Fires when select a date.
18109              * @param {Roo.bootstrap.DateField} this
18110              * @param {Mixed} date The date value
18111              */
18112             select : true,
18113             /**
18114              * @event beforeselect
18115              * Fires when before select a date.
18116              * @param {Roo.bootstrap.DateField} this
18117              * @param {Mixed} date The date value
18118              */
18119             beforeselect : true
18120         });
18121 };
18122
18123 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18124     
18125     /**
18126      * @cfg {String} format
18127      * The default date format string which can be overriden for localization support.  The format must be
18128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18129      */
18130     format : "m/d/y",
18131     /**
18132      * @cfg {String} altFormats
18133      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18134      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18135      */
18136     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18137     
18138     weekStart : 0,
18139     
18140     viewMode : '',
18141     
18142     minViewMode : '',
18143     
18144     todayHighlight : false,
18145     
18146     todayBtn: false,
18147     
18148     language: 'en',
18149     
18150     keyboardNavigation: true,
18151     
18152     calendarWeeks: false,
18153     
18154     startDate: -Infinity,
18155     
18156     endDate: Infinity,
18157     
18158     daysOfWeekDisabled: [],
18159     
18160     _events: [],
18161     
18162     singleMode : false,
18163     
18164     UTCDate: function()
18165     {
18166         return new Date(Date.UTC.apply(Date, arguments));
18167     },
18168     
18169     UTCToday: function()
18170     {
18171         var today = new Date();
18172         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18173     },
18174     
18175     getDate: function() {
18176             var d = this.getUTCDate();
18177             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18178     },
18179     
18180     getUTCDate: function() {
18181             return this.date;
18182     },
18183     
18184     setDate: function(d) {
18185             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18186     },
18187     
18188     setUTCDate: function(d) {
18189             this.date = d;
18190             this.setValue(this.formatDate(this.date));
18191     },
18192         
18193     onRender: function(ct, position)
18194     {
18195         
18196         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18197         
18198         this.language = this.language || 'en';
18199         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18200         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18201         
18202         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18203         this.format = this.format || 'm/d/y';
18204         this.isInline = false;
18205         this.isInput = true;
18206         this.component = this.el.select('.add-on', true).first() || false;
18207         this.component = (this.component && this.component.length === 0) ? false : this.component;
18208         this.hasInput = this.component && this.inputEl().length;
18209         
18210         if (typeof(this.minViewMode === 'string')) {
18211             switch (this.minViewMode) {
18212                 case 'months':
18213                     this.minViewMode = 1;
18214                     break;
18215                 case 'years':
18216                     this.minViewMode = 2;
18217                     break;
18218                 default:
18219                     this.minViewMode = 0;
18220                     break;
18221             }
18222         }
18223         
18224         if (typeof(this.viewMode === 'string')) {
18225             switch (this.viewMode) {
18226                 case 'months':
18227                     this.viewMode = 1;
18228                     break;
18229                 case 'years':
18230                     this.viewMode = 2;
18231                     break;
18232                 default:
18233                     this.viewMode = 0;
18234                     break;
18235             }
18236         }
18237                 
18238         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18239         
18240 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18241         
18242         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18243         
18244         this.picker().on('mousedown', this.onMousedown, this);
18245         this.picker().on('click', this.onClick, this);
18246         
18247         this.picker().addClass('datepicker-dropdown');
18248         
18249         this.startViewMode = this.viewMode;
18250         
18251         if(this.singleMode){
18252             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18253                 v.setVisibilityMode(Roo.Element.DISPLAY);
18254                 v.hide();
18255             });
18256             
18257             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18258                 v.setStyle('width', '189px');
18259             });
18260         }
18261         
18262         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18263             if(!this.calendarWeeks){
18264                 v.remove();
18265                 return;
18266             }
18267             
18268             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18269             v.attr('colspan', function(i, val){
18270                 return parseInt(val) + 1;
18271             });
18272         });
18273                         
18274         
18275         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18276         
18277         this.setStartDate(this.startDate);
18278         this.setEndDate(this.endDate);
18279         
18280         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18281         
18282         this.fillDow();
18283         this.fillMonths();
18284         this.update();
18285         this.showMode();
18286         
18287         if(this.isInline) {
18288             this.show();
18289         }
18290     },
18291     
18292     picker : function()
18293     {
18294         return this.pickerEl;
18295 //        return this.el.select('.datepicker', true).first();
18296     },
18297     
18298     fillDow: function()
18299     {
18300         var dowCnt = this.weekStart;
18301         
18302         var dow = {
18303             tag: 'tr',
18304             cn: [
18305                 
18306             ]
18307         };
18308         
18309         if(this.calendarWeeks){
18310             dow.cn.push({
18311                 tag: 'th',
18312                 cls: 'cw',
18313                 html: '&nbsp;'
18314             })
18315         }
18316         
18317         while (dowCnt < this.weekStart + 7) {
18318             dow.cn.push({
18319                 tag: 'th',
18320                 cls: 'dow',
18321                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18322             });
18323         }
18324         
18325         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18326     },
18327     
18328     fillMonths: function()
18329     {    
18330         var i = 0;
18331         var months = this.picker().select('>.datepicker-months td', true).first();
18332         
18333         months.dom.innerHTML = '';
18334         
18335         while (i < 12) {
18336             var month = {
18337                 tag: 'span',
18338                 cls: 'month',
18339                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18340             };
18341             
18342             months.createChild(month);
18343         }
18344         
18345     },
18346     
18347     update: function()
18348     {
18349         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;
18350         
18351         if (this.date < this.startDate) {
18352             this.viewDate = new Date(this.startDate);
18353         } else if (this.date > this.endDate) {
18354             this.viewDate = new Date(this.endDate);
18355         } else {
18356             this.viewDate = new Date(this.date);
18357         }
18358         
18359         this.fill();
18360     },
18361     
18362     fill: function() 
18363     {
18364         var d = new Date(this.viewDate),
18365                 year = d.getUTCFullYear(),
18366                 month = d.getUTCMonth(),
18367                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18368                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18369                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18370                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18371                 currentDate = this.date && this.date.valueOf(),
18372                 today = this.UTCToday();
18373         
18374         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18375         
18376 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18377         
18378 //        this.picker.select('>tfoot th.today').
18379 //                                              .text(dates[this.language].today)
18380 //                                              .toggle(this.todayBtn !== false);
18381     
18382         this.updateNavArrows();
18383         this.fillMonths();
18384                                                 
18385         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18386         
18387         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18388          
18389         prevMonth.setUTCDate(day);
18390         
18391         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18392         
18393         var nextMonth = new Date(prevMonth);
18394         
18395         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18396         
18397         nextMonth = nextMonth.valueOf();
18398         
18399         var fillMonths = false;
18400         
18401         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18402         
18403         while(prevMonth.valueOf() < nextMonth) {
18404             var clsName = '';
18405             
18406             if (prevMonth.getUTCDay() === this.weekStart) {
18407                 if(fillMonths){
18408                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18409                 }
18410                     
18411                 fillMonths = {
18412                     tag: 'tr',
18413                     cn: []
18414                 };
18415                 
18416                 if(this.calendarWeeks){
18417                     // ISO 8601: First week contains first thursday.
18418                     // ISO also states week starts on Monday, but we can be more abstract here.
18419                     var
18420                     // Start of current week: based on weekstart/current date
18421                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18422                     // Thursday of this week
18423                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18424                     // First Thursday of year, year from thursday
18425                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18426                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18427                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18428                     
18429                     fillMonths.cn.push({
18430                         tag: 'td',
18431                         cls: 'cw',
18432                         html: calWeek
18433                     });
18434                 }
18435             }
18436             
18437             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18438                 clsName += ' old';
18439             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18440                 clsName += ' new';
18441             }
18442             if (this.todayHighlight &&
18443                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18444                 prevMonth.getUTCMonth() == today.getMonth() &&
18445                 prevMonth.getUTCDate() == today.getDate()) {
18446                 clsName += ' today';
18447             }
18448             
18449             if (currentDate && prevMonth.valueOf() === currentDate) {
18450                 clsName += ' active';
18451             }
18452             
18453             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18454                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18455                     clsName += ' disabled';
18456             }
18457             
18458             fillMonths.cn.push({
18459                 tag: 'td',
18460                 cls: 'day ' + clsName,
18461                 html: prevMonth.getDate()
18462             });
18463             
18464             prevMonth.setDate(prevMonth.getDate()+1);
18465         }
18466           
18467         var currentYear = this.date && this.date.getUTCFullYear();
18468         var currentMonth = this.date && this.date.getUTCMonth();
18469         
18470         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18471         
18472         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18473             v.removeClass('active');
18474             
18475             if(currentYear === year && k === currentMonth){
18476                 v.addClass('active');
18477             }
18478             
18479             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18480                 v.addClass('disabled');
18481             }
18482             
18483         });
18484         
18485         
18486         year = parseInt(year/10, 10) * 10;
18487         
18488         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18489         
18490         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18491         
18492         year -= 1;
18493         for (var i = -1; i < 11; i++) {
18494             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18495                 tag: 'span',
18496                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18497                 html: year
18498             });
18499             
18500             year += 1;
18501         }
18502     },
18503     
18504     showMode: function(dir) 
18505     {
18506         if (dir) {
18507             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18508         }
18509         
18510         Roo.each(this.picker().select('>div',true).elements, function(v){
18511             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18512             v.hide();
18513         });
18514         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18515     },
18516     
18517     place: function()
18518     {
18519         if(this.isInline) {
18520             return;
18521         }
18522         
18523         this.picker().removeClass(['bottom', 'top']);
18524         
18525         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18526             /*
18527              * place to the top of element!
18528              *
18529              */
18530             
18531             this.picker().addClass('top');
18532             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18533             
18534             return;
18535         }
18536         
18537         this.picker().addClass('bottom');
18538         
18539         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18540     },
18541     
18542     parseDate : function(value)
18543     {
18544         if(!value || value instanceof Date){
18545             return value;
18546         }
18547         var v = Date.parseDate(value, this.format);
18548         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18549             v = Date.parseDate(value, 'Y-m-d');
18550         }
18551         if(!v && this.altFormats){
18552             if(!this.altFormatsArray){
18553                 this.altFormatsArray = this.altFormats.split("|");
18554             }
18555             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18556                 v = Date.parseDate(value, this.altFormatsArray[i]);
18557             }
18558         }
18559         return v;
18560     },
18561     
18562     formatDate : function(date, fmt)
18563     {   
18564         return (!date || !(date instanceof Date)) ?
18565         date : date.dateFormat(fmt || this.format);
18566     },
18567     
18568     onFocus : function()
18569     {
18570         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18571         this.show();
18572     },
18573     
18574     onBlur : function()
18575     {
18576         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18577         
18578         var d = this.inputEl().getValue();
18579         
18580         this.setValue(d);
18581                 
18582         this.hide();
18583     },
18584     
18585     show : function()
18586     {
18587         this.picker().show();
18588         this.update();
18589         this.place();
18590         
18591         this.fireEvent('show', this, this.date);
18592     },
18593     
18594     hide : function()
18595     {
18596         if(this.isInline) {
18597             return;
18598         }
18599         this.picker().hide();
18600         this.viewMode = this.startViewMode;
18601         this.showMode();
18602         
18603         this.fireEvent('hide', this, this.date);
18604         
18605     },
18606     
18607     onMousedown: function(e)
18608     {
18609         e.stopPropagation();
18610         e.preventDefault();
18611     },
18612     
18613     keyup: function(e)
18614     {
18615         Roo.bootstrap.DateField.superclass.keyup.call(this);
18616         this.update();
18617     },
18618
18619     setValue: function(v)
18620     {
18621         if(this.fireEvent('beforeselect', this, v) !== false){
18622             var d = new Date(this.parseDate(v) ).clearTime();
18623         
18624             if(isNaN(d.getTime())){
18625                 this.date = this.viewDate = '';
18626                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18627                 return;
18628             }
18629
18630             v = this.formatDate(d);
18631
18632             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18633
18634             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18635
18636             this.update();
18637
18638             this.fireEvent('select', this, this.date);
18639         }
18640     },
18641     
18642     getValue: function()
18643     {
18644         return this.formatDate(this.date);
18645     },
18646     
18647     fireKey: function(e)
18648     {
18649         if (!this.picker().isVisible()){
18650             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18651                 this.show();
18652             }
18653             return;
18654         }
18655         
18656         var dateChanged = false,
18657         dir, day, month,
18658         newDate, newViewDate;
18659         
18660         switch(e.keyCode){
18661             case 27: // escape
18662                 this.hide();
18663                 e.preventDefault();
18664                 break;
18665             case 37: // left
18666             case 39: // right
18667                 if (!this.keyboardNavigation) {
18668                     break;
18669                 }
18670                 dir = e.keyCode == 37 ? -1 : 1;
18671                 
18672                 if (e.ctrlKey){
18673                     newDate = this.moveYear(this.date, dir);
18674                     newViewDate = this.moveYear(this.viewDate, dir);
18675                 } else if (e.shiftKey){
18676                     newDate = this.moveMonth(this.date, dir);
18677                     newViewDate = this.moveMonth(this.viewDate, dir);
18678                 } else {
18679                     newDate = new Date(this.date);
18680                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18681                     newViewDate = new Date(this.viewDate);
18682                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18683                 }
18684                 if (this.dateWithinRange(newDate)){
18685                     this.date = newDate;
18686                     this.viewDate = newViewDate;
18687                     this.setValue(this.formatDate(this.date));
18688 //                    this.update();
18689                     e.preventDefault();
18690                     dateChanged = true;
18691                 }
18692                 break;
18693             case 38: // up
18694             case 40: // down
18695                 if (!this.keyboardNavigation) {
18696                     break;
18697                 }
18698                 dir = e.keyCode == 38 ? -1 : 1;
18699                 if (e.ctrlKey){
18700                     newDate = this.moveYear(this.date, dir);
18701                     newViewDate = this.moveYear(this.viewDate, dir);
18702                 } else if (e.shiftKey){
18703                     newDate = this.moveMonth(this.date, dir);
18704                     newViewDate = this.moveMonth(this.viewDate, dir);
18705                 } else {
18706                     newDate = new Date(this.date);
18707                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18708                     newViewDate = new Date(this.viewDate);
18709                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18710                 }
18711                 if (this.dateWithinRange(newDate)){
18712                     this.date = newDate;
18713                     this.viewDate = newViewDate;
18714                     this.setValue(this.formatDate(this.date));
18715 //                    this.update();
18716                     e.preventDefault();
18717                     dateChanged = true;
18718                 }
18719                 break;
18720             case 13: // enter
18721                 this.setValue(this.formatDate(this.date));
18722                 this.hide();
18723                 e.preventDefault();
18724                 break;
18725             case 9: // tab
18726                 this.setValue(this.formatDate(this.date));
18727                 this.hide();
18728                 break;
18729             case 16: // shift
18730             case 17: // ctrl
18731             case 18: // alt
18732                 break;
18733             default :
18734                 this.hide();
18735                 
18736         }
18737     },
18738     
18739     
18740     onClick: function(e) 
18741     {
18742         e.stopPropagation();
18743         e.preventDefault();
18744         
18745         var target = e.getTarget();
18746         
18747         if(target.nodeName.toLowerCase() === 'i'){
18748             target = Roo.get(target).dom.parentNode;
18749         }
18750         
18751         var nodeName = target.nodeName;
18752         var className = target.className;
18753         var html = target.innerHTML;
18754         //Roo.log(nodeName);
18755         
18756         switch(nodeName.toLowerCase()) {
18757             case 'th':
18758                 switch(className) {
18759                     case 'switch':
18760                         this.showMode(1);
18761                         break;
18762                     case 'prev':
18763                     case 'next':
18764                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18765                         switch(this.viewMode){
18766                                 case 0:
18767                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18768                                         break;
18769                                 case 1:
18770                                 case 2:
18771                                         this.viewDate = this.moveYear(this.viewDate, dir);
18772                                         break;
18773                         }
18774                         this.fill();
18775                         break;
18776                     case 'today':
18777                         var date = new Date();
18778                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18779 //                        this.fill()
18780                         this.setValue(this.formatDate(this.date));
18781                         
18782                         this.hide();
18783                         break;
18784                 }
18785                 break;
18786             case 'span':
18787                 if (className.indexOf('disabled') < 0) {
18788                     this.viewDate.setUTCDate(1);
18789                     if (className.indexOf('month') > -1) {
18790                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18791                     } else {
18792                         var year = parseInt(html, 10) || 0;
18793                         this.viewDate.setUTCFullYear(year);
18794                         
18795                     }
18796                     
18797                     if(this.singleMode){
18798                         this.setValue(this.formatDate(this.viewDate));
18799                         this.hide();
18800                         return;
18801                     }
18802                     
18803                     this.showMode(-1);
18804                     this.fill();
18805                 }
18806                 break;
18807                 
18808             case 'td':
18809                 //Roo.log(className);
18810                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18811                     var day = parseInt(html, 10) || 1;
18812                     var year = this.viewDate.getUTCFullYear(),
18813                         month = this.viewDate.getUTCMonth();
18814
18815                     if (className.indexOf('old') > -1) {
18816                         if(month === 0 ){
18817                             month = 11;
18818                             year -= 1;
18819                         }else{
18820                             month -= 1;
18821                         }
18822                     } else if (className.indexOf('new') > -1) {
18823                         if (month == 11) {
18824                             month = 0;
18825                             year += 1;
18826                         } else {
18827                             month += 1;
18828                         }
18829                     }
18830                     //Roo.log([year,month,day]);
18831                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18832                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18833 //                    this.fill();
18834                     //Roo.log(this.formatDate(this.date));
18835                     this.setValue(this.formatDate(this.date));
18836                     this.hide();
18837                 }
18838                 break;
18839         }
18840     },
18841     
18842     setStartDate: function(startDate)
18843     {
18844         this.startDate = startDate || -Infinity;
18845         if (this.startDate !== -Infinity) {
18846             this.startDate = this.parseDate(this.startDate);
18847         }
18848         this.update();
18849         this.updateNavArrows();
18850     },
18851
18852     setEndDate: function(endDate)
18853     {
18854         this.endDate = endDate || Infinity;
18855         if (this.endDate !== Infinity) {
18856             this.endDate = this.parseDate(this.endDate);
18857         }
18858         this.update();
18859         this.updateNavArrows();
18860     },
18861     
18862     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18863     {
18864         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18865         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18866             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18867         }
18868         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18869             return parseInt(d, 10);
18870         });
18871         this.update();
18872         this.updateNavArrows();
18873     },
18874     
18875     updateNavArrows: function() 
18876     {
18877         if(this.singleMode){
18878             return;
18879         }
18880         
18881         var d = new Date(this.viewDate),
18882         year = d.getUTCFullYear(),
18883         month = d.getUTCMonth();
18884         
18885         Roo.each(this.picker().select('.prev', true).elements, function(v){
18886             v.show();
18887             switch (this.viewMode) {
18888                 case 0:
18889
18890                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18891                         v.hide();
18892                     }
18893                     break;
18894                 case 1:
18895                 case 2:
18896                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18897                         v.hide();
18898                     }
18899                     break;
18900             }
18901         });
18902         
18903         Roo.each(this.picker().select('.next', true).elements, function(v){
18904             v.show();
18905             switch (this.viewMode) {
18906                 case 0:
18907
18908                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18909                         v.hide();
18910                     }
18911                     break;
18912                 case 1:
18913                 case 2:
18914                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18915                         v.hide();
18916                     }
18917                     break;
18918             }
18919         })
18920     },
18921     
18922     moveMonth: function(date, dir)
18923     {
18924         if (!dir) {
18925             return date;
18926         }
18927         var new_date = new Date(date.valueOf()),
18928         day = new_date.getUTCDate(),
18929         month = new_date.getUTCMonth(),
18930         mag = Math.abs(dir),
18931         new_month, test;
18932         dir = dir > 0 ? 1 : -1;
18933         if (mag == 1){
18934             test = dir == -1
18935             // If going back one month, make sure month is not current month
18936             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18937             ? function(){
18938                 return new_date.getUTCMonth() == month;
18939             }
18940             // If going forward one month, make sure month is as expected
18941             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18942             : function(){
18943                 return new_date.getUTCMonth() != new_month;
18944             };
18945             new_month = month + dir;
18946             new_date.setUTCMonth(new_month);
18947             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18948             if (new_month < 0 || new_month > 11) {
18949                 new_month = (new_month + 12) % 12;
18950             }
18951         } else {
18952             // For magnitudes >1, move one month at a time...
18953             for (var i=0; i<mag; i++) {
18954                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18955                 new_date = this.moveMonth(new_date, dir);
18956             }
18957             // ...then reset the day, keeping it in the new month
18958             new_month = new_date.getUTCMonth();
18959             new_date.setUTCDate(day);
18960             test = function(){
18961                 return new_month != new_date.getUTCMonth();
18962             };
18963         }
18964         // Common date-resetting loop -- if date is beyond end of month, make it
18965         // end of month
18966         while (test()){
18967             new_date.setUTCDate(--day);
18968             new_date.setUTCMonth(new_month);
18969         }
18970         return new_date;
18971     },
18972
18973     moveYear: function(date, dir)
18974     {
18975         return this.moveMonth(date, dir*12);
18976     },
18977
18978     dateWithinRange: function(date)
18979     {
18980         return date >= this.startDate && date <= this.endDate;
18981     },
18982
18983     
18984     remove: function() 
18985     {
18986         this.picker().remove();
18987     },
18988     
18989     validateValue : function(value)
18990     {
18991         if(value.length < 1)  {
18992             if(this.allowBlank){
18993                 return true;
18994             }
18995             return false;
18996         }
18997         
18998         if(value.length < this.minLength){
18999             return false;
19000         }
19001         if(value.length > this.maxLength){
19002             return false;
19003         }
19004         if(this.vtype){
19005             var vt = Roo.form.VTypes;
19006             if(!vt[this.vtype](value, this)){
19007                 return false;
19008             }
19009         }
19010         if(typeof this.validator == "function"){
19011             var msg = this.validator(value);
19012             if(msg !== true){
19013                 return false;
19014             }
19015         }
19016         
19017         if(this.regex && !this.regex.test(value)){
19018             return false;
19019         }
19020         
19021         if(typeof(this.parseDate(value)) == 'undefined'){
19022             return false;
19023         }
19024         
19025         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19026             return false;
19027         }      
19028         
19029         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19030             return false;
19031         } 
19032         
19033         
19034         return true;
19035     }
19036    
19037 });
19038
19039 Roo.apply(Roo.bootstrap.DateField,  {
19040     
19041     head : {
19042         tag: 'thead',
19043         cn: [
19044         {
19045             tag: 'tr',
19046             cn: [
19047             {
19048                 tag: 'th',
19049                 cls: 'prev',
19050                 html: '<i class="fa fa-arrow-left"/>'
19051             },
19052             {
19053                 tag: 'th',
19054                 cls: 'switch',
19055                 colspan: '5'
19056             },
19057             {
19058                 tag: 'th',
19059                 cls: 'next',
19060                 html: '<i class="fa fa-arrow-right"/>'
19061             }
19062
19063             ]
19064         }
19065         ]
19066     },
19067     
19068     content : {
19069         tag: 'tbody',
19070         cn: [
19071         {
19072             tag: 'tr',
19073             cn: [
19074             {
19075                 tag: 'td',
19076                 colspan: '7'
19077             }
19078             ]
19079         }
19080         ]
19081     },
19082     
19083     footer : {
19084         tag: 'tfoot',
19085         cn: [
19086         {
19087             tag: 'tr',
19088             cn: [
19089             {
19090                 tag: 'th',
19091                 colspan: '7',
19092                 cls: 'today'
19093             }
19094                     
19095             ]
19096         }
19097         ]
19098     },
19099     
19100     dates:{
19101         en: {
19102             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19103             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19104             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19105             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19106             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19107             today: "Today"
19108         }
19109     },
19110     
19111     modes: [
19112     {
19113         clsName: 'days',
19114         navFnc: 'Month',
19115         navStep: 1
19116     },
19117     {
19118         clsName: 'months',
19119         navFnc: 'FullYear',
19120         navStep: 1
19121     },
19122     {
19123         clsName: 'years',
19124         navFnc: 'FullYear',
19125         navStep: 10
19126     }]
19127 });
19128
19129 Roo.apply(Roo.bootstrap.DateField,  {
19130   
19131     template : {
19132         tag: 'div',
19133         cls: 'datepicker dropdown-menu roo-dynamic',
19134         cn: [
19135         {
19136             tag: 'div',
19137             cls: 'datepicker-days',
19138             cn: [
19139             {
19140                 tag: 'table',
19141                 cls: 'table-condensed',
19142                 cn:[
19143                 Roo.bootstrap.DateField.head,
19144                 {
19145                     tag: 'tbody'
19146                 },
19147                 Roo.bootstrap.DateField.footer
19148                 ]
19149             }
19150             ]
19151         },
19152         {
19153             tag: 'div',
19154             cls: 'datepicker-months',
19155             cn: [
19156             {
19157                 tag: 'table',
19158                 cls: 'table-condensed',
19159                 cn:[
19160                 Roo.bootstrap.DateField.head,
19161                 Roo.bootstrap.DateField.content,
19162                 Roo.bootstrap.DateField.footer
19163                 ]
19164             }
19165             ]
19166         },
19167         {
19168             tag: 'div',
19169             cls: 'datepicker-years',
19170             cn: [
19171             {
19172                 tag: 'table',
19173                 cls: 'table-condensed',
19174                 cn:[
19175                 Roo.bootstrap.DateField.head,
19176                 Roo.bootstrap.DateField.content,
19177                 Roo.bootstrap.DateField.footer
19178                 ]
19179             }
19180             ]
19181         }
19182         ]
19183     }
19184 });
19185
19186  
19187
19188  /*
19189  * - LGPL
19190  *
19191  * TimeField
19192  * 
19193  */
19194
19195 /**
19196  * @class Roo.bootstrap.TimeField
19197  * @extends Roo.bootstrap.Input
19198  * Bootstrap DateField class
19199  * 
19200  * 
19201  * @constructor
19202  * Create a new TimeField
19203  * @param {Object} config The config object
19204  */
19205
19206 Roo.bootstrap.TimeField = function(config){
19207     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19208     this.addEvents({
19209             /**
19210              * @event show
19211              * Fires when this field show.
19212              * @param {Roo.bootstrap.DateField} thisthis
19213              * @param {Mixed} date The date value
19214              */
19215             show : true,
19216             /**
19217              * @event show
19218              * Fires when this field hide.
19219              * @param {Roo.bootstrap.DateField} this
19220              * @param {Mixed} date The date value
19221              */
19222             hide : true,
19223             /**
19224              * @event select
19225              * Fires when select a date.
19226              * @param {Roo.bootstrap.DateField} this
19227              * @param {Mixed} date The date value
19228              */
19229             select : true
19230         });
19231 };
19232
19233 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19234     
19235     /**
19236      * @cfg {String} format
19237      * The default time format string which can be overriden for localization support.  The format must be
19238      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19239      */
19240     format : "H:i",
19241        
19242     onRender: function(ct, position)
19243     {
19244         
19245         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19246                 
19247         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19248         
19249         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19250         
19251         this.pop = this.picker().select('>.datepicker-time',true).first();
19252         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19253         
19254         this.picker().on('mousedown', this.onMousedown, this);
19255         this.picker().on('click', this.onClick, this);
19256         
19257         this.picker().addClass('datepicker-dropdown');
19258     
19259         this.fillTime();
19260         this.update();
19261             
19262         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19263         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19264         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19265         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19266         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19267         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19268
19269     },
19270     
19271     fireKey: function(e){
19272         if (!this.picker().isVisible()){
19273             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19274                 this.show();
19275             }
19276             return;
19277         }
19278
19279         e.preventDefault();
19280         
19281         switch(e.keyCode){
19282             case 27: // escape
19283                 this.hide();
19284                 break;
19285             case 37: // left
19286             case 39: // right
19287                 this.onTogglePeriod();
19288                 break;
19289             case 38: // up
19290                 this.onIncrementMinutes();
19291                 break;
19292             case 40: // down
19293                 this.onDecrementMinutes();
19294                 break;
19295             case 13: // enter
19296             case 9: // tab
19297                 this.setTime();
19298                 break;
19299         }
19300     },
19301     
19302     onClick: function(e) {
19303         e.stopPropagation();
19304         e.preventDefault();
19305     },
19306     
19307     picker : function()
19308     {
19309         return this.el.select('.datepicker', true).first();
19310     },
19311     
19312     fillTime: function()
19313     {    
19314         var time = this.pop.select('tbody', true).first();
19315         
19316         time.dom.innerHTML = '';
19317         
19318         time.createChild({
19319             tag: 'tr',
19320             cn: [
19321                 {
19322                     tag: 'td',
19323                     cn: [
19324                         {
19325                             tag: 'a',
19326                             href: '#',
19327                             cls: 'btn',
19328                             cn: [
19329                                 {
19330                                     tag: 'span',
19331                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19332                                 }
19333                             ]
19334                         } 
19335                     ]
19336                 },
19337                 {
19338                     tag: 'td',
19339                     cls: 'separator'
19340                 },
19341                 {
19342                     tag: 'td',
19343                     cn: [
19344                         {
19345                             tag: 'a',
19346                             href: '#',
19347                             cls: 'btn',
19348                             cn: [
19349                                 {
19350                                     tag: 'span',
19351                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19352                                 }
19353                             ]
19354                         }
19355                     ]
19356                 },
19357                 {
19358                     tag: 'td',
19359                     cls: 'separator'
19360                 }
19361             ]
19362         });
19363         
19364         time.createChild({
19365             tag: 'tr',
19366             cn: [
19367                 {
19368                     tag: 'td',
19369                     cn: [
19370                         {
19371                             tag: 'span',
19372                             cls: 'timepicker-hour',
19373                             html: '00'
19374                         }  
19375                     ]
19376                 },
19377                 {
19378                     tag: 'td',
19379                     cls: 'separator',
19380                     html: ':'
19381                 },
19382                 {
19383                     tag: 'td',
19384                     cn: [
19385                         {
19386                             tag: 'span',
19387                             cls: 'timepicker-minute',
19388                             html: '00'
19389                         }  
19390                     ]
19391                 },
19392                 {
19393                     tag: 'td',
19394                     cls: 'separator'
19395                 },
19396                 {
19397                     tag: 'td',
19398                     cn: [
19399                         {
19400                             tag: 'button',
19401                             type: 'button',
19402                             cls: 'btn btn-primary period',
19403                             html: 'AM'
19404                             
19405                         }
19406                     ]
19407                 }
19408             ]
19409         });
19410         
19411         time.createChild({
19412             tag: 'tr',
19413             cn: [
19414                 {
19415                     tag: 'td',
19416                     cn: [
19417                         {
19418                             tag: 'a',
19419                             href: '#',
19420                             cls: 'btn',
19421                             cn: [
19422                                 {
19423                                     tag: 'span',
19424                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19425                                 }
19426                             ]
19427                         }
19428                     ]
19429                 },
19430                 {
19431                     tag: 'td',
19432                     cls: 'separator'
19433                 },
19434                 {
19435                     tag: 'td',
19436                     cn: [
19437                         {
19438                             tag: 'a',
19439                             href: '#',
19440                             cls: 'btn',
19441                             cn: [
19442                                 {
19443                                     tag: 'span',
19444                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19445                                 }
19446                             ]
19447                         }
19448                     ]
19449                 },
19450                 {
19451                     tag: 'td',
19452                     cls: 'separator'
19453                 }
19454             ]
19455         });
19456         
19457     },
19458     
19459     update: function()
19460     {
19461         
19462         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19463         
19464         this.fill();
19465     },
19466     
19467     fill: function() 
19468     {
19469         var hours = this.time.getHours();
19470         var minutes = this.time.getMinutes();
19471         var period = 'AM';
19472         
19473         if(hours > 11){
19474             period = 'PM';
19475         }
19476         
19477         if(hours == 0){
19478             hours = 12;
19479         }
19480         
19481         
19482         if(hours > 12){
19483             hours = hours - 12;
19484         }
19485         
19486         if(hours < 10){
19487             hours = '0' + hours;
19488         }
19489         
19490         if(minutes < 10){
19491             minutes = '0' + minutes;
19492         }
19493         
19494         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19495         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19496         this.pop.select('button', true).first().dom.innerHTML = period;
19497         
19498     },
19499     
19500     place: function()
19501     {   
19502         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19503         
19504         var cls = ['bottom'];
19505         
19506         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19507             cls.pop();
19508             cls.push('top');
19509         }
19510         
19511         cls.push('right');
19512         
19513         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19514             cls.pop();
19515             cls.push('left');
19516         }
19517         
19518         this.picker().addClass(cls.join('-'));
19519         
19520         var _this = this;
19521         
19522         Roo.each(cls, function(c){
19523             if(c == 'bottom'){
19524                 _this.picker().setTop(_this.inputEl().getHeight());
19525                 return;
19526             }
19527             if(c == 'top'){
19528                 _this.picker().setTop(0 - _this.picker().getHeight());
19529                 return;
19530             }
19531             
19532             if(c == 'left'){
19533                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19534                 return;
19535             }
19536             if(c == 'right'){
19537                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19538                 return;
19539             }
19540         });
19541         
19542     },
19543   
19544     onFocus : function()
19545     {
19546         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19547         this.show();
19548     },
19549     
19550     onBlur : function()
19551     {
19552         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19553         this.hide();
19554     },
19555     
19556     show : function()
19557     {
19558         this.picker().show();
19559         this.pop.show();
19560         this.update();
19561         this.place();
19562         
19563         this.fireEvent('show', this, this.date);
19564     },
19565     
19566     hide : function()
19567     {
19568         this.picker().hide();
19569         this.pop.hide();
19570         
19571         this.fireEvent('hide', this, this.date);
19572     },
19573     
19574     setTime : function()
19575     {
19576         this.hide();
19577         this.setValue(this.time.format(this.format));
19578         
19579         this.fireEvent('select', this, this.date);
19580         
19581         
19582     },
19583     
19584     onMousedown: function(e){
19585         e.stopPropagation();
19586         e.preventDefault();
19587     },
19588     
19589     onIncrementHours: function()
19590     {
19591         Roo.log('onIncrementHours');
19592         this.time = this.time.add(Date.HOUR, 1);
19593         this.update();
19594         
19595     },
19596     
19597     onDecrementHours: function()
19598     {
19599         Roo.log('onDecrementHours');
19600         this.time = this.time.add(Date.HOUR, -1);
19601         this.update();
19602     },
19603     
19604     onIncrementMinutes: function()
19605     {
19606         Roo.log('onIncrementMinutes');
19607         this.time = this.time.add(Date.MINUTE, 1);
19608         this.update();
19609     },
19610     
19611     onDecrementMinutes: function()
19612     {
19613         Roo.log('onDecrementMinutes');
19614         this.time = this.time.add(Date.MINUTE, -1);
19615         this.update();
19616     },
19617     
19618     onTogglePeriod: function()
19619     {
19620         Roo.log('onTogglePeriod');
19621         this.time = this.time.add(Date.HOUR, 12);
19622         this.update();
19623     }
19624     
19625    
19626 });
19627
19628 Roo.apply(Roo.bootstrap.TimeField,  {
19629     
19630     content : {
19631         tag: 'tbody',
19632         cn: [
19633             {
19634                 tag: 'tr',
19635                 cn: [
19636                 {
19637                     tag: 'td',
19638                     colspan: '7'
19639                 }
19640                 ]
19641             }
19642         ]
19643     },
19644     
19645     footer : {
19646         tag: 'tfoot',
19647         cn: [
19648             {
19649                 tag: 'tr',
19650                 cn: [
19651                 {
19652                     tag: 'th',
19653                     colspan: '7',
19654                     cls: '',
19655                     cn: [
19656                         {
19657                             tag: 'button',
19658                             cls: 'btn btn-info ok',
19659                             html: 'OK'
19660                         }
19661                     ]
19662                 }
19663
19664                 ]
19665             }
19666         ]
19667     }
19668 });
19669
19670 Roo.apply(Roo.bootstrap.TimeField,  {
19671   
19672     template : {
19673         tag: 'div',
19674         cls: 'datepicker dropdown-menu',
19675         cn: [
19676             {
19677                 tag: 'div',
19678                 cls: 'datepicker-time',
19679                 cn: [
19680                 {
19681                     tag: 'table',
19682                     cls: 'table-condensed',
19683                     cn:[
19684                     Roo.bootstrap.TimeField.content,
19685                     Roo.bootstrap.TimeField.footer
19686                     ]
19687                 }
19688                 ]
19689             }
19690         ]
19691     }
19692 });
19693
19694  
19695
19696  /*
19697  * - LGPL
19698  *
19699  * MonthField
19700  * 
19701  */
19702
19703 /**
19704  * @class Roo.bootstrap.MonthField
19705  * @extends Roo.bootstrap.Input
19706  * Bootstrap MonthField class
19707  * 
19708  * @cfg {String} language default en
19709  * 
19710  * @constructor
19711  * Create a new MonthField
19712  * @param {Object} config The config object
19713  */
19714
19715 Roo.bootstrap.MonthField = function(config){
19716     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19717     
19718     this.addEvents({
19719         /**
19720          * @event show
19721          * Fires when this field show.
19722          * @param {Roo.bootstrap.MonthField} this
19723          * @param {Mixed} date The date value
19724          */
19725         show : true,
19726         /**
19727          * @event show
19728          * Fires when this field hide.
19729          * @param {Roo.bootstrap.MonthField} this
19730          * @param {Mixed} date The date value
19731          */
19732         hide : true,
19733         /**
19734          * @event select
19735          * Fires when select a date.
19736          * @param {Roo.bootstrap.MonthField} this
19737          * @param {String} oldvalue The old value
19738          * @param {String} newvalue The new value
19739          */
19740         select : true
19741     });
19742 };
19743
19744 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19745     
19746     onRender: function(ct, position)
19747     {
19748         
19749         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19750         
19751         this.language = this.language || 'en';
19752         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19753         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19754         
19755         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19756         this.isInline = false;
19757         this.isInput = true;
19758         this.component = this.el.select('.add-on', true).first() || false;
19759         this.component = (this.component && this.component.length === 0) ? false : this.component;
19760         this.hasInput = this.component && this.inputEL().length;
19761         
19762         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19763         
19764         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19765         
19766         this.picker().on('mousedown', this.onMousedown, this);
19767         this.picker().on('click', this.onClick, this);
19768         
19769         this.picker().addClass('datepicker-dropdown');
19770         
19771         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19772             v.setStyle('width', '189px');
19773         });
19774         
19775         this.fillMonths();
19776         
19777         this.update();
19778         
19779         if(this.isInline) {
19780             this.show();
19781         }
19782         
19783     },
19784     
19785     setValue: function(v, suppressEvent)
19786     {   
19787         var o = this.getValue();
19788         
19789         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19790         
19791         this.update();
19792
19793         if(suppressEvent !== true){
19794             this.fireEvent('select', this, o, v);
19795         }
19796         
19797     },
19798     
19799     getValue: function()
19800     {
19801         return this.value;
19802     },
19803     
19804     onClick: function(e) 
19805     {
19806         e.stopPropagation();
19807         e.preventDefault();
19808         
19809         var target = e.getTarget();
19810         
19811         if(target.nodeName.toLowerCase() === 'i'){
19812             target = Roo.get(target).dom.parentNode;
19813         }
19814         
19815         var nodeName = target.nodeName;
19816         var className = target.className;
19817         var html = target.innerHTML;
19818         
19819         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19820             return;
19821         }
19822         
19823         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19824         
19825         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19826         
19827         this.hide();
19828                         
19829     },
19830     
19831     picker : function()
19832     {
19833         return this.pickerEl;
19834     },
19835     
19836     fillMonths: function()
19837     {    
19838         var i = 0;
19839         var months = this.picker().select('>.datepicker-months td', true).first();
19840         
19841         months.dom.innerHTML = '';
19842         
19843         while (i < 12) {
19844             var month = {
19845                 tag: 'span',
19846                 cls: 'month',
19847                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19848             };
19849             
19850             months.createChild(month);
19851         }
19852         
19853     },
19854     
19855     update: function()
19856     {
19857         var _this = this;
19858         
19859         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19860             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19861         }
19862         
19863         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19864             e.removeClass('active');
19865             
19866             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19867                 e.addClass('active');
19868             }
19869         })
19870     },
19871     
19872     place: function()
19873     {
19874         if(this.isInline) {
19875             return;
19876         }
19877         
19878         this.picker().removeClass(['bottom', 'top']);
19879         
19880         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19881             /*
19882              * place to the top of element!
19883              *
19884              */
19885             
19886             this.picker().addClass('top');
19887             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19888             
19889             return;
19890         }
19891         
19892         this.picker().addClass('bottom');
19893         
19894         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19895     },
19896     
19897     onFocus : function()
19898     {
19899         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19900         this.show();
19901     },
19902     
19903     onBlur : function()
19904     {
19905         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19906         
19907         var d = this.inputEl().getValue();
19908         
19909         this.setValue(d);
19910                 
19911         this.hide();
19912     },
19913     
19914     show : function()
19915     {
19916         this.picker().show();
19917         this.picker().select('>.datepicker-months', true).first().show();
19918         this.update();
19919         this.place();
19920         
19921         this.fireEvent('show', this, this.date);
19922     },
19923     
19924     hide : function()
19925     {
19926         if(this.isInline) {
19927             return;
19928         }
19929         this.picker().hide();
19930         this.fireEvent('hide', this, this.date);
19931         
19932     },
19933     
19934     onMousedown: function(e)
19935     {
19936         e.stopPropagation();
19937         e.preventDefault();
19938     },
19939     
19940     keyup: function(e)
19941     {
19942         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19943         this.update();
19944     },
19945
19946     fireKey: function(e)
19947     {
19948         if (!this.picker().isVisible()){
19949             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19950                 this.show();
19951             }
19952             return;
19953         }
19954         
19955         var dir;
19956         
19957         switch(e.keyCode){
19958             case 27: // escape
19959                 this.hide();
19960                 e.preventDefault();
19961                 break;
19962             case 37: // left
19963             case 39: // right
19964                 dir = e.keyCode == 37 ? -1 : 1;
19965                 
19966                 this.vIndex = this.vIndex + dir;
19967                 
19968                 if(this.vIndex < 0){
19969                     this.vIndex = 0;
19970                 }
19971                 
19972                 if(this.vIndex > 11){
19973                     this.vIndex = 11;
19974                 }
19975                 
19976                 if(isNaN(this.vIndex)){
19977                     this.vIndex = 0;
19978                 }
19979                 
19980                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19981                 
19982                 break;
19983             case 38: // up
19984             case 40: // down
19985                 
19986                 dir = e.keyCode == 38 ? -1 : 1;
19987                 
19988                 this.vIndex = this.vIndex + dir * 4;
19989                 
19990                 if(this.vIndex < 0){
19991                     this.vIndex = 0;
19992                 }
19993                 
19994                 if(this.vIndex > 11){
19995                     this.vIndex = 11;
19996                 }
19997                 
19998                 if(isNaN(this.vIndex)){
19999                     this.vIndex = 0;
20000                 }
20001                 
20002                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20003                 break;
20004                 
20005             case 13: // enter
20006                 
20007                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20008                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20009                 }
20010                 
20011                 this.hide();
20012                 e.preventDefault();
20013                 break;
20014             case 9: // tab
20015                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20016                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20017                 }
20018                 this.hide();
20019                 break;
20020             case 16: // shift
20021             case 17: // ctrl
20022             case 18: // alt
20023                 break;
20024             default :
20025                 this.hide();
20026                 
20027         }
20028     },
20029     
20030     remove: function() 
20031     {
20032         this.picker().remove();
20033     }
20034    
20035 });
20036
20037 Roo.apply(Roo.bootstrap.MonthField,  {
20038     
20039     content : {
20040         tag: 'tbody',
20041         cn: [
20042         {
20043             tag: 'tr',
20044             cn: [
20045             {
20046                 tag: 'td',
20047                 colspan: '7'
20048             }
20049             ]
20050         }
20051         ]
20052     },
20053     
20054     dates:{
20055         en: {
20056             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20057             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20058         }
20059     }
20060 });
20061
20062 Roo.apply(Roo.bootstrap.MonthField,  {
20063   
20064     template : {
20065         tag: 'div',
20066         cls: 'datepicker dropdown-menu roo-dynamic',
20067         cn: [
20068             {
20069                 tag: 'div',
20070                 cls: 'datepicker-months',
20071                 cn: [
20072                 {
20073                     tag: 'table',
20074                     cls: 'table-condensed',
20075                     cn:[
20076                         Roo.bootstrap.DateField.content
20077                     ]
20078                 }
20079                 ]
20080             }
20081         ]
20082     }
20083 });
20084
20085  
20086
20087  
20088  /*
20089  * - LGPL
20090  *
20091  * CheckBox
20092  * 
20093  */
20094
20095 /**
20096  * @class Roo.bootstrap.CheckBox
20097  * @extends Roo.bootstrap.Input
20098  * Bootstrap CheckBox class
20099  * 
20100  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20101  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20102  * @cfg {String} boxLabel The text that appears beside the checkbox
20103  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20104  * @cfg {Boolean} checked initnal the element
20105  * @cfg {Boolean} inline inline the element (default false)
20106  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20107  * 
20108  * @constructor
20109  * Create a new CheckBox
20110  * @param {Object} config The config object
20111  */
20112
20113 Roo.bootstrap.CheckBox = function(config){
20114     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20115    
20116     this.addEvents({
20117         /**
20118         * @event check
20119         * Fires when the element is checked or unchecked.
20120         * @param {Roo.bootstrap.CheckBox} this This input
20121         * @param {Boolean} checked The new checked value
20122         */
20123        check : true
20124     });
20125     
20126 };
20127
20128 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20129   
20130     inputType: 'checkbox',
20131     inputValue: 1,
20132     valueOff: 0,
20133     boxLabel: false,
20134     checked: false,
20135     weight : false,
20136     inline: false,
20137     
20138     getAutoCreate : function()
20139     {
20140         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20141         
20142         var id = Roo.id();
20143         
20144         var cfg = {};
20145         
20146         cfg.cls = 'form-group ' + this.inputType; //input-group
20147         
20148         if(this.inline){
20149             cfg.cls += ' ' + this.inputType + '-inline';
20150         }
20151         
20152         var input =  {
20153             tag: 'input',
20154             id : id,
20155             type : this.inputType,
20156             value : this.inputValue,
20157             cls : 'roo-' + this.inputType, //'form-box',
20158             placeholder : this.placeholder || ''
20159             
20160         };
20161         
20162         if(this.inputType != 'radio'){
20163             var hidden =  {
20164                 tag: 'input',
20165                 type : 'hidden',
20166                 cls : 'roo-hidden-value',
20167                 value : this.checked ? this.valueOff : this.inputValue
20168             };
20169         }
20170         
20171             
20172         if (this.weight) { // Validity check?
20173             cfg.cls += " " + this.inputType + "-" + this.weight;
20174         }
20175         
20176         if (this.disabled) {
20177             input.disabled=true;
20178         }
20179         
20180         if(this.checked){
20181             input.checked = this.checked;
20182             
20183         }
20184         
20185         
20186         if (this.name) {
20187             
20188             input.name = this.name;
20189             
20190             if(this.inputType != 'radio'){
20191                 hidden.name = this.name;
20192                 input.name = '_hidden_' + this.name;
20193             }
20194         }
20195         
20196         if (this.size) {
20197             input.cls += ' input-' + this.size;
20198         }
20199         
20200         var settings=this;
20201         
20202         ['xs','sm','md','lg'].map(function(size){
20203             if (settings[size]) {
20204                 cfg.cls += ' col-' + size + '-' + settings[size];
20205             }
20206         });
20207         
20208         var inputblock = input;
20209          
20210         if (this.before || this.after) {
20211             
20212             inputblock = {
20213                 cls : 'input-group',
20214                 cn :  [] 
20215             };
20216             
20217             if (this.before) {
20218                 inputblock.cn.push({
20219                     tag :'span',
20220                     cls : 'input-group-addon',
20221                     html : this.before
20222                 });
20223             }
20224             
20225             inputblock.cn.push(input);
20226             
20227             if(this.inputType != 'radio'){
20228                 inputblock.cn.push(hidden);
20229             }
20230             
20231             if (this.after) {
20232                 inputblock.cn.push({
20233                     tag :'span',
20234                     cls : 'input-group-addon',
20235                     html : this.after
20236                 });
20237             }
20238             
20239         }
20240         
20241         if (align ==='left' && this.fieldLabel.length) {
20242 //                Roo.log("left and has label");
20243             cfg.cn = [
20244                 {
20245                     tag: 'label',
20246                     'for' :  id,
20247                     cls : 'control-label',
20248                     html : this.fieldLabel
20249
20250                 },
20251                 {
20252                     cls : "", 
20253                     cn: [
20254                         inputblock
20255                     ]
20256                 }
20257             ];
20258             
20259             if(this.labelWidth > 12){
20260                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20261             }
20262             
20263             if(this.labelWidth < 13 && this.labelmd == 0){
20264                 this.labelmd = this.labelWidth;
20265             }
20266             
20267             if(this.labellg > 0){
20268                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20269                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20270             }
20271             
20272             if(this.labelmd > 0){
20273                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20274                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20275             }
20276             
20277             if(this.labelsm > 0){
20278                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20279                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20280             }
20281             
20282             if(this.labelxs > 0){
20283                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20284                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20285             }
20286             
20287         } else if ( this.fieldLabel.length) {
20288 //                Roo.log(" label");
20289                 cfg.cn = [
20290                    
20291                     {
20292                         tag: this.boxLabel ? 'span' : 'label',
20293                         'for': id,
20294                         cls: 'control-label box-input-label',
20295                         //cls : 'input-group-addon',
20296                         html : this.fieldLabel
20297                         
20298                     },
20299                     
20300                     inputblock
20301                     
20302                 ];
20303
20304         } else {
20305             
20306 //                Roo.log(" no label && no align");
20307                 cfg.cn = [  inputblock ] ;
20308                 
20309                 
20310         }
20311         
20312         if(this.boxLabel){
20313              var boxLabelCfg = {
20314                 tag: 'label',
20315                 //'for': id, // box label is handled by onclick - so no for...
20316                 cls: 'box-label',
20317                 html: this.boxLabel
20318             };
20319             
20320             if(this.tooltip){
20321                 boxLabelCfg.tooltip = this.tooltip;
20322             }
20323              
20324             cfg.cn.push(boxLabelCfg);
20325         }
20326         
20327         if(this.inputType != 'radio'){
20328             cfg.cn.push(hidden);
20329         }
20330         
20331         return cfg;
20332         
20333     },
20334     
20335     /**
20336      * return the real input element.
20337      */
20338     inputEl: function ()
20339     {
20340         return this.el.select('input.roo-' + this.inputType,true).first();
20341     },
20342     hiddenEl: function ()
20343     {
20344         return this.el.select('input.roo-hidden-value',true).first();
20345     },
20346     
20347     labelEl: function()
20348     {
20349         return this.el.select('label.control-label',true).first();
20350     },
20351     /* depricated... */
20352     
20353     label: function()
20354     {
20355         return this.labelEl();
20356     },
20357     
20358     boxLabelEl: function()
20359     {
20360         return this.el.select('label.box-label',true).first();
20361     },
20362     
20363     initEvents : function()
20364     {
20365 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20366         
20367         this.inputEl().on('click', this.onClick,  this);
20368         
20369         if (this.boxLabel) { 
20370             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20371         }
20372         
20373         this.startValue = this.getValue();
20374         
20375         if(this.groupId){
20376             Roo.bootstrap.CheckBox.register(this);
20377         }
20378     },
20379     
20380     onClick : function()
20381     {   
20382         this.setChecked(!this.checked);
20383     },
20384     
20385     setChecked : function(state,suppressEvent)
20386     {
20387         this.startValue = this.getValue();
20388
20389         if(this.inputType == 'radio'){
20390             
20391             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20392                 e.dom.checked = false;
20393             });
20394             
20395             this.inputEl().dom.checked = true;
20396             
20397             this.inputEl().dom.value = this.inputValue;
20398             
20399             if(suppressEvent !== true){
20400                 this.fireEvent('check', this, true);
20401             }
20402             
20403             this.validate();
20404             
20405             return;
20406         }
20407         
20408         this.checked = state;
20409         
20410         this.inputEl().dom.checked = state;
20411         
20412         
20413         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20414         
20415         if(suppressEvent !== true){
20416             this.fireEvent('check', this, state);
20417         }
20418         
20419         this.validate();
20420     },
20421     
20422     getValue : function()
20423     {
20424         if(this.inputType == 'radio'){
20425             return this.getGroupValue();
20426         }
20427         
20428         return this.hiddenEl().dom.value;
20429         
20430     },
20431     
20432     getGroupValue : function()
20433     {
20434         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20435             return '';
20436         }
20437         
20438         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20439     },
20440     
20441     setValue : function(v,suppressEvent)
20442     {
20443         if(this.inputType == 'radio'){
20444             this.setGroupValue(v, suppressEvent);
20445             return;
20446         }
20447         
20448         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20449         
20450         this.validate();
20451     },
20452     
20453     setGroupValue : function(v, suppressEvent)
20454     {
20455         this.startValue = this.getValue();
20456         
20457         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20458             e.dom.checked = false;
20459             
20460             if(e.dom.value == v){
20461                 e.dom.checked = true;
20462             }
20463         });
20464         
20465         if(suppressEvent !== true){
20466             this.fireEvent('check', this, true);
20467         }
20468
20469         this.validate();
20470         
20471         return;
20472     },
20473     
20474     validate : function()
20475     {
20476         if(
20477                 this.disabled || 
20478                 (this.inputType == 'radio' && this.validateRadio()) ||
20479                 (this.inputType == 'checkbox' && this.validateCheckbox())
20480         ){
20481             this.markValid();
20482             return true;
20483         }
20484         
20485         this.markInvalid();
20486         return false;
20487     },
20488     
20489     validateRadio : function()
20490     {
20491         if(this.allowBlank){
20492             return true;
20493         }
20494         
20495         var valid = false;
20496         
20497         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20498             if(!e.dom.checked){
20499                 return;
20500             }
20501             
20502             valid = true;
20503             
20504             return false;
20505         });
20506         
20507         return valid;
20508     },
20509     
20510     validateCheckbox : function()
20511     {
20512         if(!this.groupId){
20513             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20514             //return (this.getValue() == this.inputValue) ? true : false;
20515         }
20516         
20517         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20518         
20519         if(!group){
20520             return false;
20521         }
20522         
20523         var r = false;
20524         
20525         for(var i in group){
20526             if(r){
20527                 break;
20528             }
20529             
20530             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20531         }
20532         
20533         return r;
20534     },
20535     
20536     /**
20537      * Mark this field as valid
20538      */
20539     markValid : function()
20540     {
20541         var _this = this;
20542         
20543         this.fireEvent('valid', this);
20544         
20545         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20546         
20547         if(this.groupId){
20548             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20549         }
20550         
20551         if(label){
20552             label.markValid();
20553         }
20554
20555         if(this.inputType == 'radio'){
20556             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20557                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20558                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20559             });
20560             
20561             return;
20562         }
20563
20564         if(!this.groupId){
20565             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20566             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20567             return;
20568         }
20569         
20570         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20571         
20572         if(!group){
20573             return;
20574         }
20575         
20576         for(var i in group){
20577             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20578             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20579         }
20580     },
20581     
20582      /**
20583      * Mark this field as invalid
20584      * @param {String} msg The validation message
20585      */
20586     markInvalid : function(msg)
20587     {
20588         if(this.allowBlank){
20589             return;
20590         }
20591         
20592         var _this = this;
20593         
20594         this.fireEvent('invalid', this, msg);
20595         
20596         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20597         
20598         if(this.groupId){
20599             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20600         }
20601         
20602         if(label){
20603             label.markInvalid();
20604         }
20605             
20606         if(this.inputType == 'radio'){
20607             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20608                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20609                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20610             });
20611             
20612             return;
20613         }
20614         
20615         if(!this.groupId){
20616             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20617             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20618             return;
20619         }
20620         
20621         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20622         
20623         if(!group){
20624             return;
20625         }
20626         
20627         for(var i in group){
20628             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20629             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20630         }
20631         
20632     },
20633     
20634     clearInvalid : function()
20635     {
20636         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20637         
20638         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20639         
20640         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20641         
20642         if (label) {
20643             label.iconEl.removeClass(label.validClass);
20644             label.iconEl.removeClass(label.invalidClass);
20645         }
20646     },
20647     
20648     disable : function()
20649     {
20650         if(this.inputType != 'radio'){
20651             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20652             return;
20653         }
20654         
20655         var _this = this;
20656         
20657         if(this.rendered){
20658             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20659                 _this.getActionEl().addClass(this.disabledClass);
20660                 e.dom.disabled = true;
20661             });
20662         }
20663         
20664         this.disabled = true;
20665         this.fireEvent("disable", this);
20666         return this;
20667     },
20668
20669     enable : function()
20670     {
20671         if(this.inputType != 'radio'){
20672             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20673             return;
20674         }
20675         
20676         var _this = this;
20677         
20678         if(this.rendered){
20679             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20680                 _this.getActionEl().removeClass(this.disabledClass);
20681                 e.dom.disabled = false;
20682             });
20683         }
20684         
20685         this.disabled = false;
20686         this.fireEvent("enable", this);
20687         return this;
20688     }
20689
20690 });
20691
20692 Roo.apply(Roo.bootstrap.CheckBox, {
20693     
20694     groups: {},
20695     
20696      /**
20697     * register a CheckBox Group
20698     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20699     */
20700     register : function(checkbox)
20701     {
20702         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20703             this.groups[checkbox.groupId] = {};
20704         }
20705         
20706         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20707             return;
20708         }
20709         
20710         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20711         
20712     },
20713     /**
20714     * fetch a CheckBox Group based on the group ID
20715     * @param {string} the group ID
20716     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20717     */
20718     get: function(groupId) {
20719         if (typeof(this.groups[groupId]) == 'undefined') {
20720             return false;
20721         }
20722         
20723         return this.groups[groupId] ;
20724     }
20725     
20726     
20727 });
20728 /*
20729  * - LGPL
20730  *
20731  * RadioItem
20732  * 
20733  */
20734
20735 /**
20736  * @class Roo.bootstrap.Radio
20737  * @extends Roo.bootstrap.Component
20738  * Bootstrap Radio class
20739  * @cfg {String} boxLabel - the label associated
20740  * @cfg {String} value - the value of radio
20741  * 
20742  * @constructor
20743  * Create a new Radio
20744  * @param {Object} config The config object
20745  */
20746 Roo.bootstrap.Radio = function(config){
20747     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20748     
20749 };
20750
20751 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20752     
20753     boxLabel : '',
20754     
20755     value : '',
20756     
20757     getAutoCreate : function()
20758     {
20759         var cfg = {
20760             tag : 'div',
20761             cls : 'form-group radio',
20762             cn : [
20763                 {
20764                     tag : 'label',
20765                     cls : 'box-label',
20766                     html : this.boxLabel
20767                 }
20768             ]
20769         };
20770         
20771         return cfg;
20772     },
20773     
20774     initEvents : function() 
20775     {
20776         this.parent().register(this);
20777         
20778         this.el.on('click', this.onClick, this);
20779         
20780     },
20781     
20782     onClick : function()
20783     {
20784         this.setChecked(true);
20785     },
20786     
20787     setChecked : function(state, suppressEvent)
20788     {
20789         this.parent().setValue(this.value, suppressEvent);
20790         
20791     },
20792     
20793     setBoxLabel : function(v)
20794     {
20795         this.boxLabel = v;
20796         
20797         if(this.rendered){
20798             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20799         }
20800     }
20801     
20802 });
20803  
20804
20805  /*
20806  * - LGPL
20807  *
20808  * Input
20809  * 
20810  */
20811
20812 /**
20813  * @class Roo.bootstrap.SecurePass
20814  * @extends Roo.bootstrap.Input
20815  * Bootstrap SecurePass class
20816  *
20817  * 
20818  * @constructor
20819  * Create a new SecurePass
20820  * @param {Object} config The config object
20821  */
20822  
20823 Roo.bootstrap.SecurePass = function (config) {
20824     // these go here, so the translation tool can replace them..
20825     this.errors = {
20826         PwdEmpty: "Please type a password, and then retype it to confirm.",
20827         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20828         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20829         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20830         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20831         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20832         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20833         TooWeak: "Your password is Too Weak."
20834     },
20835     this.meterLabel = "Password strength:";
20836     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20837     this.meterClass = [
20838         "roo-password-meter-tooweak", 
20839         "roo-password-meter-weak", 
20840         "roo-password-meter-medium", 
20841         "roo-password-meter-strong", 
20842         "roo-password-meter-grey"
20843     ];
20844     
20845     this.errors = {};
20846     
20847     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20848 }
20849
20850 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20851     /**
20852      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20853      * {
20854      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20855      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20856      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20857      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20858      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20859      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20860      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20861      * })
20862      */
20863     // private
20864     
20865     meterWidth: 300,
20866     errorMsg :'',    
20867     errors: false,
20868     imageRoot: '/',
20869     /**
20870      * @cfg {String/Object} Label for the strength meter (defaults to
20871      * 'Password strength:')
20872      */
20873     // private
20874     meterLabel: '',
20875     /**
20876      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20877      * ['Weak', 'Medium', 'Strong'])
20878      */
20879     // private    
20880     pwdStrengths: false,    
20881     // private
20882     strength: 0,
20883     // private
20884     _lastPwd: null,
20885     // private
20886     kCapitalLetter: 0,
20887     kSmallLetter: 1,
20888     kDigit: 2,
20889     kPunctuation: 3,
20890     
20891     insecure: false,
20892     // private
20893     initEvents: function ()
20894     {
20895         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20896
20897         if (this.el.is('input[type=password]') && Roo.isSafari) {
20898             this.el.on('keydown', this.SafariOnKeyDown, this);
20899         }
20900
20901         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20902     },
20903     // private
20904     onRender: function (ct, position)
20905     {
20906         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20907         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20908         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20909
20910         this.trigger.createChild({
20911                    cn: [
20912                     {
20913                     //id: 'PwdMeter',
20914                     tag: 'div',
20915                     cls: 'roo-password-meter-grey col-xs-12',
20916                     style: {
20917                         //width: 0,
20918                         //width: this.meterWidth + 'px'                                                
20919                         }
20920                     },
20921                     {                            
20922                          cls: 'roo-password-meter-text'                          
20923                     }
20924                 ]            
20925         });
20926
20927          
20928         if (this.hideTrigger) {
20929             this.trigger.setDisplayed(false);
20930         }
20931         this.setSize(this.width || '', this.height || '');
20932     },
20933     // private
20934     onDestroy: function ()
20935     {
20936         if (this.trigger) {
20937             this.trigger.removeAllListeners();
20938             this.trigger.remove();
20939         }
20940         if (this.wrap) {
20941             this.wrap.remove();
20942         }
20943         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20944     },
20945     // private
20946     checkStrength: function ()
20947     {
20948         var pwd = this.inputEl().getValue();
20949         if (pwd == this._lastPwd) {
20950             return;
20951         }
20952
20953         var strength;
20954         if (this.ClientSideStrongPassword(pwd)) {
20955             strength = 3;
20956         } else if (this.ClientSideMediumPassword(pwd)) {
20957             strength = 2;
20958         } else if (this.ClientSideWeakPassword(pwd)) {
20959             strength = 1;
20960         } else {
20961             strength = 0;
20962         }
20963         
20964         Roo.log('strength1: ' + strength);
20965         
20966         //var pm = this.trigger.child('div/div/div').dom;
20967         var pm = this.trigger.child('div/div');
20968         pm.removeClass(this.meterClass);
20969         pm.addClass(this.meterClass[strength]);
20970                 
20971         
20972         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20973                 
20974         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20975         
20976         this._lastPwd = pwd;
20977     },
20978     reset: function ()
20979     {
20980         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20981         
20982         this._lastPwd = '';
20983         
20984         var pm = this.trigger.child('div/div');
20985         pm.removeClass(this.meterClass);
20986         pm.addClass('roo-password-meter-grey');        
20987         
20988         
20989         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20990         
20991         pt.innerHTML = '';
20992         this.inputEl().dom.type='password';
20993     },
20994     // private
20995     validateValue: function (value)
20996     {
20997         
20998         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20999             return false;
21000         }
21001         if (value.length == 0) {
21002             if (this.allowBlank) {
21003                 this.clearInvalid();
21004                 return true;
21005             }
21006
21007             this.markInvalid(this.errors.PwdEmpty);
21008             this.errorMsg = this.errors.PwdEmpty;
21009             return false;
21010         }
21011         
21012         if(this.insecure){
21013             return true;
21014         }
21015         
21016         if ('[\x21-\x7e]*'.match(value)) {
21017             this.markInvalid(this.errors.PwdBadChar);
21018             this.errorMsg = this.errors.PwdBadChar;
21019             return false;
21020         }
21021         if (value.length < 6) {
21022             this.markInvalid(this.errors.PwdShort);
21023             this.errorMsg = this.errors.PwdShort;
21024             return false;
21025         }
21026         if (value.length > 16) {
21027             this.markInvalid(this.errors.PwdLong);
21028             this.errorMsg = this.errors.PwdLong;
21029             return false;
21030         }
21031         var strength;
21032         if (this.ClientSideStrongPassword(value)) {
21033             strength = 3;
21034         } else if (this.ClientSideMediumPassword(value)) {
21035             strength = 2;
21036         } else if (this.ClientSideWeakPassword(value)) {
21037             strength = 1;
21038         } else {
21039             strength = 0;
21040         }
21041
21042         
21043         if (strength < 2) {
21044             //this.markInvalid(this.errors.TooWeak);
21045             this.errorMsg = this.errors.TooWeak;
21046             //return false;
21047         }
21048         
21049         
21050         console.log('strength2: ' + strength);
21051         
21052         //var pm = this.trigger.child('div/div/div').dom;
21053         
21054         var pm = this.trigger.child('div/div');
21055         pm.removeClass(this.meterClass);
21056         pm.addClass(this.meterClass[strength]);
21057                 
21058         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21059                 
21060         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21061         
21062         this.errorMsg = ''; 
21063         return true;
21064     },
21065     // private
21066     CharacterSetChecks: function (type)
21067     {
21068         this.type = type;
21069         this.fResult = false;
21070     },
21071     // private
21072     isctype: function (character, type)
21073     {
21074         switch (type) {  
21075             case this.kCapitalLetter:
21076                 if (character >= 'A' && character <= 'Z') {
21077                     return true;
21078                 }
21079                 break;
21080             
21081             case this.kSmallLetter:
21082                 if (character >= 'a' && character <= 'z') {
21083                     return true;
21084                 }
21085                 break;
21086             
21087             case this.kDigit:
21088                 if (character >= '0' && character <= '9') {
21089                     return true;
21090                 }
21091                 break;
21092             
21093             case this.kPunctuation:
21094                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21095                     return true;
21096                 }
21097                 break;
21098             
21099             default:
21100                 return false;
21101         }
21102
21103     },
21104     // private
21105     IsLongEnough: function (pwd, size)
21106     {
21107         return !(pwd == null || isNaN(size) || pwd.length < size);
21108     },
21109     // private
21110     SpansEnoughCharacterSets: function (word, nb)
21111     {
21112         if (!this.IsLongEnough(word, nb))
21113         {
21114             return false;
21115         }
21116
21117         var characterSetChecks = new Array(
21118             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21119             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21120         );
21121         
21122         for (var index = 0; index < word.length; ++index) {
21123             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21124                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21125                     characterSetChecks[nCharSet].fResult = true;
21126                     break;
21127                 }
21128             }
21129         }
21130
21131         var nCharSets = 0;
21132         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21133             if (characterSetChecks[nCharSet].fResult) {
21134                 ++nCharSets;
21135             }
21136         }
21137
21138         if (nCharSets < nb) {
21139             return false;
21140         }
21141         return true;
21142     },
21143     // private
21144     ClientSideStrongPassword: function (pwd)
21145     {
21146         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21147     },
21148     // private
21149     ClientSideMediumPassword: function (pwd)
21150     {
21151         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21152     },
21153     // private
21154     ClientSideWeakPassword: function (pwd)
21155     {
21156         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21157     }
21158           
21159 })//<script type="text/javascript">
21160
21161 /*
21162  * Based  Ext JS Library 1.1.1
21163  * Copyright(c) 2006-2007, Ext JS, LLC.
21164  * LGPL
21165  *
21166  */
21167  
21168 /**
21169  * @class Roo.HtmlEditorCore
21170  * @extends Roo.Component
21171  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21172  *
21173  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21174  */
21175
21176 Roo.HtmlEditorCore = function(config){
21177     
21178     
21179     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21180     
21181     
21182     this.addEvents({
21183         /**
21184          * @event initialize
21185          * Fires when the editor is fully initialized (including the iframe)
21186          * @param {Roo.HtmlEditorCore} this
21187          */
21188         initialize: true,
21189         /**
21190          * @event activate
21191          * Fires when the editor is first receives the focus. Any insertion must wait
21192          * until after this event.
21193          * @param {Roo.HtmlEditorCore} this
21194          */
21195         activate: true,
21196          /**
21197          * @event beforesync
21198          * Fires before the textarea is updated with content from the editor iframe. Return false
21199          * to cancel the sync.
21200          * @param {Roo.HtmlEditorCore} this
21201          * @param {String} html
21202          */
21203         beforesync: true,
21204          /**
21205          * @event beforepush
21206          * Fires before the iframe editor is updated with content from the textarea. Return false
21207          * to cancel the push.
21208          * @param {Roo.HtmlEditorCore} this
21209          * @param {String} html
21210          */
21211         beforepush: true,
21212          /**
21213          * @event sync
21214          * Fires when the textarea is updated with content from the editor iframe.
21215          * @param {Roo.HtmlEditorCore} this
21216          * @param {String} html
21217          */
21218         sync: true,
21219          /**
21220          * @event push
21221          * Fires when the iframe editor is updated with content from the textarea.
21222          * @param {Roo.HtmlEditorCore} this
21223          * @param {String} html
21224          */
21225         push: true,
21226         
21227         /**
21228          * @event editorevent
21229          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21230          * @param {Roo.HtmlEditorCore} this
21231          */
21232         editorevent: true
21233         
21234     });
21235     
21236     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21237     
21238     // defaults : white / black...
21239     this.applyBlacklists();
21240     
21241     
21242     
21243 };
21244
21245
21246 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21247
21248
21249      /**
21250      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21251      */
21252     
21253     owner : false,
21254     
21255      /**
21256      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21257      *                        Roo.resizable.
21258      */
21259     resizable : false,
21260      /**
21261      * @cfg {Number} height (in pixels)
21262      */   
21263     height: 300,
21264    /**
21265      * @cfg {Number} width (in pixels)
21266      */   
21267     width: 500,
21268     
21269     /**
21270      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21271      * 
21272      */
21273     stylesheets: false,
21274     
21275     // id of frame..
21276     frameId: false,
21277     
21278     // private properties
21279     validationEvent : false,
21280     deferHeight: true,
21281     initialized : false,
21282     activated : false,
21283     sourceEditMode : false,
21284     onFocus : Roo.emptyFn,
21285     iframePad:3,
21286     hideMode:'offsets',
21287     
21288     clearUp: true,
21289     
21290     // blacklist + whitelisted elements..
21291     black: false,
21292     white: false,
21293      
21294     bodyCls : '',
21295
21296     /**
21297      * Protected method that will not generally be called directly. It
21298      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21299      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21300      */
21301     getDocMarkup : function(){
21302         // body styles..
21303         var st = '';
21304         
21305         // inherit styels from page...?? 
21306         if (this.stylesheets === false) {
21307             
21308             Roo.get(document.head).select('style').each(function(node) {
21309                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21310             });
21311             
21312             Roo.get(document.head).select('link').each(function(node) { 
21313                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21314             });
21315             
21316         } else if (!this.stylesheets.length) {
21317                 // simple..
21318                 st = '<style type="text/css">' +
21319                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21320                    '</style>';
21321         } else { 
21322             st = '<style type="text/css">' +
21323                     this.stylesheets +
21324                 '</style>';
21325         }
21326         
21327         st +=  '<style type="text/css">' +
21328             'IMG { cursor: pointer } ' +
21329         '</style>';
21330
21331         var cls = 'roo-htmleditor-body';
21332         
21333         if(this.bodyCls.length){
21334             cls += ' ' + this.bodyCls;
21335         }
21336         
21337         return '<html><head>' + st  +
21338             //<style type="text/css">' +
21339             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21340             //'</style>' +
21341             ' </head><body class="' +  cls + '"></body></html>';
21342     },
21343
21344     // private
21345     onRender : function(ct, position)
21346     {
21347         var _t = this;
21348         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21349         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21350         
21351         
21352         this.el.dom.style.border = '0 none';
21353         this.el.dom.setAttribute('tabIndex', -1);
21354         this.el.addClass('x-hidden hide');
21355         
21356         
21357         
21358         if(Roo.isIE){ // fix IE 1px bogus margin
21359             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21360         }
21361        
21362         
21363         this.frameId = Roo.id();
21364         
21365          
21366         
21367         var iframe = this.owner.wrap.createChild({
21368             tag: 'iframe',
21369             cls: 'form-control', // bootstrap..
21370             id: this.frameId,
21371             name: this.frameId,
21372             frameBorder : 'no',
21373             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21374         }, this.el
21375         );
21376         
21377         
21378         this.iframe = iframe.dom;
21379
21380          this.assignDocWin();
21381         
21382         this.doc.designMode = 'on';
21383        
21384         this.doc.open();
21385         this.doc.write(this.getDocMarkup());
21386         this.doc.close();
21387
21388         
21389         var task = { // must defer to wait for browser to be ready
21390             run : function(){
21391                 //console.log("run task?" + this.doc.readyState);
21392                 this.assignDocWin();
21393                 if(this.doc.body || this.doc.readyState == 'complete'){
21394                     try {
21395                         this.doc.designMode="on";
21396                     } catch (e) {
21397                         return;
21398                     }
21399                     Roo.TaskMgr.stop(task);
21400                     this.initEditor.defer(10, this);
21401                 }
21402             },
21403             interval : 10,
21404             duration: 10000,
21405             scope: this
21406         };
21407         Roo.TaskMgr.start(task);
21408
21409     },
21410
21411     // private
21412     onResize : function(w, h)
21413     {
21414          Roo.log('resize: ' +w + ',' + h );
21415         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21416         if(!this.iframe){
21417             return;
21418         }
21419         if(typeof w == 'number'){
21420             
21421             this.iframe.style.width = w + 'px';
21422         }
21423         if(typeof h == 'number'){
21424             
21425             this.iframe.style.height = h + 'px';
21426             if(this.doc){
21427                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21428             }
21429         }
21430         
21431     },
21432
21433     /**
21434      * Toggles the editor between standard and source edit mode.
21435      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21436      */
21437     toggleSourceEdit : function(sourceEditMode){
21438         
21439         this.sourceEditMode = sourceEditMode === true;
21440         
21441         if(this.sourceEditMode){
21442  
21443             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21444             
21445         }else{
21446             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21447             //this.iframe.className = '';
21448             this.deferFocus();
21449         }
21450         //this.setSize(this.owner.wrap.getSize());
21451         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21452     },
21453
21454     
21455   
21456
21457     /**
21458      * Protected method that will not generally be called directly. If you need/want
21459      * custom HTML cleanup, this is the method you should override.
21460      * @param {String} html The HTML to be cleaned
21461      * return {String} The cleaned HTML
21462      */
21463     cleanHtml : function(html){
21464         html = String(html);
21465         if(html.length > 5){
21466             if(Roo.isSafari){ // strip safari nonsense
21467                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21468             }
21469         }
21470         if(html == '&nbsp;'){
21471             html = '';
21472         }
21473         return html;
21474     },
21475
21476     /**
21477      * HTML Editor -> Textarea
21478      * Protected method that will not generally be called directly. Syncs the contents
21479      * of the editor iframe with the textarea.
21480      */
21481     syncValue : function(){
21482         if(this.initialized){
21483             var bd = (this.doc.body || this.doc.documentElement);
21484             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21485             var html = bd.innerHTML;
21486             if(Roo.isSafari){
21487                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21488                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21489                 if(m && m[1]){
21490                     html = '<div style="'+m[0]+'">' + html + '</div>';
21491                 }
21492             }
21493             html = this.cleanHtml(html);
21494             // fix up the special chars.. normaly like back quotes in word...
21495             // however we do not want to do this with chinese..
21496             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21497                 var cc = b.charCodeAt();
21498                 if (
21499                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21500                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21501                     (cc >= 0xf900 && cc < 0xfb00 )
21502                 ) {
21503                         return b;
21504                 }
21505                 return "&#"+cc+";" 
21506             });
21507             if(this.owner.fireEvent('beforesync', this, html) !== false){
21508                 this.el.dom.value = html;
21509                 this.owner.fireEvent('sync', this, html);
21510             }
21511         }
21512     },
21513
21514     /**
21515      * Protected method that will not generally be called directly. Pushes the value of the textarea
21516      * into the iframe editor.
21517      */
21518     pushValue : function(){
21519         if(this.initialized){
21520             var v = this.el.dom.value.trim();
21521             
21522 //            if(v.length < 1){
21523 //                v = '&#160;';
21524 //            }
21525             
21526             if(this.owner.fireEvent('beforepush', this, v) !== false){
21527                 var d = (this.doc.body || this.doc.documentElement);
21528                 d.innerHTML = v;
21529                 this.cleanUpPaste();
21530                 this.el.dom.value = d.innerHTML;
21531                 this.owner.fireEvent('push', this, v);
21532             }
21533         }
21534     },
21535
21536     // private
21537     deferFocus : function(){
21538         this.focus.defer(10, this);
21539     },
21540
21541     // doc'ed in Field
21542     focus : function(){
21543         if(this.win && !this.sourceEditMode){
21544             this.win.focus();
21545         }else{
21546             this.el.focus();
21547         }
21548     },
21549     
21550     assignDocWin: function()
21551     {
21552         var iframe = this.iframe;
21553         
21554          if(Roo.isIE){
21555             this.doc = iframe.contentWindow.document;
21556             this.win = iframe.contentWindow;
21557         } else {
21558 //            if (!Roo.get(this.frameId)) {
21559 //                return;
21560 //            }
21561 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21562 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21563             
21564             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21565                 return;
21566             }
21567             
21568             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21569             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21570         }
21571     },
21572     
21573     // private
21574     initEditor : function(){
21575         //console.log("INIT EDITOR");
21576         this.assignDocWin();
21577         
21578         
21579         
21580         this.doc.designMode="on";
21581         this.doc.open();
21582         this.doc.write(this.getDocMarkup());
21583         this.doc.close();
21584         
21585         var dbody = (this.doc.body || this.doc.documentElement);
21586         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21587         // this copies styles from the containing element into thsi one..
21588         // not sure why we need all of this..
21589         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21590         
21591         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21592         //ss['background-attachment'] = 'fixed'; // w3c
21593         dbody.bgProperties = 'fixed'; // ie
21594         //Roo.DomHelper.applyStyles(dbody, ss);
21595         Roo.EventManager.on(this.doc, {
21596             //'mousedown': this.onEditorEvent,
21597             'mouseup': this.onEditorEvent,
21598             'dblclick': this.onEditorEvent,
21599             'click': this.onEditorEvent,
21600             'keyup': this.onEditorEvent,
21601             buffer:100,
21602             scope: this
21603         });
21604         if(Roo.isGecko){
21605             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21606         }
21607         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21608             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21609         }
21610         this.initialized = true;
21611
21612         this.owner.fireEvent('initialize', this);
21613         this.pushValue();
21614     },
21615
21616     // private
21617     onDestroy : function(){
21618         
21619         
21620         
21621         if(this.rendered){
21622             
21623             //for (var i =0; i < this.toolbars.length;i++) {
21624             //    // fixme - ask toolbars for heights?
21625             //    this.toolbars[i].onDestroy();
21626            // }
21627             
21628             //this.wrap.dom.innerHTML = '';
21629             //this.wrap.remove();
21630         }
21631     },
21632
21633     // private
21634     onFirstFocus : function(){
21635         
21636         this.assignDocWin();
21637         
21638         
21639         this.activated = true;
21640          
21641     
21642         if(Roo.isGecko){ // prevent silly gecko errors
21643             this.win.focus();
21644             var s = this.win.getSelection();
21645             if(!s.focusNode || s.focusNode.nodeType != 3){
21646                 var r = s.getRangeAt(0);
21647                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21648                 r.collapse(true);
21649                 this.deferFocus();
21650             }
21651             try{
21652                 this.execCmd('useCSS', true);
21653                 this.execCmd('styleWithCSS', false);
21654             }catch(e){}
21655         }
21656         this.owner.fireEvent('activate', this);
21657     },
21658
21659     // private
21660     adjustFont: function(btn){
21661         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21662         //if(Roo.isSafari){ // safari
21663         //    adjust *= 2;
21664        // }
21665         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21666         if(Roo.isSafari){ // safari
21667             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21668             v =  (v < 10) ? 10 : v;
21669             v =  (v > 48) ? 48 : v;
21670             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21671             
21672         }
21673         
21674         
21675         v = Math.max(1, v+adjust);
21676         
21677         this.execCmd('FontSize', v  );
21678     },
21679
21680     onEditorEvent : function(e)
21681     {
21682         this.owner.fireEvent('editorevent', this, e);
21683       //  this.updateToolbar();
21684         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21685     },
21686
21687     insertTag : function(tg)
21688     {
21689         // could be a bit smarter... -> wrap the current selected tRoo..
21690         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21691             
21692             range = this.createRange(this.getSelection());
21693             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21694             wrappingNode.appendChild(range.extractContents());
21695             range.insertNode(wrappingNode);
21696
21697             return;
21698             
21699             
21700             
21701         }
21702         this.execCmd("formatblock",   tg);
21703         
21704     },
21705     
21706     insertText : function(txt)
21707     {
21708         
21709         
21710         var range = this.createRange();
21711         range.deleteContents();
21712                //alert(Sender.getAttribute('label'));
21713                
21714         range.insertNode(this.doc.createTextNode(txt));
21715     } ,
21716     
21717      
21718
21719     /**
21720      * Executes a Midas editor command on the editor document and performs necessary focus and
21721      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21722      * @param {String} cmd The Midas command
21723      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21724      */
21725     relayCmd : function(cmd, value){
21726         this.win.focus();
21727         this.execCmd(cmd, value);
21728         this.owner.fireEvent('editorevent', this);
21729         //this.updateToolbar();
21730         this.owner.deferFocus();
21731     },
21732
21733     /**
21734      * Executes a Midas editor command directly on the editor document.
21735      * For visual commands, you should use {@link #relayCmd} instead.
21736      * <b>This should only be called after the editor is initialized.</b>
21737      * @param {String} cmd The Midas command
21738      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21739      */
21740     execCmd : function(cmd, value){
21741         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21742         this.syncValue();
21743     },
21744  
21745  
21746    
21747     /**
21748      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21749      * to insert tRoo.
21750      * @param {String} text | dom node.. 
21751      */
21752     insertAtCursor : function(text)
21753     {
21754         
21755         if(!this.activated){
21756             return;
21757         }
21758         /*
21759         if(Roo.isIE){
21760             this.win.focus();
21761             var r = this.doc.selection.createRange();
21762             if(r){
21763                 r.collapse(true);
21764                 r.pasteHTML(text);
21765                 this.syncValue();
21766                 this.deferFocus();
21767             
21768             }
21769             return;
21770         }
21771         */
21772         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21773             this.win.focus();
21774             
21775             
21776             // from jquery ui (MIT licenced)
21777             var range, node;
21778             var win = this.win;
21779             
21780             if (win.getSelection && win.getSelection().getRangeAt) {
21781                 range = win.getSelection().getRangeAt(0);
21782                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21783                 range.insertNode(node);
21784             } else if (win.document.selection && win.document.selection.createRange) {
21785                 // no firefox support
21786                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21787                 win.document.selection.createRange().pasteHTML(txt);
21788             } else {
21789                 // no firefox support
21790                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21791                 this.execCmd('InsertHTML', txt);
21792             } 
21793             
21794             this.syncValue();
21795             
21796             this.deferFocus();
21797         }
21798     },
21799  // private
21800     mozKeyPress : function(e){
21801         if(e.ctrlKey){
21802             var c = e.getCharCode(), cmd;
21803           
21804             if(c > 0){
21805                 c = String.fromCharCode(c).toLowerCase();
21806                 switch(c){
21807                     case 'b':
21808                         cmd = 'bold';
21809                         break;
21810                     case 'i':
21811                         cmd = 'italic';
21812                         break;
21813                     
21814                     case 'u':
21815                         cmd = 'underline';
21816                         break;
21817                     
21818                     case 'v':
21819                         this.cleanUpPaste.defer(100, this);
21820                         return;
21821                         
21822                 }
21823                 if(cmd){
21824                     this.win.focus();
21825                     this.execCmd(cmd);
21826                     this.deferFocus();
21827                     e.preventDefault();
21828                 }
21829                 
21830             }
21831         }
21832     },
21833
21834     // private
21835     fixKeys : function(){ // load time branching for fastest keydown performance
21836         if(Roo.isIE){
21837             return function(e){
21838                 var k = e.getKey(), r;
21839                 if(k == e.TAB){
21840                     e.stopEvent();
21841                     r = this.doc.selection.createRange();
21842                     if(r){
21843                         r.collapse(true);
21844                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21845                         this.deferFocus();
21846                     }
21847                     return;
21848                 }
21849                 
21850                 if(k == e.ENTER){
21851                     r = this.doc.selection.createRange();
21852                     if(r){
21853                         var target = r.parentElement();
21854                         if(!target || target.tagName.toLowerCase() != 'li'){
21855                             e.stopEvent();
21856                             r.pasteHTML('<br />');
21857                             r.collapse(false);
21858                             r.select();
21859                         }
21860                     }
21861                 }
21862                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21863                     this.cleanUpPaste.defer(100, this);
21864                     return;
21865                 }
21866                 
21867                 
21868             };
21869         }else if(Roo.isOpera){
21870             return function(e){
21871                 var k = e.getKey();
21872                 if(k == e.TAB){
21873                     e.stopEvent();
21874                     this.win.focus();
21875                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21876                     this.deferFocus();
21877                 }
21878                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21879                     this.cleanUpPaste.defer(100, this);
21880                     return;
21881                 }
21882                 
21883             };
21884         }else if(Roo.isSafari){
21885             return function(e){
21886                 var k = e.getKey();
21887                 
21888                 if(k == e.TAB){
21889                     e.stopEvent();
21890                     this.execCmd('InsertText','\t');
21891                     this.deferFocus();
21892                     return;
21893                 }
21894                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21895                     this.cleanUpPaste.defer(100, this);
21896                     return;
21897                 }
21898                 
21899              };
21900         }
21901     }(),
21902     
21903     getAllAncestors: function()
21904     {
21905         var p = this.getSelectedNode();
21906         var a = [];
21907         if (!p) {
21908             a.push(p); // push blank onto stack..
21909             p = this.getParentElement();
21910         }
21911         
21912         
21913         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21914             a.push(p);
21915             p = p.parentNode;
21916         }
21917         a.push(this.doc.body);
21918         return a;
21919     },
21920     lastSel : false,
21921     lastSelNode : false,
21922     
21923     
21924     getSelection : function() 
21925     {
21926         this.assignDocWin();
21927         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21928     },
21929     
21930     getSelectedNode: function() 
21931     {
21932         // this may only work on Gecko!!!
21933         
21934         // should we cache this!!!!
21935         
21936         
21937         
21938          
21939         var range = this.createRange(this.getSelection()).cloneRange();
21940         
21941         if (Roo.isIE) {
21942             var parent = range.parentElement();
21943             while (true) {
21944                 var testRange = range.duplicate();
21945                 testRange.moveToElementText(parent);
21946                 if (testRange.inRange(range)) {
21947                     break;
21948                 }
21949                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21950                     break;
21951                 }
21952                 parent = parent.parentElement;
21953             }
21954             return parent;
21955         }
21956         
21957         // is ancestor a text element.
21958         var ac =  range.commonAncestorContainer;
21959         if (ac.nodeType == 3) {
21960             ac = ac.parentNode;
21961         }
21962         
21963         var ar = ac.childNodes;
21964          
21965         var nodes = [];
21966         var other_nodes = [];
21967         var has_other_nodes = false;
21968         for (var i=0;i<ar.length;i++) {
21969             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21970                 continue;
21971             }
21972             // fullly contained node.
21973             
21974             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21975                 nodes.push(ar[i]);
21976                 continue;
21977             }
21978             
21979             // probably selected..
21980             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21981                 other_nodes.push(ar[i]);
21982                 continue;
21983             }
21984             // outer..
21985             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21986                 continue;
21987             }
21988             
21989             
21990             has_other_nodes = true;
21991         }
21992         if (!nodes.length && other_nodes.length) {
21993             nodes= other_nodes;
21994         }
21995         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21996             return false;
21997         }
21998         
21999         return nodes[0];
22000     },
22001     createRange: function(sel)
22002     {
22003         // this has strange effects when using with 
22004         // top toolbar - not sure if it's a great idea.
22005         //this.editor.contentWindow.focus();
22006         if (typeof sel != "undefined") {
22007             try {
22008                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22009             } catch(e) {
22010                 return this.doc.createRange();
22011             }
22012         } else {
22013             return this.doc.createRange();
22014         }
22015     },
22016     getParentElement: function()
22017     {
22018         
22019         this.assignDocWin();
22020         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22021         
22022         var range = this.createRange(sel);
22023          
22024         try {
22025             var p = range.commonAncestorContainer;
22026             while (p.nodeType == 3) { // text node
22027                 p = p.parentNode;
22028             }
22029             return p;
22030         } catch (e) {
22031             return null;
22032         }
22033     
22034     },
22035     /***
22036      *
22037      * Range intersection.. the hard stuff...
22038      *  '-1' = before
22039      *  '0' = hits..
22040      *  '1' = after.
22041      *         [ -- selected range --- ]
22042      *   [fail]                        [fail]
22043      *
22044      *    basically..
22045      *      if end is before start or  hits it. fail.
22046      *      if start is after end or hits it fail.
22047      *
22048      *   if either hits (but other is outside. - then it's not 
22049      *   
22050      *    
22051      **/
22052     
22053     
22054     // @see http://www.thismuchiknow.co.uk/?p=64.
22055     rangeIntersectsNode : function(range, node)
22056     {
22057         var nodeRange = node.ownerDocument.createRange();
22058         try {
22059             nodeRange.selectNode(node);
22060         } catch (e) {
22061             nodeRange.selectNodeContents(node);
22062         }
22063     
22064         var rangeStartRange = range.cloneRange();
22065         rangeStartRange.collapse(true);
22066     
22067         var rangeEndRange = range.cloneRange();
22068         rangeEndRange.collapse(false);
22069     
22070         var nodeStartRange = nodeRange.cloneRange();
22071         nodeStartRange.collapse(true);
22072     
22073         var nodeEndRange = nodeRange.cloneRange();
22074         nodeEndRange.collapse(false);
22075     
22076         return rangeStartRange.compareBoundaryPoints(
22077                  Range.START_TO_START, nodeEndRange) == -1 &&
22078                rangeEndRange.compareBoundaryPoints(
22079                  Range.START_TO_START, nodeStartRange) == 1;
22080         
22081          
22082     },
22083     rangeCompareNode : function(range, node)
22084     {
22085         var nodeRange = node.ownerDocument.createRange();
22086         try {
22087             nodeRange.selectNode(node);
22088         } catch (e) {
22089             nodeRange.selectNodeContents(node);
22090         }
22091         
22092         
22093         range.collapse(true);
22094     
22095         nodeRange.collapse(true);
22096      
22097         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22098         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22099          
22100         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22101         
22102         var nodeIsBefore   =  ss == 1;
22103         var nodeIsAfter    = ee == -1;
22104         
22105         if (nodeIsBefore && nodeIsAfter) {
22106             return 0; // outer
22107         }
22108         if (!nodeIsBefore && nodeIsAfter) {
22109             return 1; //right trailed.
22110         }
22111         
22112         if (nodeIsBefore && !nodeIsAfter) {
22113             return 2;  // left trailed.
22114         }
22115         // fully contined.
22116         return 3;
22117     },
22118
22119     // private? - in a new class?
22120     cleanUpPaste :  function()
22121     {
22122         // cleans up the whole document..
22123         Roo.log('cleanuppaste');
22124         
22125         this.cleanUpChildren(this.doc.body);
22126         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22127         if (clean != this.doc.body.innerHTML) {
22128             this.doc.body.innerHTML = clean;
22129         }
22130         
22131     },
22132     
22133     cleanWordChars : function(input) {// change the chars to hex code
22134         var he = Roo.HtmlEditorCore;
22135         
22136         var output = input;
22137         Roo.each(he.swapCodes, function(sw) { 
22138             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22139             
22140             output = output.replace(swapper, sw[1]);
22141         });
22142         
22143         return output;
22144     },
22145     
22146     
22147     cleanUpChildren : function (n)
22148     {
22149         if (!n.childNodes.length) {
22150             return;
22151         }
22152         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22153            this.cleanUpChild(n.childNodes[i]);
22154         }
22155     },
22156     
22157     
22158         
22159     
22160     cleanUpChild : function (node)
22161     {
22162         var ed = this;
22163         //console.log(node);
22164         if (node.nodeName == "#text") {
22165             // clean up silly Windows -- stuff?
22166             return; 
22167         }
22168         if (node.nodeName == "#comment") {
22169             node.parentNode.removeChild(node);
22170             // clean up silly Windows -- stuff?
22171             return; 
22172         }
22173         var lcname = node.tagName.toLowerCase();
22174         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22175         // whitelist of tags..
22176         
22177         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22178             // remove node.
22179             node.parentNode.removeChild(node);
22180             return;
22181             
22182         }
22183         
22184         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22185         
22186         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22187         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22188         
22189         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22190         //    remove_keep_children = true;
22191         //}
22192         
22193         if (remove_keep_children) {
22194             this.cleanUpChildren(node);
22195             // inserts everything just before this node...
22196             while (node.childNodes.length) {
22197                 var cn = node.childNodes[0];
22198                 node.removeChild(cn);
22199                 node.parentNode.insertBefore(cn, node);
22200             }
22201             node.parentNode.removeChild(node);
22202             return;
22203         }
22204         
22205         if (!node.attributes || !node.attributes.length) {
22206             this.cleanUpChildren(node);
22207             return;
22208         }
22209         
22210         function cleanAttr(n,v)
22211         {
22212             
22213             if (v.match(/^\./) || v.match(/^\//)) {
22214                 return;
22215             }
22216             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22217                 return;
22218             }
22219             if (v.match(/^#/)) {
22220                 return;
22221             }
22222 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22223             node.removeAttribute(n);
22224             
22225         }
22226         
22227         var cwhite = this.cwhite;
22228         var cblack = this.cblack;
22229             
22230         function cleanStyle(n,v)
22231         {
22232             if (v.match(/expression/)) { //XSS?? should we even bother..
22233                 node.removeAttribute(n);
22234                 return;
22235             }
22236             
22237             var parts = v.split(/;/);
22238             var clean = [];
22239             
22240             Roo.each(parts, function(p) {
22241                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22242                 if (!p.length) {
22243                     return true;
22244                 }
22245                 var l = p.split(':').shift().replace(/\s+/g,'');
22246                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22247                 
22248                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22249 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22250                     //node.removeAttribute(n);
22251                     return true;
22252                 }
22253                 //Roo.log()
22254                 // only allow 'c whitelisted system attributes'
22255                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22256 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22257                     //node.removeAttribute(n);
22258                     return true;
22259                 }
22260                 
22261                 
22262                  
22263                 
22264                 clean.push(p);
22265                 return true;
22266             });
22267             if (clean.length) { 
22268                 node.setAttribute(n, clean.join(';'));
22269             } else {
22270                 node.removeAttribute(n);
22271             }
22272             
22273         }
22274         
22275         
22276         for (var i = node.attributes.length-1; i > -1 ; i--) {
22277             var a = node.attributes[i];
22278             //console.log(a);
22279             
22280             if (a.name.toLowerCase().substr(0,2)=='on')  {
22281                 node.removeAttribute(a.name);
22282                 continue;
22283             }
22284             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22285                 node.removeAttribute(a.name);
22286                 continue;
22287             }
22288             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22289                 cleanAttr(a.name,a.value); // fixme..
22290                 continue;
22291             }
22292             if (a.name == 'style') {
22293                 cleanStyle(a.name,a.value);
22294                 continue;
22295             }
22296             /// clean up MS crap..
22297             // tecnically this should be a list of valid class'es..
22298             
22299             
22300             if (a.name == 'class') {
22301                 if (a.value.match(/^Mso/)) {
22302                     node.className = '';
22303                 }
22304                 
22305                 if (a.value.match(/^body$/)) {
22306                     node.className = '';
22307                 }
22308                 continue;
22309             }
22310             
22311             // style cleanup!?
22312             // class cleanup?
22313             
22314         }
22315         
22316         
22317         this.cleanUpChildren(node);
22318         
22319         
22320     },
22321     
22322     /**
22323      * Clean up MS wordisms...
22324      */
22325     cleanWord : function(node)
22326     {
22327         
22328         
22329         if (!node) {
22330             this.cleanWord(this.doc.body);
22331             return;
22332         }
22333         if (node.nodeName == "#text") {
22334             // clean up silly Windows -- stuff?
22335             return; 
22336         }
22337         if (node.nodeName == "#comment") {
22338             node.parentNode.removeChild(node);
22339             // clean up silly Windows -- stuff?
22340             return; 
22341         }
22342         
22343         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22344             node.parentNode.removeChild(node);
22345             return;
22346         }
22347         
22348         // remove - but keep children..
22349         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22350             while (node.childNodes.length) {
22351                 var cn = node.childNodes[0];
22352                 node.removeChild(cn);
22353                 node.parentNode.insertBefore(cn, node);
22354             }
22355             node.parentNode.removeChild(node);
22356             this.iterateChildren(node, this.cleanWord);
22357             return;
22358         }
22359         // clean styles
22360         if (node.className.length) {
22361             
22362             var cn = node.className.split(/\W+/);
22363             var cna = [];
22364             Roo.each(cn, function(cls) {
22365                 if (cls.match(/Mso[a-zA-Z]+/)) {
22366                     return;
22367                 }
22368                 cna.push(cls);
22369             });
22370             node.className = cna.length ? cna.join(' ') : '';
22371             if (!cna.length) {
22372                 node.removeAttribute("class");
22373             }
22374         }
22375         
22376         if (node.hasAttribute("lang")) {
22377             node.removeAttribute("lang");
22378         }
22379         
22380         if (node.hasAttribute("style")) {
22381             
22382             var styles = node.getAttribute("style").split(";");
22383             var nstyle = [];
22384             Roo.each(styles, function(s) {
22385                 if (!s.match(/:/)) {
22386                     return;
22387                 }
22388                 var kv = s.split(":");
22389                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22390                     return;
22391                 }
22392                 // what ever is left... we allow.
22393                 nstyle.push(s);
22394             });
22395             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22396             if (!nstyle.length) {
22397                 node.removeAttribute('style');
22398             }
22399         }
22400         this.iterateChildren(node, this.cleanWord);
22401         
22402         
22403         
22404     },
22405     /**
22406      * iterateChildren of a Node, calling fn each time, using this as the scole..
22407      * @param {DomNode} node node to iterate children of.
22408      * @param {Function} fn method of this class to call on each item.
22409      */
22410     iterateChildren : function(node, fn)
22411     {
22412         if (!node.childNodes.length) {
22413                 return;
22414         }
22415         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22416            fn.call(this, node.childNodes[i])
22417         }
22418     },
22419     
22420     
22421     /**
22422      * cleanTableWidths.
22423      *
22424      * Quite often pasting from word etc.. results in tables with column and widths.
22425      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22426      *
22427      */
22428     cleanTableWidths : function(node)
22429     {
22430          
22431          
22432         if (!node) {
22433             this.cleanTableWidths(this.doc.body);
22434             return;
22435         }
22436         
22437         // ignore list...
22438         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22439             return; 
22440         }
22441         Roo.log(node.tagName);
22442         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22443             this.iterateChildren(node, this.cleanTableWidths);
22444             return;
22445         }
22446         if (node.hasAttribute('width')) {
22447             node.removeAttribute('width');
22448         }
22449         
22450          
22451         if (node.hasAttribute("style")) {
22452             // pretty basic...
22453             
22454             var styles = node.getAttribute("style").split(";");
22455             var nstyle = [];
22456             Roo.each(styles, function(s) {
22457                 if (!s.match(/:/)) {
22458                     return;
22459                 }
22460                 var kv = s.split(":");
22461                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22462                     return;
22463                 }
22464                 // what ever is left... we allow.
22465                 nstyle.push(s);
22466             });
22467             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22468             if (!nstyle.length) {
22469                 node.removeAttribute('style');
22470             }
22471         }
22472         
22473         this.iterateChildren(node, this.cleanTableWidths);
22474         
22475         
22476     },
22477     
22478     
22479     
22480     
22481     domToHTML : function(currentElement, depth, nopadtext) {
22482         
22483         depth = depth || 0;
22484         nopadtext = nopadtext || false;
22485     
22486         if (!currentElement) {
22487             return this.domToHTML(this.doc.body);
22488         }
22489         
22490         //Roo.log(currentElement);
22491         var j;
22492         var allText = false;
22493         var nodeName = currentElement.nodeName;
22494         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22495         
22496         if  (nodeName == '#text') {
22497             
22498             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22499         }
22500         
22501         
22502         var ret = '';
22503         if (nodeName != 'BODY') {
22504              
22505             var i = 0;
22506             // Prints the node tagName, such as <A>, <IMG>, etc
22507             if (tagName) {
22508                 var attr = [];
22509                 for(i = 0; i < currentElement.attributes.length;i++) {
22510                     // quoting?
22511                     var aname = currentElement.attributes.item(i).name;
22512                     if (!currentElement.attributes.item(i).value.length) {
22513                         continue;
22514                     }
22515                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22516                 }
22517                 
22518                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22519             } 
22520             else {
22521                 
22522                 // eack
22523             }
22524         } else {
22525             tagName = false;
22526         }
22527         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22528             return ret;
22529         }
22530         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22531             nopadtext = true;
22532         }
22533         
22534         
22535         // Traverse the tree
22536         i = 0;
22537         var currentElementChild = currentElement.childNodes.item(i);
22538         var allText = true;
22539         var innerHTML  = '';
22540         lastnode = '';
22541         while (currentElementChild) {
22542             // Formatting code (indent the tree so it looks nice on the screen)
22543             var nopad = nopadtext;
22544             if (lastnode == 'SPAN') {
22545                 nopad  = true;
22546             }
22547             // text
22548             if  (currentElementChild.nodeName == '#text') {
22549                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22550                 toadd = nopadtext ? toadd : toadd.trim();
22551                 if (!nopad && toadd.length > 80) {
22552                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22553                 }
22554                 innerHTML  += toadd;
22555                 
22556                 i++;
22557                 currentElementChild = currentElement.childNodes.item(i);
22558                 lastNode = '';
22559                 continue;
22560             }
22561             allText = false;
22562             
22563             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22564                 
22565             // Recursively traverse the tree structure of the child node
22566             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22567             lastnode = currentElementChild.nodeName;
22568             i++;
22569             currentElementChild=currentElement.childNodes.item(i);
22570         }
22571         
22572         ret += innerHTML;
22573         
22574         if (!allText) {
22575                 // The remaining code is mostly for formatting the tree
22576             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22577         }
22578         
22579         
22580         if (tagName) {
22581             ret+= "</"+tagName+">";
22582         }
22583         return ret;
22584         
22585     },
22586         
22587     applyBlacklists : function()
22588     {
22589         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22590         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22591         
22592         this.white = [];
22593         this.black = [];
22594         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22595             if (b.indexOf(tag) > -1) {
22596                 return;
22597             }
22598             this.white.push(tag);
22599             
22600         }, this);
22601         
22602         Roo.each(w, function(tag) {
22603             if (b.indexOf(tag) > -1) {
22604                 return;
22605             }
22606             if (this.white.indexOf(tag) > -1) {
22607                 return;
22608             }
22609             this.white.push(tag);
22610             
22611         }, this);
22612         
22613         
22614         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22615             if (w.indexOf(tag) > -1) {
22616                 return;
22617             }
22618             this.black.push(tag);
22619             
22620         }, this);
22621         
22622         Roo.each(b, function(tag) {
22623             if (w.indexOf(tag) > -1) {
22624                 return;
22625             }
22626             if (this.black.indexOf(tag) > -1) {
22627                 return;
22628             }
22629             this.black.push(tag);
22630             
22631         }, this);
22632         
22633         
22634         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22635         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22636         
22637         this.cwhite = [];
22638         this.cblack = [];
22639         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22640             if (b.indexOf(tag) > -1) {
22641                 return;
22642             }
22643             this.cwhite.push(tag);
22644             
22645         }, this);
22646         
22647         Roo.each(w, function(tag) {
22648             if (b.indexOf(tag) > -1) {
22649                 return;
22650             }
22651             if (this.cwhite.indexOf(tag) > -1) {
22652                 return;
22653             }
22654             this.cwhite.push(tag);
22655             
22656         }, this);
22657         
22658         
22659         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22660             if (w.indexOf(tag) > -1) {
22661                 return;
22662             }
22663             this.cblack.push(tag);
22664             
22665         }, this);
22666         
22667         Roo.each(b, function(tag) {
22668             if (w.indexOf(tag) > -1) {
22669                 return;
22670             }
22671             if (this.cblack.indexOf(tag) > -1) {
22672                 return;
22673             }
22674             this.cblack.push(tag);
22675             
22676         }, this);
22677     },
22678     
22679     setStylesheets : function(stylesheets)
22680     {
22681         if(typeof(stylesheets) == 'string'){
22682             Roo.get(this.iframe.contentDocument.head).createChild({
22683                 tag : 'link',
22684                 rel : 'stylesheet',
22685                 type : 'text/css',
22686                 href : stylesheets
22687             });
22688             
22689             return;
22690         }
22691         var _this = this;
22692      
22693         Roo.each(stylesheets, function(s) {
22694             if(!s.length){
22695                 return;
22696             }
22697             
22698             Roo.get(_this.iframe.contentDocument.head).createChild({
22699                 tag : 'link',
22700                 rel : 'stylesheet',
22701                 type : 'text/css',
22702                 href : s
22703             });
22704         });
22705
22706         
22707     },
22708     
22709     removeStylesheets : function()
22710     {
22711         var _this = this;
22712         
22713         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22714             s.remove();
22715         });
22716     },
22717     
22718     setStyle : function(style)
22719     {
22720         Roo.get(this.iframe.contentDocument.head).createChild({
22721             tag : 'style',
22722             type : 'text/css',
22723             html : style
22724         });
22725
22726         return;
22727     }
22728     
22729     // hide stuff that is not compatible
22730     /**
22731      * @event blur
22732      * @hide
22733      */
22734     /**
22735      * @event change
22736      * @hide
22737      */
22738     /**
22739      * @event focus
22740      * @hide
22741      */
22742     /**
22743      * @event specialkey
22744      * @hide
22745      */
22746     /**
22747      * @cfg {String} fieldClass @hide
22748      */
22749     /**
22750      * @cfg {String} focusClass @hide
22751      */
22752     /**
22753      * @cfg {String} autoCreate @hide
22754      */
22755     /**
22756      * @cfg {String} inputType @hide
22757      */
22758     /**
22759      * @cfg {String} invalidClass @hide
22760      */
22761     /**
22762      * @cfg {String} invalidText @hide
22763      */
22764     /**
22765      * @cfg {String} msgFx @hide
22766      */
22767     /**
22768      * @cfg {String} validateOnBlur @hide
22769      */
22770 });
22771
22772 Roo.HtmlEditorCore.white = [
22773         'area', 'br', 'img', 'input', 'hr', 'wbr',
22774         
22775        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22776        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22777        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22778        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22779        'table',   'ul',         'xmp', 
22780        
22781        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22782       'thead',   'tr', 
22783      
22784       'dir', 'menu', 'ol', 'ul', 'dl',
22785        
22786       'embed',  'object'
22787 ];
22788
22789
22790 Roo.HtmlEditorCore.black = [
22791     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22792         'applet', // 
22793         'base',   'basefont', 'bgsound', 'blink',  'body', 
22794         'frame',  'frameset', 'head',    'html',   'ilayer', 
22795         'iframe', 'layer',  'link',     'meta',    'object',   
22796         'script', 'style' ,'title',  'xml' // clean later..
22797 ];
22798 Roo.HtmlEditorCore.clean = [
22799     'script', 'style', 'title', 'xml'
22800 ];
22801 Roo.HtmlEditorCore.remove = [
22802     'font'
22803 ];
22804 // attributes..
22805
22806 Roo.HtmlEditorCore.ablack = [
22807     'on'
22808 ];
22809     
22810 Roo.HtmlEditorCore.aclean = [ 
22811     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22812 ];
22813
22814 // protocols..
22815 Roo.HtmlEditorCore.pwhite= [
22816         'http',  'https',  'mailto'
22817 ];
22818
22819 // white listed style attributes.
22820 Roo.HtmlEditorCore.cwhite= [
22821       //  'text-align', /// default is to allow most things..
22822       
22823          
22824 //        'font-size'//??
22825 ];
22826
22827 // black listed style attributes.
22828 Roo.HtmlEditorCore.cblack= [
22829       //  'font-size' -- this can be set by the project 
22830 ];
22831
22832
22833 Roo.HtmlEditorCore.swapCodes   =[ 
22834     [    8211, "--" ], 
22835     [    8212, "--" ], 
22836     [    8216,  "'" ],  
22837     [    8217, "'" ],  
22838     [    8220, '"' ],  
22839     [    8221, '"' ],  
22840     [    8226, "*" ],  
22841     [    8230, "..." ]
22842 ]; 
22843
22844     /*
22845  * - LGPL
22846  *
22847  * HtmlEditor
22848  * 
22849  */
22850
22851 /**
22852  * @class Roo.bootstrap.HtmlEditor
22853  * @extends Roo.bootstrap.TextArea
22854  * Bootstrap HtmlEditor class
22855
22856  * @constructor
22857  * Create a new HtmlEditor
22858  * @param {Object} config The config object
22859  */
22860
22861 Roo.bootstrap.HtmlEditor = function(config){
22862     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22863     if (!this.toolbars) {
22864         this.toolbars = [];
22865     }
22866     
22867     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22868     this.addEvents({
22869             /**
22870              * @event initialize
22871              * Fires when the editor is fully initialized (including the iframe)
22872              * @param {HtmlEditor} this
22873              */
22874             initialize: true,
22875             /**
22876              * @event activate
22877              * Fires when the editor is first receives the focus. Any insertion must wait
22878              * until after this event.
22879              * @param {HtmlEditor} this
22880              */
22881             activate: true,
22882              /**
22883              * @event beforesync
22884              * Fires before the textarea is updated with content from the editor iframe. Return false
22885              * to cancel the sync.
22886              * @param {HtmlEditor} this
22887              * @param {String} html
22888              */
22889             beforesync: true,
22890              /**
22891              * @event beforepush
22892              * Fires before the iframe editor is updated with content from the textarea. Return false
22893              * to cancel the push.
22894              * @param {HtmlEditor} this
22895              * @param {String} html
22896              */
22897             beforepush: true,
22898              /**
22899              * @event sync
22900              * Fires when the textarea is updated with content from the editor iframe.
22901              * @param {HtmlEditor} this
22902              * @param {String} html
22903              */
22904             sync: true,
22905              /**
22906              * @event push
22907              * Fires when the iframe editor is updated with content from the textarea.
22908              * @param {HtmlEditor} this
22909              * @param {String} html
22910              */
22911             push: true,
22912              /**
22913              * @event editmodechange
22914              * Fires when the editor switches edit modes
22915              * @param {HtmlEditor} this
22916              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22917              */
22918             editmodechange: true,
22919             /**
22920              * @event editorevent
22921              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22922              * @param {HtmlEditor} this
22923              */
22924             editorevent: true,
22925             /**
22926              * @event firstfocus
22927              * Fires when on first focus - needed by toolbars..
22928              * @param {HtmlEditor} this
22929              */
22930             firstfocus: true,
22931             /**
22932              * @event autosave
22933              * Auto save the htmlEditor value as a file into Events
22934              * @param {HtmlEditor} this
22935              */
22936             autosave: true,
22937             /**
22938              * @event savedpreview
22939              * preview the saved version of htmlEditor
22940              * @param {HtmlEditor} this
22941              */
22942             savedpreview: true
22943         });
22944 };
22945
22946
22947 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22948     
22949     
22950       /**
22951      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22952      */
22953     toolbars : false,
22954     
22955      /**
22956     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22957     */
22958     btns : [],
22959    
22960      /**
22961      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22962      *                        Roo.resizable.
22963      */
22964     resizable : false,
22965      /**
22966      * @cfg {Number} height (in pixels)
22967      */   
22968     height: 300,
22969    /**
22970      * @cfg {Number} width (in pixels)
22971      */   
22972     width: false,
22973     
22974     /**
22975      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22976      * 
22977      */
22978     stylesheets: false,
22979     
22980     // id of frame..
22981     frameId: false,
22982     
22983     // private properties
22984     validationEvent : false,
22985     deferHeight: true,
22986     initialized : false,
22987     activated : false,
22988     
22989     onFocus : Roo.emptyFn,
22990     iframePad:3,
22991     hideMode:'offsets',
22992     
22993     tbContainer : false,
22994     
22995     bodyCls : '',
22996     
22997     toolbarContainer :function() {
22998         return this.wrap.select('.x-html-editor-tb',true).first();
22999     },
23000
23001     /**
23002      * Protected method that will not generally be called directly. It
23003      * is called when the editor creates its toolbar. Override this method if you need to
23004      * add custom toolbar buttons.
23005      * @param {HtmlEditor} editor
23006      */
23007     createToolbar : function(){
23008         Roo.log('renewing');
23009         Roo.log("create toolbars");
23010         
23011         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23012         this.toolbars[0].render(this.toolbarContainer());
23013         
23014         return;
23015         
23016 //        if (!editor.toolbars || !editor.toolbars.length) {
23017 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23018 //        }
23019 //        
23020 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23021 //            editor.toolbars[i] = Roo.factory(
23022 //                    typeof(editor.toolbars[i]) == 'string' ?
23023 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23024 //                Roo.bootstrap.HtmlEditor);
23025 //            editor.toolbars[i].init(editor);
23026 //        }
23027     },
23028
23029      
23030     // private
23031     onRender : function(ct, position)
23032     {
23033        // Roo.log("Call onRender: " + this.xtype);
23034         var _t = this;
23035         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23036       
23037         this.wrap = this.inputEl().wrap({
23038             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23039         });
23040         
23041         this.editorcore.onRender(ct, position);
23042          
23043         if (this.resizable) {
23044             this.resizeEl = new Roo.Resizable(this.wrap, {
23045                 pinned : true,
23046                 wrap: true,
23047                 dynamic : true,
23048                 minHeight : this.height,
23049                 height: this.height,
23050                 handles : this.resizable,
23051                 width: this.width,
23052                 listeners : {
23053                     resize : function(r, w, h) {
23054                         _t.onResize(w,h); // -something
23055                     }
23056                 }
23057             });
23058             
23059         }
23060         this.createToolbar(this);
23061        
23062         
23063         if(!this.width && this.resizable){
23064             this.setSize(this.wrap.getSize());
23065         }
23066         if (this.resizeEl) {
23067             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23068             // should trigger onReize..
23069         }
23070         
23071     },
23072
23073     // private
23074     onResize : function(w, h)
23075     {
23076         Roo.log('resize: ' +w + ',' + h );
23077         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23078         var ew = false;
23079         var eh = false;
23080         
23081         if(this.inputEl() ){
23082             if(typeof w == 'number'){
23083                 var aw = w - this.wrap.getFrameWidth('lr');
23084                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23085                 ew = aw;
23086             }
23087             if(typeof h == 'number'){
23088                  var tbh = -11;  // fixme it needs to tool bar size!
23089                 for (var i =0; i < this.toolbars.length;i++) {
23090                     // fixme - ask toolbars for heights?
23091                     tbh += this.toolbars[i].el.getHeight();
23092                     //if (this.toolbars[i].footer) {
23093                     //    tbh += this.toolbars[i].footer.el.getHeight();
23094                     //}
23095                 }
23096               
23097                 
23098                 
23099                 
23100                 
23101                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23102                 ah -= 5; // knock a few pixes off for look..
23103                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23104                 var eh = ah;
23105             }
23106         }
23107         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23108         this.editorcore.onResize(ew,eh);
23109         
23110     },
23111
23112     /**
23113      * Toggles the editor between standard and source edit mode.
23114      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23115      */
23116     toggleSourceEdit : function(sourceEditMode)
23117     {
23118         this.editorcore.toggleSourceEdit(sourceEditMode);
23119         
23120         if(this.editorcore.sourceEditMode){
23121             Roo.log('editor - showing textarea');
23122             
23123 //            Roo.log('in');
23124 //            Roo.log(this.syncValue());
23125             this.syncValue();
23126             this.inputEl().removeClass(['hide', 'x-hidden']);
23127             this.inputEl().dom.removeAttribute('tabIndex');
23128             this.inputEl().focus();
23129         }else{
23130             Roo.log('editor - hiding textarea');
23131 //            Roo.log('out')
23132 //            Roo.log(this.pushValue()); 
23133             this.pushValue();
23134             
23135             this.inputEl().addClass(['hide', 'x-hidden']);
23136             this.inputEl().dom.setAttribute('tabIndex', -1);
23137             //this.deferFocus();
23138         }
23139          
23140         if(this.resizable){
23141             this.setSize(this.wrap.getSize());
23142         }
23143         
23144         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23145     },
23146  
23147     // private (for BoxComponent)
23148     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23149
23150     // private (for BoxComponent)
23151     getResizeEl : function(){
23152         return this.wrap;
23153     },
23154
23155     // private (for BoxComponent)
23156     getPositionEl : function(){
23157         return this.wrap;
23158     },
23159
23160     // private
23161     initEvents : function(){
23162         this.originalValue = this.getValue();
23163     },
23164
23165 //    /**
23166 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23167 //     * @method
23168 //     */
23169 //    markInvalid : Roo.emptyFn,
23170 //    /**
23171 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23172 //     * @method
23173 //     */
23174 //    clearInvalid : Roo.emptyFn,
23175
23176     setValue : function(v){
23177         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23178         this.editorcore.pushValue();
23179     },
23180
23181      
23182     // private
23183     deferFocus : function(){
23184         this.focus.defer(10, this);
23185     },
23186
23187     // doc'ed in Field
23188     focus : function(){
23189         this.editorcore.focus();
23190         
23191     },
23192       
23193
23194     // private
23195     onDestroy : function(){
23196         
23197         
23198         
23199         if(this.rendered){
23200             
23201             for (var i =0; i < this.toolbars.length;i++) {
23202                 // fixme - ask toolbars for heights?
23203                 this.toolbars[i].onDestroy();
23204             }
23205             
23206             this.wrap.dom.innerHTML = '';
23207             this.wrap.remove();
23208         }
23209     },
23210
23211     // private
23212     onFirstFocus : function(){
23213         //Roo.log("onFirstFocus");
23214         this.editorcore.onFirstFocus();
23215          for (var i =0; i < this.toolbars.length;i++) {
23216             this.toolbars[i].onFirstFocus();
23217         }
23218         
23219     },
23220     
23221     // private
23222     syncValue : function()
23223     {   
23224         this.editorcore.syncValue();
23225     },
23226     
23227     pushValue : function()
23228     {   
23229         this.editorcore.pushValue();
23230     }
23231      
23232     
23233     // hide stuff that is not compatible
23234     /**
23235      * @event blur
23236      * @hide
23237      */
23238     /**
23239      * @event change
23240      * @hide
23241      */
23242     /**
23243      * @event focus
23244      * @hide
23245      */
23246     /**
23247      * @event specialkey
23248      * @hide
23249      */
23250     /**
23251      * @cfg {String} fieldClass @hide
23252      */
23253     /**
23254      * @cfg {String} focusClass @hide
23255      */
23256     /**
23257      * @cfg {String} autoCreate @hide
23258      */
23259     /**
23260      * @cfg {String} inputType @hide
23261      */
23262     /**
23263      * @cfg {String} invalidClass @hide
23264      */
23265     /**
23266      * @cfg {String} invalidText @hide
23267      */
23268     /**
23269      * @cfg {String} msgFx @hide
23270      */
23271     /**
23272      * @cfg {String} validateOnBlur @hide
23273      */
23274 });
23275  
23276     
23277    
23278    
23279    
23280       
23281 Roo.namespace('Roo.bootstrap.htmleditor');
23282 /**
23283  * @class Roo.bootstrap.HtmlEditorToolbar1
23284  * Basic Toolbar
23285  * 
23286  * Usage:
23287  *
23288  new Roo.bootstrap.HtmlEditor({
23289     ....
23290     toolbars : [
23291         new Roo.bootstrap.HtmlEditorToolbar1({
23292             disable : { fonts: 1 , format: 1, ..., ... , ...],
23293             btns : [ .... ]
23294         })
23295     }
23296      
23297  * 
23298  * @cfg {Object} disable List of elements to disable..
23299  * @cfg {Array} btns List of additional buttons.
23300  * 
23301  * 
23302  * NEEDS Extra CSS? 
23303  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23304  */
23305  
23306 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23307 {
23308     
23309     Roo.apply(this, config);
23310     
23311     // default disabled, based on 'good practice'..
23312     this.disable = this.disable || {};
23313     Roo.applyIf(this.disable, {
23314         fontSize : true,
23315         colors : true,
23316         specialElements : true
23317     });
23318     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23319     
23320     this.editor = config.editor;
23321     this.editorcore = config.editor.editorcore;
23322     
23323     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23324     
23325     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23326     // dont call parent... till later.
23327 }
23328 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23329      
23330     bar : true,
23331     
23332     editor : false,
23333     editorcore : false,
23334     
23335     
23336     formats : [
23337         "p" ,  
23338         "h1","h2","h3","h4","h5","h6", 
23339         "pre", "code", 
23340         "abbr", "acronym", "address", "cite", "samp", "var",
23341         'div','span'
23342     ],
23343     
23344     onRender : function(ct, position)
23345     {
23346        // Roo.log("Call onRender: " + this.xtype);
23347         
23348        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23349        Roo.log(this.el);
23350        this.el.dom.style.marginBottom = '0';
23351        var _this = this;
23352        var editorcore = this.editorcore;
23353        var editor= this.editor;
23354        
23355        var children = [];
23356        var btn = function(id,cmd , toggle, handler, html){
23357        
23358             var  event = toggle ? 'toggle' : 'click';
23359        
23360             var a = {
23361                 size : 'sm',
23362                 xtype: 'Button',
23363                 xns: Roo.bootstrap,
23364                 glyphicon : id,
23365                 cmd : id || cmd,
23366                 enableToggle:toggle !== false,
23367                 html : html || '',
23368                 pressed : toggle ? false : null,
23369                 listeners : {}
23370             };
23371             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23372                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23373             };
23374             children.push(a);
23375             return a;
23376        }
23377        
23378     //    var cb_box = function...
23379         
23380         var style = {
23381                 xtype: 'Button',
23382                 size : 'sm',
23383                 xns: Roo.bootstrap,
23384                 glyphicon : 'font',
23385                 //html : 'submit'
23386                 menu : {
23387                     xtype: 'Menu',
23388                     xns: Roo.bootstrap,
23389                     items:  []
23390                 }
23391         };
23392         Roo.each(this.formats, function(f) {
23393             style.menu.items.push({
23394                 xtype :'MenuItem',
23395                 xns: Roo.bootstrap,
23396                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23397                 tagname : f,
23398                 listeners : {
23399                     click : function()
23400                     {
23401                         editorcore.insertTag(this.tagname);
23402                         editor.focus();
23403                     }
23404                 }
23405                 
23406             });
23407         });
23408         children.push(style);   
23409         
23410         btn('bold',false,true);
23411         btn('italic',false,true);
23412         btn('align-left', 'justifyleft',true);
23413         btn('align-center', 'justifycenter',true);
23414         btn('align-right' , 'justifyright',true);
23415         btn('link', false, false, function(btn) {
23416             //Roo.log("create link?");
23417             var url = prompt(this.createLinkText, this.defaultLinkValue);
23418             if(url && url != 'http:/'+'/'){
23419                 this.editorcore.relayCmd('createlink', url);
23420             }
23421         }),
23422         btn('list','insertunorderedlist',true);
23423         btn('pencil', false,true, function(btn){
23424                 Roo.log(this);
23425                 this.toggleSourceEdit(btn.pressed);
23426         });
23427         
23428         if (this.editor.btns.length > 0) {
23429             for (var i = 0; i<this.editor.btns.length; i++) {
23430                 children.push(this.editor.btns[i]);
23431             }
23432         }
23433         
23434         /*
23435         var cog = {
23436                 xtype: 'Button',
23437                 size : 'sm',
23438                 xns: Roo.bootstrap,
23439                 glyphicon : 'cog',
23440                 //html : 'submit'
23441                 menu : {
23442                     xtype: 'Menu',
23443                     xns: Roo.bootstrap,
23444                     items:  []
23445                 }
23446         };
23447         
23448         cog.menu.items.push({
23449             xtype :'MenuItem',
23450             xns: Roo.bootstrap,
23451             html : Clean styles,
23452             tagname : f,
23453             listeners : {
23454                 click : function()
23455                 {
23456                     editorcore.insertTag(this.tagname);
23457                     editor.focus();
23458                 }
23459             }
23460             
23461         });
23462        */
23463         
23464          
23465        this.xtype = 'NavSimplebar';
23466         
23467         for(var i=0;i< children.length;i++) {
23468             
23469             this.buttons.add(this.addxtypeChild(children[i]));
23470             
23471         }
23472         
23473         editor.on('editorevent', this.updateToolbar, this);
23474     },
23475     onBtnClick : function(id)
23476     {
23477        this.editorcore.relayCmd(id);
23478        this.editorcore.focus();
23479     },
23480     
23481     /**
23482      * Protected method that will not generally be called directly. It triggers
23483      * a toolbar update by reading the markup state of the current selection in the editor.
23484      */
23485     updateToolbar: function(){
23486
23487         if(!this.editorcore.activated){
23488             this.editor.onFirstFocus(); // is this neeed?
23489             return;
23490         }
23491
23492         var btns = this.buttons; 
23493         var doc = this.editorcore.doc;
23494         btns.get('bold').setActive(doc.queryCommandState('bold'));
23495         btns.get('italic').setActive(doc.queryCommandState('italic'));
23496         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23497         
23498         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23499         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23500         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23501         
23502         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23503         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23504          /*
23505         
23506         var ans = this.editorcore.getAllAncestors();
23507         if (this.formatCombo) {
23508             
23509             
23510             var store = this.formatCombo.store;
23511             this.formatCombo.setValue("");
23512             for (var i =0; i < ans.length;i++) {
23513                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23514                     // select it..
23515                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23516                     break;
23517                 }
23518             }
23519         }
23520         
23521         
23522         
23523         // hides menus... - so this cant be on a menu...
23524         Roo.bootstrap.MenuMgr.hideAll();
23525         */
23526         Roo.bootstrap.MenuMgr.hideAll();
23527         //this.editorsyncValue();
23528     },
23529     onFirstFocus: function() {
23530         this.buttons.each(function(item){
23531            item.enable();
23532         });
23533     },
23534     toggleSourceEdit : function(sourceEditMode){
23535         
23536           
23537         if(sourceEditMode){
23538             Roo.log("disabling buttons");
23539            this.buttons.each( function(item){
23540                 if(item.cmd != 'pencil'){
23541                     item.disable();
23542                 }
23543             });
23544           
23545         }else{
23546             Roo.log("enabling buttons");
23547             if(this.editorcore.initialized){
23548                 this.buttons.each( function(item){
23549                     item.enable();
23550                 });
23551             }
23552             
23553         }
23554         Roo.log("calling toggole on editor");
23555         // tell the editor that it's been pressed..
23556         this.editor.toggleSourceEdit(sourceEditMode);
23557        
23558     }
23559 });
23560
23561
23562
23563
23564
23565 /**
23566  * @class Roo.bootstrap.Table.AbstractSelectionModel
23567  * @extends Roo.util.Observable
23568  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23569  * implemented by descendant classes.  This class should not be directly instantiated.
23570  * @constructor
23571  */
23572 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23573     this.locked = false;
23574     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23575 };
23576
23577
23578 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23579     /** @ignore Called by the grid automatically. Do not call directly. */
23580     init : function(grid){
23581         this.grid = grid;
23582         this.initEvents();
23583     },
23584
23585     /**
23586      * Locks the selections.
23587      */
23588     lock : function(){
23589         this.locked = true;
23590     },
23591
23592     /**
23593      * Unlocks the selections.
23594      */
23595     unlock : function(){
23596         this.locked = false;
23597     },
23598
23599     /**
23600      * Returns true if the selections are locked.
23601      * @return {Boolean}
23602      */
23603     isLocked : function(){
23604         return this.locked;
23605     }
23606 });
23607 /**
23608  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23609  * @class Roo.bootstrap.Table.RowSelectionModel
23610  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23611  * It supports multiple selections and keyboard selection/navigation. 
23612  * @constructor
23613  * @param {Object} config
23614  */
23615
23616 Roo.bootstrap.Table.RowSelectionModel = function(config){
23617     Roo.apply(this, config);
23618     this.selections = new Roo.util.MixedCollection(false, function(o){
23619         return o.id;
23620     });
23621
23622     this.last = false;
23623     this.lastActive = false;
23624
23625     this.addEvents({
23626         /**
23627              * @event selectionchange
23628              * Fires when the selection changes
23629              * @param {SelectionModel} this
23630              */
23631             "selectionchange" : true,
23632         /**
23633              * @event afterselectionchange
23634              * Fires after the selection changes (eg. by key press or clicking)
23635              * @param {SelectionModel} this
23636              */
23637             "afterselectionchange" : true,
23638         /**
23639              * @event beforerowselect
23640              * Fires when a row is selected being selected, return false to cancel.
23641              * @param {SelectionModel} this
23642              * @param {Number} rowIndex The selected index
23643              * @param {Boolean} keepExisting False if other selections will be cleared
23644              */
23645             "beforerowselect" : true,
23646         /**
23647              * @event rowselect
23648              * Fires when a row is selected.
23649              * @param {SelectionModel} this
23650              * @param {Number} rowIndex The selected index
23651              * @param {Roo.data.Record} r The record
23652              */
23653             "rowselect" : true,
23654         /**
23655              * @event rowdeselect
23656              * Fires when a row is deselected.
23657              * @param {SelectionModel} this
23658              * @param {Number} rowIndex The selected index
23659              */
23660         "rowdeselect" : true
23661     });
23662     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23663     this.locked = false;
23664  };
23665
23666 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23667     /**
23668      * @cfg {Boolean} singleSelect
23669      * True to allow selection of only one row at a time (defaults to false)
23670      */
23671     singleSelect : false,
23672
23673     // private
23674     initEvents : function()
23675     {
23676
23677         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23678         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23679         //}else{ // allow click to work like normal
23680          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23681         //}
23682         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23683         this.grid.on("rowclick", this.handleMouseDown, this);
23684         
23685         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23686             "up" : function(e){
23687                 if(!e.shiftKey){
23688                     this.selectPrevious(e.shiftKey);
23689                 }else if(this.last !== false && this.lastActive !== false){
23690                     var last = this.last;
23691                     this.selectRange(this.last,  this.lastActive-1);
23692                     this.grid.getView().focusRow(this.lastActive);
23693                     if(last !== false){
23694                         this.last = last;
23695                     }
23696                 }else{
23697                     this.selectFirstRow();
23698                 }
23699                 this.fireEvent("afterselectionchange", this);
23700             },
23701             "down" : function(e){
23702                 if(!e.shiftKey){
23703                     this.selectNext(e.shiftKey);
23704                 }else if(this.last !== false && this.lastActive !== false){
23705                     var last = this.last;
23706                     this.selectRange(this.last,  this.lastActive+1);
23707                     this.grid.getView().focusRow(this.lastActive);
23708                     if(last !== false){
23709                         this.last = last;
23710                     }
23711                 }else{
23712                     this.selectFirstRow();
23713                 }
23714                 this.fireEvent("afterselectionchange", this);
23715             },
23716             scope: this
23717         });
23718         this.grid.store.on('load', function(){
23719             this.selections.clear();
23720         },this);
23721         /*
23722         var view = this.grid.view;
23723         view.on("refresh", this.onRefresh, this);
23724         view.on("rowupdated", this.onRowUpdated, this);
23725         view.on("rowremoved", this.onRemove, this);
23726         */
23727     },
23728
23729     // private
23730     onRefresh : function()
23731     {
23732         var ds = this.grid.store, i, v = this.grid.view;
23733         var s = this.selections;
23734         s.each(function(r){
23735             if((i = ds.indexOfId(r.id)) != -1){
23736                 v.onRowSelect(i);
23737             }else{
23738                 s.remove(r);
23739             }
23740         });
23741     },
23742
23743     // private
23744     onRemove : function(v, index, r){
23745         this.selections.remove(r);
23746     },
23747
23748     // private
23749     onRowUpdated : function(v, index, r){
23750         if(this.isSelected(r)){
23751             v.onRowSelect(index);
23752         }
23753     },
23754
23755     /**
23756      * Select records.
23757      * @param {Array} records The records to select
23758      * @param {Boolean} keepExisting (optional) True to keep existing selections
23759      */
23760     selectRecords : function(records, keepExisting)
23761     {
23762         if(!keepExisting){
23763             this.clearSelections();
23764         }
23765             var ds = this.grid.store;
23766         for(var i = 0, len = records.length; i < len; i++){
23767             this.selectRow(ds.indexOf(records[i]), true);
23768         }
23769     },
23770
23771     /**
23772      * Gets the number of selected rows.
23773      * @return {Number}
23774      */
23775     getCount : function(){
23776         return this.selections.length;
23777     },
23778
23779     /**
23780      * Selects the first row in the grid.
23781      */
23782     selectFirstRow : function(){
23783         this.selectRow(0);
23784     },
23785
23786     /**
23787      * Select the last row.
23788      * @param {Boolean} keepExisting (optional) True to keep existing selections
23789      */
23790     selectLastRow : function(keepExisting){
23791         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23792         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23793     },
23794
23795     /**
23796      * Selects the row immediately following the last selected row.
23797      * @param {Boolean} keepExisting (optional) True to keep existing selections
23798      */
23799     selectNext : function(keepExisting)
23800     {
23801             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23802             this.selectRow(this.last+1, keepExisting);
23803             this.grid.getView().focusRow(this.last);
23804         }
23805     },
23806
23807     /**
23808      * Selects the row that precedes the last selected row.
23809      * @param {Boolean} keepExisting (optional) True to keep existing selections
23810      */
23811     selectPrevious : function(keepExisting){
23812         if(this.last){
23813             this.selectRow(this.last-1, keepExisting);
23814             this.grid.getView().focusRow(this.last);
23815         }
23816     },
23817
23818     /**
23819      * Returns the selected records
23820      * @return {Array} Array of selected records
23821      */
23822     getSelections : function(){
23823         return [].concat(this.selections.items);
23824     },
23825
23826     /**
23827      * Returns the first selected record.
23828      * @return {Record}
23829      */
23830     getSelected : function(){
23831         return this.selections.itemAt(0);
23832     },
23833
23834
23835     /**
23836      * Clears all selections.
23837      */
23838     clearSelections : function(fast)
23839     {
23840         if(this.locked) {
23841             return;
23842         }
23843         if(fast !== true){
23844                 var ds = this.grid.store;
23845             var s = this.selections;
23846             s.each(function(r){
23847                 this.deselectRow(ds.indexOfId(r.id));
23848             }, this);
23849             s.clear();
23850         }else{
23851             this.selections.clear();
23852         }
23853         this.last = false;
23854     },
23855
23856
23857     /**
23858      * Selects all rows.
23859      */
23860     selectAll : function(){
23861         if(this.locked) {
23862             return;
23863         }
23864         this.selections.clear();
23865         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23866             this.selectRow(i, true);
23867         }
23868     },
23869
23870     /**
23871      * Returns True if there is a selection.
23872      * @return {Boolean}
23873      */
23874     hasSelection : function(){
23875         return this.selections.length > 0;
23876     },
23877
23878     /**
23879      * Returns True if the specified row is selected.
23880      * @param {Number/Record} record The record or index of the record to check
23881      * @return {Boolean}
23882      */
23883     isSelected : function(index){
23884             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23885         return (r && this.selections.key(r.id) ? true : false);
23886     },
23887
23888     /**
23889      * Returns True if the specified record id is selected.
23890      * @param {String} id The id of record to check
23891      * @return {Boolean}
23892      */
23893     isIdSelected : function(id){
23894         return (this.selections.key(id) ? true : false);
23895     },
23896
23897
23898     // private
23899     handleMouseDBClick : function(e, t){
23900         
23901     },
23902     // private
23903     handleMouseDown : function(e, t)
23904     {
23905             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23906         if(this.isLocked() || rowIndex < 0 ){
23907             return;
23908         };
23909         if(e.shiftKey && this.last !== false){
23910             var last = this.last;
23911             this.selectRange(last, rowIndex, e.ctrlKey);
23912             this.last = last; // reset the last
23913             t.focus();
23914     
23915         }else{
23916             var isSelected = this.isSelected(rowIndex);
23917             //Roo.log("select row:" + rowIndex);
23918             if(isSelected){
23919                 this.deselectRow(rowIndex);
23920             } else {
23921                         this.selectRow(rowIndex, true);
23922             }
23923     
23924             /*
23925                 if(e.button !== 0 && isSelected){
23926                 alert('rowIndex 2: ' + rowIndex);
23927                     view.focusRow(rowIndex);
23928                 }else if(e.ctrlKey && isSelected){
23929                     this.deselectRow(rowIndex);
23930                 }else if(!isSelected){
23931                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23932                     view.focusRow(rowIndex);
23933                 }
23934             */
23935         }
23936         this.fireEvent("afterselectionchange", this);
23937     },
23938     // private
23939     handleDragableRowClick :  function(grid, rowIndex, e) 
23940     {
23941         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23942             this.selectRow(rowIndex, false);
23943             grid.view.focusRow(rowIndex);
23944              this.fireEvent("afterselectionchange", this);
23945         }
23946     },
23947     
23948     /**
23949      * Selects multiple rows.
23950      * @param {Array} rows Array of the indexes of the row to select
23951      * @param {Boolean} keepExisting (optional) True to keep existing selections
23952      */
23953     selectRows : function(rows, keepExisting){
23954         if(!keepExisting){
23955             this.clearSelections();
23956         }
23957         for(var i = 0, len = rows.length; i < len; i++){
23958             this.selectRow(rows[i], true);
23959         }
23960     },
23961
23962     /**
23963      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23964      * @param {Number} startRow The index of the first row in the range
23965      * @param {Number} endRow The index of the last row in the range
23966      * @param {Boolean} keepExisting (optional) True to retain existing selections
23967      */
23968     selectRange : function(startRow, endRow, keepExisting){
23969         if(this.locked) {
23970             return;
23971         }
23972         if(!keepExisting){
23973             this.clearSelections();
23974         }
23975         if(startRow <= endRow){
23976             for(var i = startRow; i <= endRow; i++){
23977                 this.selectRow(i, true);
23978             }
23979         }else{
23980             for(var i = startRow; i >= endRow; i--){
23981                 this.selectRow(i, true);
23982             }
23983         }
23984     },
23985
23986     /**
23987      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23988      * @param {Number} startRow The index of the first row in the range
23989      * @param {Number} endRow The index of the last row in the range
23990      */
23991     deselectRange : function(startRow, endRow, preventViewNotify){
23992         if(this.locked) {
23993             return;
23994         }
23995         for(var i = startRow; i <= endRow; i++){
23996             this.deselectRow(i, preventViewNotify);
23997         }
23998     },
23999
24000     /**
24001      * Selects a row.
24002      * @param {Number} row The index of the row to select
24003      * @param {Boolean} keepExisting (optional) True to keep existing selections
24004      */
24005     selectRow : function(index, keepExisting, preventViewNotify)
24006     {
24007             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24008             return;
24009         }
24010         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24011             if(!keepExisting || this.singleSelect){
24012                 this.clearSelections();
24013             }
24014             
24015             var r = this.grid.store.getAt(index);
24016             //console.log('selectRow - record id :' + r.id);
24017             
24018             this.selections.add(r);
24019             this.last = this.lastActive = index;
24020             if(!preventViewNotify){
24021                 var proxy = new Roo.Element(
24022                                 this.grid.getRowDom(index)
24023                 );
24024                 proxy.addClass('bg-info info');
24025             }
24026             this.fireEvent("rowselect", this, index, r);
24027             this.fireEvent("selectionchange", this);
24028         }
24029     },
24030
24031     /**
24032      * Deselects a row.
24033      * @param {Number} row The index of the row to deselect
24034      */
24035     deselectRow : function(index, preventViewNotify)
24036     {
24037         if(this.locked) {
24038             return;
24039         }
24040         if(this.last == index){
24041             this.last = false;
24042         }
24043         if(this.lastActive == index){
24044             this.lastActive = false;
24045         }
24046         
24047         var r = this.grid.store.getAt(index);
24048         if (!r) {
24049             return;
24050         }
24051         
24052         this.selections.remove(r);
24053         //.console.log('deselectRow - record id :' + r.id);
24054         if(!preventViewNotify){
24055         
24056             var proxy = new Roo.Element(
24057                 this.grid.getRowDom(index)
24058             );
24059             proxy.removeClass('bg-info info');
24060         }
24061         this.fireEvent("rowdeselect", this, index);
24062         this.fireEvent("selectionchange", this);
24063     },
24064
24065     // private
24066     restoreLast : function(){
24067         if(this._last){
24068             this.last = this._last;
24069         }
24070     },
24071
24072     // private
24073     acceptsNav : function(row, col, cm){
24074         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24075     },
24076
24077     // private
24078     onEditorKey : function(field, e){
24079         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24080         if(k == e.TAB){
24081             e.stopEvent();
24082             ed.completeEdit();
24083             if(e.shiftKey){
24084                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24085             }else{
24086                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24087             }
24088         }else if(k == e.ENTER && !e.ctrlKey){
24089             e.stopEvent();
24090             ed.completeEdit();
24091             if(e.shiftKey){
24092                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24093             }else{
24094                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24095             }
24096         }else if(k == e.ESC){
24097             ed.cancelEdit();
24098         }
24099         if(newCell){
24100             g.startEditing(newCell[0], newCell[1]);
24101         }
24102     }
24103 });
24104 /*
24105  * Based on:
24106  * Ext JS Library 1.1.1
24107  * Copyright(c) 2006-2007, Ext JS, LLC.
24108  *
24109  * Originally Released Under LGPL - original licence link has changed is not relivant.
24110  *
24111  * Fork - LGPL
24112  * <script type="text/javascript">
24113  */
24114  
24115 /**
24116  * @class Roo.bootstrap.PagingToolbar
24117  * @extends Roo.bootstrap.NavSimplebar
24118  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24119  * @constructor
24120  * Create a new PagingToolbar
24121  * @param {Object} config The config object
24122  * @param {Roo.data.Store} store
24123  */
24124 Roo.bootstrap.PagingToolbar = function(config)
24125 {
24126     // old args format still supported... - xtype is prefered..
24127         // created from xtype...
24128     
24129     this.ds = config.dataSource;
24130     
24131     if (config.store && !this.ds) {
24132         this.store= Roo.factory(config.store, Roo.data);
24133         this.ds = this.store;
24134         this.ds.xmodule = this.xmodule || false;
24135     }
24136     
24137     this.toolbarItems = [];
24138     if (config.items) {
24139         this.toolbarItems = config.items;
24140     }
24141     
24142     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24143     
24144     this.cursor = 0;
24145     
24146     if (this.ds) { 
24147         this.bind(this.ds);
24148     }
24149     
24150     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24151     
24152 };
24153
24154 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24155     /**
24156      * @cfg {Roo.data.Store} dataSource
24157      * The underlying data store providing the paged data
24158      */
24159     /**
24160      * @cfg {String/HTMLElement/Element} container
24161      * container The id or element that will contain the toolbar
24162      */
24163     /**
24164      * @cfg {Boolean} displayInfo
24165      * True to display the displayMsg (defaults to false)
24166      */
24167     /**
24168      * @cfg {Number} pageSize
24169      * The number of records to display per page (defaults to 20)
24170      */
24171     pageSize: 20,
24172     /**
24173      * @cfg {String} displayMsg
24174      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24175      */
24176     displayMsg : 'Displaying {0} - {1} of {2}',
24177     /**
24178      * @cfg {String} emptyMsg
24179      * The message to display when no records are found (defaults to "No data to display")
24180      */
24181     emptyMsg : 'No data to display',
24182     /**
24183      * Customizable piece of the default paging text (defaults to "Page")
24184      * @type String
24185      */
24186     beforePageText : "Page",
24187     /**
24188      * Customizable piece of the default paging text (defaults to "of %0")
24189      * @type String
24190      */
24191     afterPageText : "of {0}",
24192     /**
24193      * Customizable piece of the default paging text (defaults to "First Page")
24194      * @type String
24195      */
24196     firstText : "First Page",
24197     /**
24198      * Customizable piece of the default paging text (defaults to "Previous Page")
24199      * @type String
24200      */
24201     prevText : "Previous Page",
24202     /**
24203      * Customizable piece of the default paging text (defaults to "Next Page")
24204      * @type String
24205      */
24206     nextText : "Next Page",
24207     /**
24208      * Customizable piece of the default paging text (defaults to "Last Page")
24209      * @type String
24210      */
24211     lastText : "Last Page",
24212     /**
24213      * Customizable piece of the default paging text (defaults to "Refresh")
24214      * @type String
24215      */
24216     refreshText : "Refresh",
24217
24218     buttons : false,
24219     // private
24220     onRender : function(ct, position) 
24221     {
24222         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24223         this.navgroup.parentId = this.id;
24224         this.navgroup.onRender(this.el, null);
24225         // add the buttons to the navgroup
24226         
24227         if(this.displayInfo){
24228             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24229             this.displayEl = this.el.select('.x-paging-info', true).first();
24230 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24231 //            this.displayEl = navel.el.select('span',true).first();
24232         }
24233         
24234         var _this = this;
24235         
24236         if(this.buttons){
24237             Roo.each(_this.buttons, function(e){ // this might need to use render????
24238                Roo.factory(e).onRender(_this.el, null);
24239             });
24240         }
24241             
24242         Roo.each(_this.toolbarItems, function(e) {
24243             _this.navgroup.addItem(e);
24244         });
24245         
24246         
24247         this.first = this.navgroup.addItem({
24248             tooltip: this.firstText,
24249             cls: "prev",
24250             icon : 'fa fa-backward',
24251             disabled: true,
24252             preventDefault: true,
24253             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24254         });
24255         
24256         this.prev =  this.navgroup.addItem({
24257             tooltip: this.prevText,
24258             cls: "prev",
24259             icon : 'fa fa-step-backward',
24260             disabled: true,
24261             preventDefault: true,
24262             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24263         });
24264     //this.addSeparator();
24265         
24266         
24267         var field = this.navgroup.addItem( {
24268             tagtype : 'span',
24269             cls : 'x-paging-position',
24270             
24271             html : this.beforePageText  +
24272                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24273                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24274          } ); //?? escaped?
24275         
24276         this.field = field.el.select('input', true).first();
24277         this.field.on("keydown", this.onPagingKeydown, this);
24278         this.field.on("focus", function(){this.dom.select();});
24279     
24280     
24281         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24282         //this.field.setHeight(18);
24283         //this.addSeparator();
24284         this.next = this.navgroup.addItem({
24285             tooltip: this.nextText,
24286             cls: "next",
24287             html : ' <i class="fa fa-step-forward">',
24288             disabled: true,
24289             preventDefault: true,
24290             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24291         });
24292         this.last = this.navgroup.addItem({
24293             tooltip: this.lastText,
24294             icon : 'fa fa-forward',
24295             cls: "next",
24296             disabled: true,
24297             preventDefault: true,
24298             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24299         });
24300     //this.addSeparator();
24301         this.loading = this.navgroup.addItem({
24302             tooltip: this.refreshText,
24303             icon: 'fa fa-refresh',
24304             preventDefault: true,
24305             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24306         });
24307         
24308     },
24309
24310     // private
24311     updateInfo : function(){
24312         if(this.displayEl){
24313             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24314             var msg = count == 0 ?
24315                 this.emptyMsg :
24316                 String.format(
24317                     this.displayMsg,
24318                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24319                 );
24320             this.displayEl.update(msg);
24321         }
24322     },
24323
24324     // private
24325     onLoad : function(ds, r, o)
24326     {
24327         this.cursor = o.params ? o.params.start : 0;
24328         var d = this.getPageData(),
24329             ap = d.activePage,
24330             ps = d.pages;
24331         
24332         
24333         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24334         this.field.dom.value = ap;
24335         this.first.setDisabled(ap == 1);
24336         this.prev.setDisabled(ap == 1);
24337         this.next.setDisabled(ap == ps);
24338         this.last.setDisabled(ap == ps);
24339         this.loading.enable();
24340         this.updateInfo();
24341     },
24342
24343     // private
24344     getPageData : function(){
24345         var total = this.ds.getTotalCount();
24346         return {
24347             total : total,
24348             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24349             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24350         };
24351     },
24352
24353     // private
24354     onLoadError : function(){
24355         this.loading.enable();
24356     },
24357
24358     // private
24359     onPagingKeydown : function(e){
24360         var k = e.getKey();
24361         var d = this.getPageData();
24362         if(k == e.RETURN){
24363             var v = this.field.dom.value, pageNum;
24364             if(!v || isNaN(pageNum = parseInt(v, 10))){
24365                 this.field.dom.value = d.activePage;
24366                 return;
24367             }
24368             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24369             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24370             e.stopEvent();
24371         }
24372         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))
24373         {
24374           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24375           this.field.dom.value = pageNum;
24376           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24377           e.stopEvent();
24378         }
24379         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24380         {
24381           var v = this.field.dom.value, pageNum; 
24382           var increment = (e.shiftKey) ? 10 : 1;
24383           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24384                 increment *= -1;
24385           }
24386           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24387             this.field.dom.value = d.activePage;
24388             return;
24389           }
24390           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24391           {
24392             this.field.dom.value = parseInt(v, 10) + increment;
24393             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24394             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24395           }
24396           e.stopEvent();
24397         }
24398     },
24399
24400     // private
24401     beforeLoad : function(){
24402         if(this.loading){
24403             this.loading.disable();
24404         }
24405     },
24406
24407     // private
24408     onClick : function(which){
24409         
24410         var ds = this.ds;
24411         if (!ds) {
24412             return;
24413         }
24414         
24415         switch(which){
24416             case "first":
24417                 ds.load({params:{start: 0, limit: this.pageSize}});
24418             break;
24419             case "prev":
24420                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24421             break;
24422             case "next":
24423                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24424             break;
24425             case "last":
24426                 var total = ds.getTotalCount();
24427                 var extra = total % this.pageSize;
24428                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24429                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24430             break;
24431             case "refresh":
24432                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24433             break;
24434         }
24435     },
24436
24437     /**
24438      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24439      * @param {Roo.data.Store} store The data store to unbind
24440      */
24441     unbind : function(ds){
24442         ds.un("beforeload", this.beforeLoad, this);
24443         ds.un("load", this.onLoad, this);
24444         ds.un("loadexception", this.onLoadError, this);
24445         ds.un("remove", this.updateInfo, this);
24446         ds.un("add", this.updateInfo, this);
24447         this.ds = undefined;
24448     },
24449
24450     /**
24451      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24452      * @param {Roo.data.Store} store The data store to bind
24453      */
24454     bind : function(ds){
24455         ds.on("beforeload", this.beforeLoad, this);
24456         ds.on("load", this.onLoad, this);
24457         ds.on("loadexception", this.onLoadError, this);
24458         ds.on("remove", this.updateInfo, this);
24459         ds.on("add", this.updateInfo, this);
24460         this.ds = ds;
24461     }
24462 });/*
24463  * - LGPL
24464  *
24465  * element
24466  * 
24467  */
24468
24469 /**
24470  * @class Roo.bootstrap.MessageBar
24471  * @extends Roo.bootstrap.Component
24472  * Bootstrap MessageBar class
24473  * @cfg {String} html contents of the MessageBar
24474  * @cfg {String} weight (info | success | warning | danger) default info
24475  * @cfg {String} beforeClass insert the bar before the given class
24476  * @cfg {Boolean} closable (true | false) default false
24477  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24478  * 
24479  * @constructor
24480  * Create a new Element
24481  * @param {Object} config The config object
24482  */
24483
24484 Roo.bootstrap.MessageBar = function(config){
24485     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24486 };
24487
24488 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24489     
24490     html: '',
24491     weight: 'info',
24492     closable: false,
24493     fixed: false,
24494     beforeClass: 'bootstrap-sticky-wrap',
24495     
24496     getAutoCreate : function(){
24497         
24498         var cfg = {
24499             tag: 'div',
24500             cls: 'alert alert-dismissable alert-' + this.weight,
24501             cn: [
24502                 {
24503                     tag: 'span',
24504                     cls: 'message',
24505                     html: this.html || ''
24506                 }
24507             ]
24508         };
24509         
24510         if(this.fixed){
24511             cfg.cls += ' alert-messages-fixed';
24512         }
24513         
24514         if(this.closable){
24515             cfg.cn.push({
24516                 tag: 'button',
24517                 cls: 'close',
24518                 html: 'x'
24519             });
24520         }
24521         
24522         return cfg;
24523     },
24524     
24525     onRender : function(ct, position)
24526     {
24527         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24528         
24529         if(!this.el){
24530             var cfg = Roo.apply({},  this.getAutoCreate());
24531             cfg.id = Roo.id();
24532             
24533             if (this.cls) {
24534                 cfg.cls += ' ' + this.cls;
24535             }
24536             if (this.style) {
24537                 cfg.style = this.style;
24538             }
24539             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24540             
24541             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24542         }
24543         
24544         this.el.select('>button.close').on('click', this.hide, this);
24545         
24546     },
24547     
24548     show : function()
24549     {
24550         if (!this.rendered) {
24551             this.render();
24552         }
24553         
24554         this.el.show();
24555         
24556         this.fireEvent('show', this);
24557         
24558     },
24559     
24560     hide : function()
24561     {
24562         if (!this.rendered) {
24563             this.render();
24564         }
24565         
24566         this.el.hide();
24567         
24568         this.fireEvent('hide', this);
24569     },
24570     
24571     update : function()
24572     {
24573 //        var e = this.el.dom.firstChild;
24574 //        
24575 //        if(this.closable){
24576 //            e = e.nextSibling;
24577 //        }
24578 //        
24579 //        e.data = this.html || '';
24580
24581         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24582     }
24583    
24584 });
24585
24586  
24587
24588      /*
24589  * - LGPL
24590  *
24591  * Graph
24592  * 
24593  */
24594
24595
24596 /**
24597  * @class Roo.bootstrap.Graph
24598  * @extends Roo.bootstrap.Component
24599  * Bootstrap Graph class
24600 > Prameters
24601  -sm {number} sm 4
24602  -md {number} md 5
24603  @cfg {String} graphtype  bar | vbar | pie
24604  @cfg {number} g_x coodinator | centre x (pie)
24605  @cfg {number} g_y coodinator | centre y (pie)
24606  @cfg {number} g_r radius (pie)
24607  @cfg {number} g_height height of the chart (respected by all elements in the set)
24608  @cfg {number} g_width width of the chart (respected by all elements in the set)
24609  @cfg {Object} title The title of the chart
24610     
24611  -{Array}  values
24612  -opts (object) options for the chart 
24613      o {
24614      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24615      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24616      o vgutter (number)
24617      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.
24618      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24619      o to
24620      o stretch (boolean)
24621      o }
24622  -opts (object) options for the pie
24623      o{
24624      o cut
24625      o startAngle (number)
24626      o endAngle (number)
24627      } 
24628  *
24629  * @constructor
24630  * Create a new Input
24631  * @param {Object} config The config object
24632  */
24633
24634 Roo.bootstrap.Graph = function(config){
24635     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24636     
24637     this.addEvents({
24638         // img events
24639         /**
24640          * @event click
24641          * The img click event for the img.
24642          * @param {Roo.EventObject} e
24643          */
24644         "click" : true
24645     });
24646 };
24647
24648 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24649     
24650     sm: 4,
24651     md: 5,
24652     graphtype: 'bar',
24653     g_height: 250,
24654     g_width: 400,
24655     g_x: 50,
24656     g_y: 50,
24657     g_r: 30,
24658     opts:{
24659         //g_colors: this.colors,
24660         g_type: 'soft',
24661         g_gutter: '20%'
24662
24663     },
24664     title : false,
24665
24666     getAutoCreate : function(){
24667         
24668         var cfg = {
24669             tag: 'div',
24670             html : null
24671         };
24672         
24673         
24674         return  cfg;
24675     },
24676
24677     onRender : function(ct,position){
24678         
24679         
24680         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24681         
24682         if (typeof(Raphael) == 'undefined') {
24683             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24684             return;
24685         }
24686         
24687         this.raphael = Raphael(this.el.dom);
24688         
24689                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24690                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24691                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24692                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24693                 /*
24694                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24695                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24696                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24697                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24698                 
24699                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24700                 r.barchart(330, 10, 300, 220, data1);
24701                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24702                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24703                 */
24704                 
24705                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24706                 // r.barchart(30, 30, 560, 250,  xdata, {
24707                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24708                 //     axis : "0 0 1 1",
24709                 //     axisxlabels :  xdata
24710                 //     //yvalues : cols,
24711                    
24712                 // });
24713 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24714 //        
24715 //        this.load(null,xdata,{
24716 //                axis : "0 0 1 1",
24717 //                axisxlabels :  xdata
24718 //                });
24719
24720     },
24721
24722     load : function(graphtype,xdata,opts)
24723     {
24724         this.raphael.clear();
24725         if(!graphtype) {
24726             graphtype = this.graphtype;
24727         }
24728         if(!opts){
24729             opts = this.opts;
24730         }
24731         var r = this.raphael,
24732             fin = function () {
24733                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24734             },
24735             fout = function () {
24736                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24737             },
24738             pfin = function() {
24739                 this.sector.stop();
24740                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24741
24742                 if (this.label) {
24743                     this.label[0].stop();
24744                     this.label[0].attr({ r: 7.5 });
24745                     this.label[1].attr({ "font-weight": 800 });
24746                 }
24747             },
24748             pfout = function() {
24749                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24750
24751                 if (this.label) {
24752                     this.label[0].animate({ r: 5 }, 500, "bounce");
24753                     this.label[1].attr({ "font-weight": 400 });
24754                 }
24755             };
24756
24757         switch(graphtype){
24758             case 'bar':
24759                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24760                 break;
24761             case 'hbar':
24762                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24763                 break;
24764             case 'pie':
24765 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24766 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24767 //            
24768                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24769                 
24770                 break;
24771
24772         }
24773         
24774         if(this.title){
24775             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24776         }
24777         
24778     },
24779     
24780     setTitle: function(o)
24781     {
24782         this.title = o;
24783     },
24784     
24785     initEvents: function() {
24786         
24787         if(!this.href){
24788             this.el.on('click', this.onClick, this);
24789         }
24790     },
24791     
24792     onClick : function(e)
24793     {
24794         Roo.log('img onclick');
24795         this.fireEvent('click', this, e);
24796     }
24797    
24798 });
24799
24800  
24801 /*
24802  * - LGPL
24803  *
24804  * numberBox
24805  * 
24806  */
24807 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24808
24809 /**
24810  * @class Roo.bootstrap.dash.NumberBox
24811  * @extends Roo.bootstrap.Component
24812  * Bootstrap NumberBox class
24813  * @cfg {String} headline Box headline
24814  * @cfg {String} content Box content
24815  * @cfg {String} icon Box icon
24816  * @cfg {String} footer Footer text
24817  * @cfg {String} fhref Footer href
24818  * 
24819  * @constructor
24820  * Create a new NumberBox
24821  * @param {Object} config The config object
24822  */
24823
24824
24825 Roo.bootstrap.dash.NumberBox = function(config){
24826     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24827     
24828 };
24829
24830 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24831     
24832     headline : '',
24833     content : '',
24834     icon : '',
24835     footer : '',
24836     fhref : '',
24837     ficon : '',
24838     
24839     getAutoCreate : function(){
24840         
24841         var cfg = {
24842             tag : 'div',
24843             cls : 'small-box ',
24844             cn : [
24845                 {
24846                     tag : 'div',
24847                     cls : 'inner',
24848                     cn :[
24849                         {
24850                             tag : 'h3',
24851                             cls : 'roo-headline',
24852                             html : this.headline
24853                         },
24854                         {
24855                             tag : 'p',
24856                             cls : 'roo-content',
24857                             html : this.content
24858                         }
24859                     ]
24860                 }
24861             ]
24862         };
24863         
24864         if(this.icon){
24865             cfg.cn.push({
24866                 tag : 'div',
24867                 cls : 'icon',
24868                 cn :[
24869                     {
24870                         tag : 'i',
24871                         cls : 'ion ' + this.icon
24872                     }
24873                 ]
24874             });
24875         }
24876         
24877         if(this.footer){
24878             var footer = {
24879                 tag : 'a',
24880                 cls : 'small-box-footer',
24881                 href : this.fhref || '#',
24882                 html : this.footer
24883             };
24884             
24885             cfg.cn.push(footer);
24886             
24887         }
24888         
24889         return  cfg;
24890     },
24891
24892     onRender : function(ct,position){
24893         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24894
24895
24896        
24897                 
24898     },
24899
24900     setHeadline: function (value)
24901     {
24902         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24903     },
24904     
24905     setFooter: function (value, href)
24906     {
24907         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24908         
24909         if(href){
24910             this.el.select('a.small-box-footer',true).first().attr('href', href);
24911         }
24912         
24913     },
24914
24915     setContent: function (value)
24916     {
24917         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24918     },
24919
24920     initEvents: function() 
24921     {   
24922         
24923     }
24924     
24925 });
24926
24927  
24928 /*
24929  * - LGPL
24930  *
24931  * TabBox
24932  * 
24933  */
24934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24935
24936 /**
24937  * @class Roo.bootstrap.dash.TabBox
24938  * @extends Roo.bootstrap.Component
24939  * Bootstrap TabBox class
24940  * @cfg {String} title Title of the TabBox
24941  * @cfg {String} icon Icon of the TabBox
24942  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24943  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24944  * 
24945  * @constructor
24946  * Create a new TabBox
24947  * @param {Object} config The config object
24948  */
24949
24950
24951 Roo.bootstrap.dash.TabBox = function(config){
24952     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24953     this.addEvents({
24954         // raw events
24955         /**
24956          * @event addpane
24957          * When a pane is added
24958          * @param {Roo.bootstrap.dash.TabPane} pane
24959          */
24960         "addpane" : true,
24961         /**
24962          * @event activatepane
24963          * When a pane is activated
24964          * @param {Roo.bootstrap.dash.TabPane} pane
24965          */
24966         "activatepane" : true
24967         
24968          
24969     });
24970     
24971     this.panes = [];
24972 };
24973
24974 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24975
24976     title : '',
24977     icon : false,
24978     showtabs : true,
24979     tabScrollable : false,
24980     
24981     getChildContainer : function()
24982     {
24983         return this.el.select('.tab-content', true).first();
24984     },
24985     
24986     getAutoCreate : function(){
24987         
24988         var header = {
24989             tag: 'li',
24990             cls: 'pull-left header',
24991             html: this.title,
24992             cn : []
24993         };
24994         
24995         if(this.icon){
24996             header.cn.push({
24997                 tag: 'i',
24998                 cls: 'fa ' + this.icon
24999             });
25000         }
25001         
25002         var h = {
25003             tag: 'ul',
25004             cls: 'nav nav-tabs pull-right',
25005             cn: [
25006                 header
25007             ]
25008         };
25009         
25010         if(this.tabScrollable){
25011             h = {
25012                 tag: 'div',
25013                 cls: 'tab-header',
25014                 cn: [
25015                     {
25016                         tag: 'ul',
25017                         cls: 'nav nav-tabs pull-right',
25018                         cn: [
25019                             header
25020                         ]
25021                     }
25022                 ]
25023             };
25024         }
25025         
25026         var cfg = {
25027             tag: 'div',
25028             cls: 'nav-tabs-custom',
25029             cn: [
25030                 h,
25031                 {
25032                     tag: 'div',
25033                     cls: 'tab-content no-padding',
25034                     cn: []
25035                 }
25036             ]
25037         };
25038
25039         return  cfg;
25040     },
25041     initEvents : function()
25042     {
25043         //Roo.log('add add pane handler');
25044         this.on('addpane', this.onAddPane, this);
25045     },
25046      /**
25047      * Updates the box title
25048      * @param {String} html to set the title to.
25049      */
25050     setTitle : function(value)
25051     {
25052         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25053     },
25054     onAddPane : function(pane)
25055     {
25056         this.panes.push(pane);
25057         //Roo.log('addpane');
25058         //Roo.log(pane);
25059         // tabs are rendere left to right..
25060         if(!this.showtabs){
25061             return;
25062         }
25063         
25064         var ctr = this.el.select('.nav-tabs', true).first();
25065          
25066          
25067         var existing = ctr.select('.nav-tab',true);
25068         var qty = existing.getCount();;
25069         
25070         
25071         var tab = ctr.createChild({
25072             tag : 'li',
25073             cls : 'nav-tab' + (qty ? '' : ' active'),
25074             cn : [
25075                 {
25076                     tag : 'a',
25077                     href:'#',
25078                     html : pane.title
25079                 }
25080             ]
25081         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25082         pane.tab = tab;
25083         
25084         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25085         if (!qty) {
25086             pane.el.addClass('active');
25087         }
25088         
25089                 
25090     },
25091     onTabClick : function(ev,un,ob,pane)
25092     {
25093         //Roo.log('tab - prev default');
25094         ev.preventDefault();
25095         
25096         
25097         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25098         pane.tab.addClass('active');
25099         //Roo.log(pane.title);
25100         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25101         // technically we should have a deactivate event.. but maybe add later.
25102         // and it should not de-activate the selected tab...
25103         this.fireEvent('activatepane', pane);
25104         pane.el.addClass('active');
25105         pane.fireEvent('activate');
25106         
25107         
25108     },
25109     
25110     getActivePane : function()
25111     {
25112         var r = false;
25113         Roo.each(this.panes, function(p) {
25114             if(p.el.hasClass('active')){
25115                 r = p;
25116                 return false;
25117             }
25118             
25119             return;
25120         });
25121         
25122         return r;
25123     }
25124     
25125     
25126 });
25127
25128  
25129 /*
25130  * - LGPL
25131  *
25132  * Tab pane
25133  * 
25134  */
25135 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25136 /**
25137  * @class Roo.bootstrap.TabPane
25138  * @extends Roo.bootstrap.Component
25139  * Bootstrap TabPane class
25140  * @cfg {Boolean} active (false | true) Default false
25141  * @cfg {String} title title of panel
25142
25143  * 
25144  * @constructor
25145  * Create a new TabPane
25146  * @param {Object} config The config object
25147  */
25148
25149 Roo.bootstrap.dash.TabPane = function(config){
25150     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25151     
25152     this.addEvents({
25153         // raw events
25154         /**
25155          * @event activate
25156          * When a pane is activated
25157          * @param {Roo.bootstrap.dash.TabPane} pane
25158          */
25159         "activate" : true
25160          
25161     });
25162 };
25163
25164 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25165     
25166     active : false,
25167     title : '',
25168     
25169     // the tabBox that this is attached to.
25170     tab : false,
25171      
25172     getAutoCreate : function() 
25173     {
25174         var cfg = {
25175             tag: 'div',
25176             cls: 'tab-pane'
25177         };
25178         
25179         if(this.active){
25180             cfg.cls += ' active';
25181         }
25182         
25183         return cfg;
25184     },
25185     initEvents  : function()
25186     {
25187         //Roo.log('trigger add pane handler');
25188         this.parent().fireEvent('addpane', this)
25189     },
25190     
25191      /**
25192      * Updates the tab title 
25193      * @param {String} html to set the title to.
25194      */
25195     setTitle: function(str)
25196     {
25197         if (!this.tab) {
25198             return;
25199         }
25200         this.title = str;
25201         this.tab.select('a', true).first().dom.innerHTML = str;
25202         
25203     }
25204     
25205     
25206     
25207 });
25208
25209  
25210
25211
25212  /*
25213  * - LGPL
25214  *
25215  * menu
25216  * 
25217  */
25218 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25219
25220 /**
25221  * @class Roo.bootstrap.menu.Menu
25222  * @extends Roo.bootstrap.Component
25223  * Bootstrap Menu class - container for Menu
25224  * @cfg {String} html Text of the menu
25225  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25226  * @cfg {String} icon Font awesome icon
25227  * @cfg {String} pos Menu align to (top | bottom) default bottom
25228  * 
25229  * 
25230  * @constructor
25231  * Create a new Menu
25232  * @param {Object} config The config object
25233  */
25234
25235
25236 Roo.bootstrap.menu.Menu = function(config){
25237     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25238     
25239     this.addEvents({
25240         /**
25241          * @event beforeshow
25242          * Fires before this menu is displayed
25243          * @param {Roo.bootstrap.menu.Menu} this
25244          */
25245         beforeshow : true,
25246         /**
25247          * @event beforehide
25248          * Fires before this menu is hidden
25249          * @param {Roo.bootstrap.menu.Menu} this
25250          */
25251         beforehide : true,
25252         /**
25253          * @event show
25254          * Fires after this menu is displayed
25255          * @param {Roo.bootstrap.menu.Menu} this
25256          */
25257         show : true,
25258         /**
25259          * @event hide
25260          * Fires after this menu is hidden
25261          * @param {Roo.bootstrap.menu.Menu} this
25262          */
25263         hide : true,
25264         /**
25265          * @event click
25266          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25267          * @param {Roo.bootstrap.menu.Menu} this
25268          * @param {Roo.EventObject} e
25269          */
25270         click : true
25271     });
25272     
25273 };
25274
25275 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25276     
25277     submenu : false,
25278     html : '',
25279     weight : 'default',
25280     icon : false,
25281     pos : 'bottom',
25282     
25283     
25284     getChildContainer : function() {
25285         if(this.isSubMenu){
25286             return this.el;
25287         }
25288         
25289         return this.el.select('ul.dropdown-menu', true).first();  
25290     },
25291     
25292     getAutoCreate : function()
25293     {
25294         var text = [
25295             {
25296                 tag : 'span',
25297                 cls : 'roo-menu-text',
25298                 html : this.html
25299             }
25300         ];
25301         
25302         if(this.icon){
25303             text.unshift({
25304                 tag : 'i',
25305                 cls : 'fa ' + this.icon
25306             })
25307         }
25308         
25309         
25310         var cfg = {
25311             tag : 'div',
25312             cls : 'btn-group',
25313             cn : [
25314                 {
25315                     tag : 'button',
25316                     cls : 'dropdown-button btn btn-' + this.weight,
25317                     cn : text
25318                 },
25319                 {
25320                     tag : 'button',
25321                     cls : 'dropdown-toggle btn btn-' + this.weight,
25322                     cn : [
25323                         {
25324                             tag : 'span',
25325                             cls : 'caret'
25326                         }
25327                     ]
25328                 },
25329                 {
25330                     tag : 'ul',
25331                     cls : 'dropdown-menu'
25332                 }
25333             ]
25334             
25335         };
25336         
25337         if(this.pos == 'top'){
25338             cfg.cls += ' dropup';
25339         }
25340         
25341         if(this.isSubMenu){
25342             cfg = {
25343                 tag : 'ul',
25344                 cls : 'dropdown-menu'
25345             }
25346         }
25347         
25348         return cfg;
25349     },
25350     
25351     onRender : function(ct, position)
25352     {
25353         this.isSubMenu = ct.hasClass('dropdown-submenu');
25354         
25355         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25356     },
25357     
25358     initEvents : function() 
25359     {
25360         if(this.isSubMenu){
25361             return;
25362         }
25363         
25364         this.hidden = true;
25365         
25366         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25367         this.triggerEl.on('click', this.onTriggerPress, this);
25368         
25369         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25370         this.buttonEl.on('click', this.onClick, this);
25371         
25372     },
25373     
25374     list : function()
25375     {
25376         if(this.isSubMenu){
25377             return this.el;
25378         }
25379         
25380         return this.el.select('ul.dropdown-menu', true).first();
25381     },
25382     
25383     onClick : function(e)
25384     {
25385         this.fireEvent("click", this, e);
25386     },
25387     
25388     onTriggerPress  : function(e)
25389     {   
25390         if (this.isVisible()) {
25391             this.hide();
25392         } else {
25393             this.show();
25394         }
25395     },
25396     
25397     isVisible : function(){
25398         return !this.hidden;
25399     },
25400     
25401     show : function()
25402     {
25403         this.fireEvent("beforeshow", this);
25404         
25405         this.hidden = false;
25406         this.el.addClass('open');
25407         
25408         Roo.get(document).on("mouseup", this.onMouseUp, this);
25409         
25410         this.fireEvent("show", this);
25411         
25412         
25413     },
25414     
25415     hide : function()
25416     {
25417         this.fireEvent("beforehide", this);
25418         
25419         this.hidden = true;
25420         this.el.removeClass('open');
25421         
25422         Roo.get(document).un("mouseup", this.onMouseUp);
25423         
25424         this.fireEvent("hide", this);
25425     },
25426     
25427     onMouseUp : function()
25428     {
25429         this.hide();
25430     }
25431     
25432 });
25433
25434  
25435  /*
25436  * - LGPL
25437  *
25438  * menu item
25439  * 
25440  */
25441 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25442
25443 /**
25444  * @class Roo.bootstrap.menu.Item
25445  * @extends Roo.bootstrap.Component
25446  * Bootstrap MenuItem class
25447  * @cfg {Boolean} submenu (true | false) default false
25448  * @cfg {String} html text of the item
25449  * @cfg {String} href the link
25450  * @cfg {Boolean} disable (true | false) default false
25451  * @cfg {Boolean} preventDefault (true | false) default true
25452  * @cfg {String} icon Font awesome icon
25453  * @cfg {String} pos Submenu align to (left | right) default right 
25454  * 
25455  * 
25456  * @constructor
25457  * Create a new Item
25458  * @param {Object} config The config object
25459  */
25460
25461
25462 Roo.bootstrap.menu.Item = function(config){
25463     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25464     this.addEvents({
25465         /**
25466          * @event mouseover
25467          * Fires when the mouse is hovering over this menu
25468          * @param {Roo.bootstrap.menu.Item} this
25469          * @param {Roo.EventObject} e
25470          */
25471         mouseover : true,
25472         /**
25473          * @event mouseout
25474          * Fires when the mouse exits this menu
25475          * @param {Roo.bootstrap.menu.Item} this
25476          * @param {Roo.EventObject} e
25477          */
25478         mouseout : true,
25479         // raw events
25480         /**
25481          * @event click
25482          * The raw click event for the entire grid.
25483          * @param {Roo.EventObject} e
25484          */
25485         click : true
25486     });
25487 };
25488
25489 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25490     
25491     submenu : false,
25492     href : '',
25493     html : '',
25494     preventDefault: true,
25495     disable : false,
25496     icon : false,
25497     pos : 'right',
25498     
25499     getAutoCreate : function()
25500     {
25501         var text = [
25502             {
25503                 tag : 'span',
25504                 cls : 'roo-menu-item-text',
25505                 html : this.html
25506             }
25507         ];
25508         
25509         if(this.icon){
25510             text.unshift({
25511                 tag : 'i',
25512                 cls : 'fa ' + this.icon
25513             })
25514         }
25515         
25516         var cfg = {
25517             tag : 'li',
25518             cn : [
25519                 {
25520                     tag : 'a',
25521                     href : this.href || '#',
25522                     cn : text
25523                 }
25524             ]
25525         };
25526         
25527         if(this.disable){
25528             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25529         }
25530         
25531         if(this.submenu){
25532             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25533             
25534             if(this.pos == 'left'){
25535                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25536             }
25537         }
25538         
25539         return cfg;
25540     },
25541     
25542     initEvents : function() 
25543     {
25544         this.el.on('mouseover', this.onMouseOver, this);
25545         this.el.on('mouseout', this.onMouseOut, this);
25546         
25547         this.el.select('a', true).first().on('click', this.onClick, this);
25548         
25549     },
25550     
25551     onClick : function(e)
25552     {
25553         if(this.preventDefault){
25554             e.preventDefault();
25555         }
25556         
25557         this.fireEvent("click", this, e);
25558     },
25559     
25560     onMouseOver : function(e)
25561     {
25562         if(this.submenu && this.pos == 'left'){
25563             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25564         }
25565         
25566         this.fireEvent("mouseover", this, e);
25567     },
25568     
25569     onMouseOut : function(e)
25570     {
25571         this.fireEvent("mouseout", this, e);
25572     }
25573 });
25574
25575  
25576
25577  /*
25578  * - LGPL
25579  *
25580  * menu separator
25581  * 
25582  */
25583 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25584
25585 /**
25586  * @class Roo.bootstrap.menu.Separator
25587  * @extends Roo.bootstrap.Component
25588  * Bootstrap Separator class
25589  * 
25590  * @constructor
25591  * Create a new Separator
25592  * @param {Object} config The config object
25593  */
25594
25595
25596 Roo.bootstrap.menu.Separator = function(config){
25597     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25598 };
25599
25600 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25601     
25602     getAutoCreate : function(){
25603         var cfg = {
25604             tag : 'li',
25605             cls: 'divider'
25606         };
25607         
25608         return cfg;
25609     }
25610    
25611 });
25612
25613  
25614
25615  /*
25616  * - LGPL
25617  *
25618  * Tooltip
25619  * 
25620  */
25621
25622 /**
25623  * @class Roo.bootstrap.Tooltip
25624  * Bootstrap Tooltip class
25625  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25626  * to determine which dom element triggers the tooltip.
25627  * 
25628  * It needs to add support for additional attributes like tooltip-position
25629  * 
25630  * @constructor
25631  * Create a new Toolti
25632  * @param {Object} config The config object
25633  */
25634
25635 Roo.bootstrap.Tooltip = function(config){
25636     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25637     
25638     this.alignment = Roo.bootstrap.Tooltip.alignment;
25639     
25640     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25641         this.alignment = config.alignment;
25642     }
25643     
25644 };
25645
25646 Roo.apply(Roo.bootstrap.Tooltip, {
25647     /**
25648      * @function init initialize tooltip monitoring.
25649      * @static
25650      */
25651     currentEl : false,
25652     currentTip : false,
25653     currentRegion : false,
25654     
25655     //  init : delay?
25656     
25657     init : function()
25658     {
25659         Roo.get(document).on('mouseover', this.enter ,this);
25660         Roo.get(document).on('mouseout', this.leave, this);
25661          
25662         
25663         this.currentTip = new Roo.bootstrap.Tooltip();
25664     },
25665     
25666     enter : function(ev)
25667     {
25668         var dom = ev.getTarget();
25669         
25670         //Roo.log(['enter',dom]);
25671         var el = Roo.fly(dom);
25672         if (this.currentEl) {
25673             //Roo.log(dom);
25674             //Roo.log(this.currentEl);
25675             //Roo.log(this.currentEl.contains(dom));
25676             if (this.currentEl == el) {
25677                 return;
25678             }
25679             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25680                 return;
25681             }
25682
25683         }
25684         
25685         if (this.currentTip.el) {
25686             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25687         }    
25688         //Roo.log(ev);
25689         
25690         if(!el || el.dom == document){
25691             return;
25692         }
25693         
25694         var bindEl = el;
25695         
25696         // you can not look for children, as if el is the body.. then everythign is the child..
25697         if (!el.attr('tooltip')) { //
25698             if (!el.select("[tooltip]").elements.length) {
25699                 return;
25700             }
25701             // is the mouse over this child...?
25702             bindEl = el.select("[tooltip]").first();
25703             var xy = ev.getXY();
25704             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25705                 //Roo.log("not in region.");
25706                 return;
25707             }
25708             //Roo.log("child element over..");
25709             
25710         }
25711         this.currentEl = bindEl;
25712         this.currentTip.bind(bindEl);
25713         this.currentRegion = Roo.lib.Region.getRegion(dom);
25714         this.currentTip.enter();
25715         
25716     },
25717     leave : function(ev)
25718     {
25719         var dom = ev.getTarget();
25720         //Roo.log(['leave',dom]);
25721         if (!this.currentEl) {
25722             return;
25723         }
25724         
25725         
25726         if (dom != this.currentEl.dom) {
25727             return;
25728         }
25729         var xy = ev.getXY();
25730         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25731             return;
25732         }
25733         // only activate leave if mouse cursor is outside... bounding box..
25734         
25735         
25736         
25737         
25738         if (this.currentTip) {
25739             this.currentTip.leave();
25740         }
25741         //Roo.log('clear currentEl');
25742         this.currentEl = false;
25743         
25744         
25745     },
25746     alignment : {
25747         'left' : ['r-l', [-2,0], 'right'],
25748         'right' : ['l-r', [2,0], 'left'],
25749         'bottom' : ['t-b', [0,2], 'top'],
25750         'top' : [ 'b-t', [0,-2], 'bottom']
25751     }
25752     
25753 });
25754
25755
25756 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25757     
25758     
25759     bindEl : false,
25760     
25761     delay : null, // can be { show : 300 , hide: 500}
25762     
25763     timeout : null,
25764     
25765     hoverState : null, //???
25766     
25767     placement : 'bottom', 
25768     
25769     alignment : false,
25770     
25771     getAutoCreate : function(){
25772     
25773         var cfg = {
25774            cls : 'tooltip',
25775            role : 'tooltip',
25776            cn : [
25777                 {
25778                     cls : 'tooltip-arrow'
25779                 },
25780                 {
25781                     cls : 'tooltip-inner'
25782                 }
25783            ]
25784         };
25785         
25786         return cfg;
25787     },
25788     bind : function(el)
25789     {
25790         this.bindEl = el;
25791     },
25792       
25793     
25794     enter : function () {
25795        
25796         if (this.timeout != null) {
25797             clearTimeout(this.timeout);
25798         }
25799         
25800         this.hoverState = 'in';
25801          //Roo.log("enter - show");
25802         if (!this.delay || !this.delay.show) {
25803             this.show();
25804             return;
25805         }
25806         var _t = this;
25807         this.timeout = setTimeout(function () {
25808             if (_t.hoverState == 'in') {
25809                 _t.show();
25810             }
25811         }, this.delay.show);
25812     },
25813     leave : function()
25814     {
25815         clearTimeout(this.timeout);
25816     
25817         this.hoverState = 'out';
25818          if (!this.delay || !this.delay.hide) {
25819             this.hide();
25820             return;
25821         }
25822        
25823         var _t = this;
25824         this.timeout = setTimeout(function () {
25825             //Roo.log("leave - timeout");
25826             
25827             if (_t.hoverState == 'out') {
25828                 _t.hide();
25829                 Roo.bootstrap.Tooltip.currentEl = false;
25830             }
25831         }, delay);
25832     },
25833     
25834     show : function (msg)
25835     {
25836         if (!this.el) {
25837             this.render(document.body);
25838         }
25839         // set content.
25840         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25841         
25842         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25843         
25844         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25845         
25846         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25847         
25848         var placement = typeof this.placement == 'function' ?
25849             this.placement.call(this, this.el, on_el) :
25850             this.placement;
25851             
25852         var autoToken = /\s?auto?\s?/i;
25853         var autoPlace = autoToken.test(placement);
25854         if (autoPlace) {
25855             placement = placement.replace(autoToken, '') || 'top';
25856         }
25857         
25858         //this.el.detach()
25859         //this.el.setXY([0,0]);
25860         this.el.show();
25861         //this.el.dom.style.display='block';
25862         
25863         //this.el.appendTo(on_el);
25864         
25865         var p = this.getPosition();
25866         var box = this.el.getBox();
25867         
25868         if (autoPlace) {
25869             // fixme..
25870         }
25871         
25872         var align = this.alignment[placement];
25873         
25874         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25875         
25876         if(placement == 'top' || placement == 'bottom'){
25877             if(xy[0] < 0){
25878                 placement = 'right';
25879             }
25880             
25881             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25882                 placement = 'left';
25883             }
25884             
25885             var scroll = Roo.select('body', true).first().getScroll();
25886             
25887             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25888                 placement = 'top';
25889             }
25890             
25891         }
25892         
25893         this.el.alignTo(this.bindEl, align[0],align[1]);
25894         //var arrow = this.el.select('.arrow',true).first();
25895         //arrow.set(align[2], 
25896         
25897         this.el.addClass(placement);
25898         
25899         this.el.addClass('in fade');
25900         
25901         this.hoverState = null;
25902         
25903         if (this.el.hasClass('fade')) {
25904             // fade it?
25905         }
25906         
25907     },
25908     hide : function()
25909     {
25910          
25911         if (!this.el) {
25912             return;
25913         }
25914         //this.el.setXY([0,0]);
25915         this.el.removeClass('in');
25916         //this.el.hide();
25917         
25918     }
25919     
25920 });
25921  
25922
25923  /*
25924  * - LGPL
25925  *
25926  * Location Picker
25927  * 
25928  */
25929
25930 /**
25931  * @class Roo.bootstrap.LocationPicker
25932  * @extends Roo.bootstrap.Component
25933  * Bootstrap LocationPicker class
25934  * @cfg {Number} latitude Position when init default 0
25935  * @cfg {Number} longitude Position when init default 0
25936  * @cfg {Number} zoom default 15
25937  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25938  * @cfg {Boolean} mapTypeControl default false
25939  * @cfg {Boolean} disableDoubleClickZoom default false
25940  * @cfg {Boolean} scrollwheel default true
25941  * @cfg {Boolean} streetViewControl default false
25942  * @cfg {Number} radius default 0
25943  * @cfg {String} locationName
25944  * @cfg {Boolean} draggable default true
25945  * @cfg {Boolean} enableAutocomplete default false
25946  * @cfg {Boolean} enableReverseGeocode default true
25947  * @cfg {String} markerTitle
25948  * 
25949  * @constructor
25950  * Create a new LocationPicker
25951  * @param {Object} config The config object
25952  */
25953
25954
25955 Roo.bootstrap.LocationPicker = function(config){
25956     
25957     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25958     
25959     this.addEvents({
25960         /**
25961          * @event initial
25962          * Fires when the picker initialized.
25963          * @param {Roo.bootstrap.LocationPicker} this
25964          * @param {Google Location} location
25965          */
25966         initial : true,
25967         /**
25968          * @event positionchanged
25969          * Fires when the picker position changed.
25970          * @param {Roo.bootstrap.LocationPicker} this
25971          * @param {Google Location} location
25972          */
25973         positionchanged : true,
25974         /**
25975          * @event resize
25976          * Fires when the map resize.
25977          * @param {Roo.bootstrap.LocationPicker} this
25978          */
25979         resize : true,
25980         /**
25981          * @event show
25982          * Fires when the map show.
25983          * @param {Roo.bootstrap.LocationPicker} this
25984          */
25985         show : true,
25986         /**
25987          * @event hide
25988          * Fires when the map hide.
25989          * @param {Roo.bootstrap.LocationPicker} this
25990          */
25991         hide : true,
25992         /**
25993          * @event mapClick
25994          * Fires when click the map.
25995          * @param {Roo.bootstrap.LocationPicker} this
25996          * @param {Map event} e
25997          */
25998         mapClick : true,
25999         /**
26000          * @event mapRightClick
26001          * Fires when right click the map.
26002          * @param {Roo.bootstrap.LocationPicker} this
26003          * @param {Map event} e
26004          */
26005         mapRightClick : true,
26006         /**
26007          * @event markerClick
26008          * Fires when click the marker.
26009          * @param {Roo.bootstrap.LocationPicker} this
26010          * @param {Map event} e
26011          */
26012         markerClick : true,
26013         /**
26014          * @event markerRightClick
26015          * Fires when right click the marker.
26016          * @param {Roo.bootstrap.LocationPicker} this
26017          * @param {Map event} e
26018          */
26019         markerRightClick : true,
26020         /**
26021          * @event OverlayViewDraw
26022          * Fires when OverlayView Draw
26023          * @param {Roo.bootstrap.LocationPicker} this
26024          */
26025         OverlayViewDraw : true,
26026         /**
26027          * @event OverlayViewOnAdd
26028          * Fires when OverlayView Draw
26029          * @param {Roo.bootstrap.LocationPicker} this
26030          */
26031         OverlayViewOnAdd : true,
26032         /**
26033          * @event OverlayViewOnRemove
26034          * Fires when OverlayView Draw
26035          * @param {Roo.bootstrap.LocationPicker} this
26036          */
26037         OverlayViewOnRemove : true,
26038         /**
26039          * @event OverlayViewShow
26040          * Fires when OverlayView Draw
26041          * @param {Roo.bootstrap.LocationPicker} this
26042          * @param {Pixel} cpx
26043          */
26044         OverlayViewShow : true,
26045         /**
26046          * @event OverlayViewHide
26047          * Fires when OverlayView Draw
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          */
26050         OverlayViewHide : true,
26051         /**
26052          * @event loadexception
26053          * Fires when load google lib failed.
26054          * @param {Roo.bootstrap.LocationPicker} this
26055          */
26056         loadexception : true
26057     });
26058         
26059 };
26060
26061 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26062     
26063     gMapContext: false,
26064     
26065     latitude: 0,
26066     longitude: 0,
26067     zoom: 15,
26068     mapTypeId: false,
26069     mapTypeControl: false,
26070     disableDoubleClickZoom: false,
26071     scrollwheel: true,
26072     streetViewControl: false,
26073     radius: 0,
26074     locationName: '',
26075     draggable: true,
26076     enableAutocomplete: false,
26077     enableReverseGeocode: true,
26078     markerTitle: '',
26079     
26080     getAutoCreate: function()
26081     {
26082
26083         var cfg = {
26084             tag: 'div',
26085             cls: 'roo-location-picker'
26086         };
26087         
26088         return cfg
26089     },
26090     
26091     initEvents: function(ct, position)
26092     {       
26093         if(!this.el.getWidth() || this.isApplied()){
26094             return;
26095         }
26096         
26097         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26098         
26099         this.initial();
26100     },
26101     
26102     initial: function()
26103     {
26104         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26105             this.fireEvent('loadexception', this);
26106             return;
26107         }
26108         
26109         if(!this.mapTypeId){
26110             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26111         }
26112         
26113         this.gMapContext = this.GMapContext();
26114         
26115         this.initOverlayView();
26116         
26117         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26118         
26119         var _this = this;
26120                 
26121         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26122             _this.setPosition(_this.gMapContext.marker.position);
26123         });
26124         
26125         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26126             _this.fireEvent('mapClick', this, event);
26127             
26128         });
26129
26130         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26131             _this.fireEvent('mapRightClick', this, event);
26132             
26133         });
26134         
26135         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26136             _this.fireEvent('markerClick', this, event);
26137             
26138         });
26139
26140         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26141             _this.fireEvent('markerRightClick', this, event);
26142             
26143         });
26144         
26145         this.setPosition(this.gMapContext.location);
26146         
26147         this.fireEvent('initial', this, this.gMapContext.location);
26148     },
26149     
26150     initOverlayView: function()
26151     {
26152         var _this = this;
26153         
26154         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26155             
26156             draw: function()
26157             {
26158                 _this.fireEvent('OverlayViewDraw', _this);
26159             },
26160             
26161             onAdd: function()
26162             {
26163                 _this.fireEvent('OverlayViewOnAdd', _this);
26164             },
26165             
26166             onRemove: function()
26167             {
26168                 _this.fireEvent('OverlayViewOnRemove', _this);
26169             },
26170             
26171             show: function(cpx)
26172             {
26173                 _this.fireEvent('OverlayViewShow', _this, cpx);
26174             },
26175             
26176             hide: function()
26177             {
26178                 _this.fireEvent('OverlayViewHide', _this);
26179             }
26180             
26181         });
26182     },
26183     
26184     fromLatLngToContainerPixel: function(event)
26185     {
26186         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26187     },
26188     
26189     isApplied: function() 
26190     {
26191         return this.getGmapContext() == false ? false : true;
26192     },
26193     
26194     getGmapContext: function() 
26195     {
26196         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26197     },
26198     
26199     GMapContext: function() 
26200     {
26201         var position = new google.maps.LatLng(this.latitude, this.longitude);
26202         
26203         var _map = new google.maps.Map(this.el.dom, {
26204             center: position,
26205             zoom: this.zoom,
26206             mapTypeId: this.mapTypeId,
26207             mapTypeControl: this.mapTypeControl,
26208             disableDoubleClickZoom: this.disableDoubleClickZoom,
26209             scrollwheel: this.scrollwheel,
26210             streetViewControl: this.streetViewControl,
26211             locationName: this.locationName,
26212             draggable: this.draggable,
26213             enableAutocomplete: this.enableAutocomplete,
26214             enableReverseGeocode: this.enableReverseGeocode
26215         });
26216         
26217         var _marker = new google.maps.Marker({
26218             position: position,
26219             map: _map,
26220             title: this.markerTitle,
26221             draggable: this.draggable
26222         });
26223         
26224         return {
26225             map: _map,
26226             marker: _marker,
26227             circle: null,
26228             location: position,
26229             radius: this.radius,
26230             locationName: this.locationName,
26231             addressComponents: {
26232                 formatted_address: null,
26233                 addressLine1: null,
26234                 addressLine2: null,
26235                 streetName: null,
26236                 streetNumber: null,
26237                 city: null,
26238                 district: null,
26239                 state: null,
26240                 stateOrProvince: null
26241             },
26242             settings: this,
26243             domContainer: this.el.dom,
26244             geodecoder: new google.maps.Geocoder()
26245         };
26246     },
26247     
26248     drawCircle: function(center, radius, options) 
26249     {
26250         if (this.gMapContext.circle != null) {
26251             this.gMapContext.circle.setMap(null);
26252         }
26253         if (radius > 0) {
26254             radius *= 1;
26255             options = Roo.apply({}, options, {
26256                 strokeColor: "#0000FF",
26257                 strokeOpacity: .35,
26258                 strokeWeight: 2,
26259                 fillColor: "#0000FF",
26260                 fillOpacity: .2
26261             });
26262             
26263             options.map = this.gMapContext.map;
26264             options.radius = radius;
26265             options.center = center;
26266             this.gMapContext.circle = new google.maps.Circle(options);
26267             return this.gMapContext.circle;
26268         }
26269         
26270         return null;
26271     },
26272     
26273     setPosition: function(location) 
26274     {
26275         this.gMapContext.location = location;
26276         this.gMapContext.marker.setPosition(location);
26277         this.gMapContext.map.panTo(location);
26278         this.drawCircle(location, this.gMapContext.radius, {});
26279         
26280         var _this = this;
26281         
26282         if (this.gMapContext.settings.enableReverseGeocode) {
26283             this.gMapContext.geodecoder.geocode({
26284                 latLng: this.gMapContext.location
26285             }, function(results, status) {
26286                 
26287                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26288                     _this.gMapContext.locationName = results[0].formatted_address;
26289                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26290                     
26291                     _this.fireEvent('positionchanged', this, location);
26292                 }
26293             });
26294             
26295             return;
26296         }
26297         
26298         this.fireEvent('positionchanged', this, location);
26299     },
26300     
26301     resize: function()
26302     {
26303         google.maps.event.trigger(this.gMapContext.map, "resize");
26304         
26305         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26306         
26307         this.fireEvent('resize', this);
26308     },
26309     
26310     setPositionByLatLng: function(latitude, longitude)
26311     {
26312         this.setPosition(new google.maps.LatLng(latitude, longitude));
26313     },
26314     
26315     getCurrentPosition: function() 
26316     {
26317         return {
26318             latitude: this.gMapContext.location.lat(),
26319             longitude: this.gMapContext.location.lng()
26320         };
26321     },
26322     
26323     getAddressName: function() 
26324     {
26325         return this.gMapContext.locationName;
26326     },
26327     
26328     getAddressComponents: function() 
26329     {
26330         return this.gMapContext.addressComponents;
26331     },
26332     
26333     address_component_from_google_geocode: function(address_components) 
26334     {
26335         var result = {};
26336         
26337         for (var i = 0; i < address_components.length; i++) {
26338             var component = address_components[i];
26339             if (component.types.indexOf("postal_code") >= 0) {
26340                 result.postalCode = component.short_name;
26341             } else if (component.types.indexOf("street_number") >= 0) {
26342                 result.streetNumber = component.short_name;
26343             } else if (component.types.indexOf("route") >= 0) {
26344                 result.streetName = component.short_name;
26345             } else if (component.types.indexOf("neighborhood") >= 0) {
26346                 result.city = component.short_name;
26347             } else if (component.types.indexOf("locality") >= 0) {
26348                 result.city = component.short_name;
26349             } else if (component.types.indexOf("sublocality") >= 0) {
26350                 result.district = component.short_name;
26351             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26352                 result.stateOrProvince = component.short_name;
26353             } else if (component.types.indexOf("country") >= 0) {
26354                 result.country = component.short_name;
26355             }
26356         }
26357         
26358         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26359         result.addressLine2 = "";
26360         return result;
26361     },
26362     
26363     setZoomLevel: function(zoom)
26364     {
26365         this.gMapContext.map.setZoom(zoom);
26366     },
26367     
26368     show: function()
26369     {
26370         if(!this.el){
26371             return;
26372         }
26373         
26374         this.el.show();
26375         
26376         this.resize();
26377         
26378         this.fireEvent('show', this);
26379     },
26380     
26381     hide: function()
26382     {
26383         if(!this.el){
26384             return;
26385         }
26386         
26387         this.el.hide();
26388         
26389         this.fireEvent('hide', this);
26390     }
26391     
26392 });
26393
26394 Roo.apply(Roo.bootstrap.LocationPicker, {
26395     
26396     OverlayView : function(map, options)
26397     {
26398         options = options || {};
26399         
26400         this.setMap(map);
26401     }
26402     
26403     
26404 });/*
26405  * - LGPL
26406  *
26407  * Alert
26408  * 
26409  */
26410
26411 /**
26412  * @class Roo.bootstrap.Alert
26413  * @extends Roo.bootstrap.Component
26414  * Bootstrap Alert class
26415  * @cfg {String} title The title of alert
26416  * @cfg {String} html The content of alert
26417  * @cfg {String} weight (  success | info | warning | danger )
26418  * @cfg {String} faicon font-awesomeicon
26419  * 
26420  * @constructor
26421  * Create a new alert
26422  * @param {Object} config The config object
26423  */
26424
26425
26426 Roo.bootstrap.Alert = function(config){
26427     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26428     
26429 };
26430
26431 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26432     
26433     title: '',
26434     html: '',
26435     weight: false,
26436     faicon: false,
26437     
26438     getAutoCreate : function()
26439     {
26440         
26441         var cfg = {
26442             tag : 'div',
26443             cls : 'alert',
26444             cn : [
26445                 {
26446                     tag : 'i',
26447                     cls : 'roo-alert-icon'
26448                     
26449                 },
26450                 {
26451                     tag : 'b',
26452                     cls : 'roo-alert-title',
26453                     html : this.title
26454                 },
26455                 {
26456                     tag : 'span',
26457                     cls : 'roo-alert-text',
26458                     html : this.html
26459                 }
26460             ]
26461         };
26462         
26463         if(this.faicon){
26464             cfg.cn[0].cls += ' fa ' + this.faicon;
26465         }
26466         
26467         if(this.weight){
26468             cfg.cls += ' alert-' + this.weight;
26469         }
26470         
26471         return cfg;
26472     },
26473     
26474     initEvents: function() 
26475     {
26476         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26477     },
26478     
26479     setTitle : function(str)
26480     {
26481         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26482     },
26483     
26484     setText : function(str)
26485     {
26486         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26487     },
26488     
26489     setWeight : function(weight)
26490     {
26491         if(this.weight){
26492             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26493         }
26494         
26495         this.weight = weight;
26496         
26497         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26498     },
26499     
26500     setIcon : function(icon)
26501     {
26502         if(this.faicon){
26503             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26504         }
26505         
26506         this.faicon = icon;
26507         
26508         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26509     },
26510     
26511     hide: function() 
26512     {
26513         this.el.hide();   
26514     },
26515     
26516     show: function() 
26517     {  
26518         this.el.show();   
26519     }
26520     
26521 });
26522
26523  
26524 /*
26525 * Licence: LGPL
26526 */
26527
26528 /**
26529  * @class Roo.bootstrap.UploadCropbox
26530  * @extends Roo.bootstrap.Component
26531  * Bootstrap UploadCropbox class
26532  * @cfg {String} emptyText show when image has been loaded
26533  * @cfg {String} rotateNotify show when image too small to rotate
26534  * @cfg {Number} errorTimeout default 3000
26535  * @cfg {Number} minWidth default 300
26536  * @cfg {Number} minHeight default 300
26537  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26538  * @cfg {Boolean} isDocument (true|false) default false
26539  * @cfg {String} url action url
26540  * @cfg {String} paramName default 'imageUpload'
26541  * @cfg {String} method default POST
26542  * @cfg {Boolean} loadMask (true|false) default true
26543  * @cfg {Boolean} loadingText default 'Loading...'
26544  * 
26545  * @constructor
26546  * Create a new UploadCropbox
26547  * @param {Object} config The config object
26548  */
26549
26550 Roo.bootstrap.UploadCropbox = function(config){
26551     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26552     
26553     this.addEvents({
26554         /**
26555          * @event beforeselectfile
26556          * Fire before select file
26557          * @param {Roo.bootstrap.UploadCropbox} this
26558          */
26559         "beforeselectfile" : true,
26560         /**
26561          * @event initial
26562          * Fire after initEvent
26563          * @param {Roo.bootstrap.UploadCropbox} this
26564          */
26565         "initial" : true,
26566         /**
26567          * @event crop
26568          * Fire after initEvent
26569          * @param {Roo.bootstrap.UploadCropbox} this
26570          * @param {String} data
26571          */
26572         "crop" : true,
26573         /**
26574          * @event prepare
26575          * Fire when preparing the file data
26576          * @param {Roo.bootstrap.UploadCropbox} this
26577          * @param {Object} file
26578          */
26579         "prepare" : true,
26580         /**
26581          * @event exception
26582          * Fire when get exception
26583          * @param {Roo.bootstrap.UploadCropbox} this
26584          * @param {XMLHttpRequest} xhr
26585          */
26586         "exception" : true,
26587         /**
26588          * @event beforeloadcanvas
26589          * Fire before load the canvas
26590          * @param {Roo.bootstrap.UploadCropbox} this
26591          * @param {String} src
26592          */
26593         "beforeloadcanvas" : true,
26594         /**
26595          * @event trash
26596          * Fire when trash image
26597          * @param {Roo.bootstrap.UploadCropbox} this
26598          */
26599         "trash" : true,
26600         /**
26601          * @event download
26602          * Fire when download the image
26603          * @param {Roo.bootstrap.UploadCropbox} this
26604          */
26605         "download" : true,
26606         /**
26607          * @event footerbuttonclick
26608          * Fire when footerbuttonclick
26609          * @param {Roo.bootstrap.UploadCropbox} this
26610          * @param {String} type
26611          */
26612         "footerbuttonclick" : true,
26613         /**
26614          * @event resize
26615          * Fire when resize
26616          * @param {Roo.bootstrap.UploadCropbox} this
26617          */
26618         "resize" : true,
26619         /**
26620          * @event rotate
26621          * Fire when rotate the image
26622          * @param {Roo.bootstrap.UploadCropbox} this
26623          * @param {String} pos
26624          */
26625         "rotate" : true,
26626         /**
26627          * @event inspect
26628          * Fire when inspect the file
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          * @param {Object} file
26631          */
26632         "inspect" : true,
26633         /**
26634          * @event upload
26635          * Fire when xhr upload the file
26636          * @param {Roo.bootstrap.UploadCropbox} this
26637          * @param {Object} data
26638          */
26639         "upload" : true,
26640         /**
26641          * @event arrange
26642          * Fire when arrange the file data
26643          * @param {Roo.bootstrap.UploadCropbox} this
26644          * @param {Object} formData
26645          */
26646         "arrange" : true
26647     });
26648     
26649     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26650 };
26651
26652 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26653     
26654     emptyText : 'Click to upload image',
26655     rotateNotify : 'Image is too small to rotate',
26656     errorTimeout : 3000,
26657     scale : 0,
26658     baseScale : 1,
26659     rotate : 0,
26660     dragable : false,
26661     pinching : false,
26662     mouseX : 0,
26663     mouseY : 0,
26664     cropData : false,
26665     minWidth : 300,
26666     minHeight : 300,
26667     file : false,
26668     exif : {},
26669     baseRotate : 1,
26670     cropType : 'image/jpeg',
26671     buttons : false,
26672     canvasLoaded : false,
26673     isDocument : false,
26674     method : 'POST',
26675     paramName : 'imageUpload',
26676     loadMask : true,
26677     loadingText : 'Loading...',
26678     maskEl : false,
26679     
26680     getAutoCreate : function()
26681     {
26682         var cfg = {
26683             tag : 'div',
26684             cls : 'roo-upload-cropbox',
26685             cn : [
26686                 {
26687                     tag : 'input',
26688                     cls : 'roo-upload-cropbox-selector',
26689                     type : 'file'
26690                 },
26691                 {
26692                     tag : 'div',
26693                     cls : 'roo-upload-cropbox-body',
26694                     style : 'cursor:pointer',
26695                     cn : [
26696                         {
26697                             tag : 'div',
26698                             cls : 'roo-upload-cropbox-preview'
26699                         },
26700                         {
26701                             tag : 'div',
26702                             cls : 'roo-upload-cropbox-thumb'
26703                         },
26704                         {
26705                             tag : 'div',
26706                             cls : 'roo-upload-cropbox-empty-notify',
26707                             html : this.emptyText
26708                         },
26709                         {
26710                             tag : 'div',
26711                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26712                             html : this.rotateNotify
26713                         }
26714                     ]
26715                 },
26716                 {
26717                     tag : 'div',
26718                     cls : 'roo-upload-cropbox-footer',
26719                     cn : {
26720                         tag : 'div',
26721                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26722                         cn : []
26723                     }
26724                 }
26725             ]
26726         };
26727         
26728         return cfg;
26729     },
26730     
26731     onRender : function(ct, position)
26732     {
26733         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26734         
26735         if (this.buttons.length) {
26736             
26737             Roo.each(this.buttons, function(bb) {
26738                 
26739                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26740                 
26741                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26742                 
26743             }, this);
26744         }
26745         
26746         if(this.loadMask){
26747             this.maskEl = this.el;
26748         }
26749     },
26750     
26751     initEvents : function()
26752     {
26753         this.urlAPI = (window.createObjectURL && window) || 
26754                                 (window.URL && URL.revokeObjectURL && URL) || 
26755                                 (window.webkitURL && webkitURL);
26756                         
26757         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26758         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26759         
26760         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26761         this.selectorEl.hide();
26762         
26763         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26764         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26765         
26766         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26767         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26768         this.thumbEl.hide();
26769         
26770         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26771         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26772         
26773         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26774         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26775         this.errorEl.hide();
26776         
26777         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26778         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26779         this.footerEl.hide();
26780         
26781         this.setThumbBoxSize();
26782         
26783         this.bind();
26784         
26785         this.resize();
26786         
26787         this.fireEvent('initial', this);
26788     },
26789
26790     bind : function()
26791     {
26792         var _this = this;
26793         
26794         window.addEventListener("resize", function() { _this.resize(); } );
26795         
26796         this.bodyEl.on('click', this.beforeSelectFile, this);
26797         
26798         if(Roo.isTouch){
26799             this.bodyEl.on('touchstart', this.onTouchStart, this);
26800             this.bodyEl.on('touchmove', this.onTouchMove, this);
26801             this.bodyEl.on('touchend', this.onTouchEnd, this);
26802         }
26803         
26804         if(!Roo.isTouch){
26805             this.bodyEl.on('mousedown', this.onMouseDown, this);
26806             this.bodyEl.on('mousemove', this.onMouseMove, this);
26807             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26808             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26809             Roo.get(document).on('mouseup', this.onMouseUp, this);
26810         }
26811         
26812         this.selectorEl.on('change', this.onFileSelected, this);
26813     },
26814     
26815     reset : function()
26816     {    
26817         this.scale = 0;
26818         this.baseScale = 1;
26819         this.rotate = 0;
26820         this.baseRotate = 1;
26821         this.dragable = false;
26822         this.pinching = false;
26823         this.mouseX = 0;
26824         this.mouseY = 0;
26825         this.cropData = false;
26826         this.notifyEl.dom.innerHTML = this.emptyText;
26827         
26828         this.selectorEl.dom.value = '';
26829         
26830     },
26831     
26832     resize : function()
26833     {
26834         if(this.fireEvent('resize', this) != false){
26835             this.setThumbBoxPosition();
26836             this.setCanvasPosition();
26837         }
26838     },
26839     
26840     onFooterButtonClick : function(e, el, o, type)
26841     {
26842         switch (type) {
26843             case 'rotate-left' :
26844                 this.onRotateLeft(e);
26845                 break;
26846             case 'rotate-right' :
26847                 this.onRotateRight(e);
26848                 break;
26849             case 'picture' :
26850                 this.beforeSelectFile(e);
26851                 break;
26852             case 'trash' :
26853                 this.trash(e);
26854                 break;
26855             case 'crop' :
26856                 this.crop(e);
26857                 break;
26858             case 'download' :
26859                 this.download(e);
26860                 break;
26861             default :
26862                 break;
26863         }
26864         
26865         this.fireEvent('footerbuttonclick', this, type);
26866     },
26867     
26868     beforeSelectFile : function(e)
26869     {
26870         e.preventDefault();
26871         
26872         if(this.fireEvent('beforeselectfile', this) != false){
26873             this.selectorEl.dom.click();
26874         }
26875     },
26876     
26877     onFileSelected : function(e)
26878     {
26879         e.preventDefault();
26880         
26881         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26882             return;
26883         }
26884         
26885         var file = this.selectorEl.dom.files[0];
26886         
26887         if(this.fireEvent('inspect', this, file) != false){
26888             this.prepare(file);
26889         }
26890         
26891     },
26892     
26893     trash : function(e)
26894     {
26895         this.fireEvent('trash', this);
26896     },
26897     
26898     download : function(e)
26899     {
26900         this.fireEvent('download', this);
26901     },
26902     
26903     loadCanvas : function(src)
26904     {   
26905         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26906             
26907             this.reset();
26908             
26909             this.imageEl = document.createElement('img');
26910             
26911             var _this = this;
26912             
26913             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26914             
26915             this.imageEl.src = src;
26916         }
26917     },
26918     
26919     onLoadCanvas : function()
26920     {   
26921         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26922         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26923         
26924         this.bodyEl.un('click', this.beforeSelectFile, this);
26925         
26926         this.notifyEl.hide();
26927         this.thumbEl.show();
26928         this.footerEl.show();
26929         
26930         this.baseRotateLevel();
26931         
26932         if(this.isDocument){
26933             this.setThumbBoxSize();
26934         }
26935         
26936         this.setThumbBoxPosition();
26937         
26938         this.baseScaleLevel();
26939         
26940         this.draw();
26941         
26942         this.resize();
26943         
26944         this.canvasLoaded = true;
26945         
26946         if(this.loadMask){
26947             this.maskEl.unmask();
26948         }
26949         
26950     },
26951     
26952     setCanvasPosition : function()
26953     {   
26954         if(!this.canvasEl){
26955             return;
26956         }
26957         
26958         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26959         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26960         
26961         this.previewEl.setLeft(pw);
26962         this.previewEl.setTop(ph);
26963         
26964     },
26965     
26966     onMouseDown : function(e)
26967     {   
26968         e.stopEvent();
26969         
26970         this.dragable = true;
26971         this.pinching = false;
26972         
26973         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26974             this.dragable = false;
26975             return;
26976         }
26977         
26978         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26979         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26980         
26981     },
26982     
26983     onMouseMove : function(e)
26984     {   
26985         e.stopEvent();
26986         
26987         if(!this.canvasLoaded){
26988             return;
26989         }
26990         
26991         if (!this.dragable){
26992             return;
26993         }
26994         
26995         var minX = Math.ceil(this.thumbEl.getLeft(true));
26996         var minY = Math.ceil(this.thumbEl.getTop(true));
26997         
26998         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26999         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27000         
27001         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27002         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27003         
27004         x = x - this.mouseX;
27005         y = y - this.mouseY;
27006         
27007         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27008         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27009         
27010         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27011         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27012         
27013         this.previewEl.setLeft(bgX);
27014         this.previewEl.setTop(bgY);
27015         
27016         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27017         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27018     },
27019     
27020     onMouseUp : function(e)
27021     {   
27022         e.stopEvent();
27023         
27024         this.dragable = false;
27025     },
27026     
27027     onMouseWheel : function(e)
27028     {   
27029         e.stopEvent();
27030         
27031         this.startScale = this.scale;
27032         
27033         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27034         
27035         if(!this.zoomable()){
27036             this.scale = this.startScale;
27037             return;
27038         }
27039         
27040         this.draw();
27041         
27042         return;
27043     },
27044     
27045     zoomable : function()
27046     {
27047         var minScale = this.thumbEl.getWidth() / this.minWidth;
27048         
27049         if(this.minWidth < this.minHeight){
27050             minScale = this.thumbEl.getHeight() / this.minHeight;
27051         }
27052         
27053         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27054         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27055         
27056         if(
27057                 this.isDocument &&
27058                 (this.rotate == 0 || this.rotate == 180) && 
27059                 (
27060                     width > this.imageEl.OriginWidth || 
27061                     height > this.imageEl.OriginHeight ||
27062                     (width < this.minWidth && height < this.minHeight)
27063                 )
27064         ){
27065             return false;
27066         }
27067         
27068         if(
27069                 this.isDocument &&
27070                 (this.rotate == 90 || this.rotate == 270) && 
27071                 (
27072                     width > this.imageEl.OriginWidth || 
27073                     height > this.imageEl.OriginHeight ||
27074                     (width < this.minHeight && height < this.minWidth)
27075                 )
27076         ){
27077             return false;
27078         }
27079         
27080         if(
27081                 !this.isDocument &&
27082                 (this.rotate == 0 || this.rotate == 180) && 
27083                 (
27084                     width < this.minWidth || 
27085                     width > this.imageEl.OriginWidth || 
27086                     height < this.minHeight || 
27087                     height > this.imageEl.OriginHeight
27088                 )
27089         ){
27090             return false;
27091         }
27092         
27093         if(
27094                 !this.isDocument &&
27095                 (this.rotate == 90 || this.rotate == 270) && 
27096                 (
27097                     width < this.minHeight || 
27098                     width > this.imageEl.OriginWidth || 
27099                     height < this.minWidth || 
27100                     height > this.imageEl.OriginHeight
27101                 )
27102         ){
27103             return false;
27104         }
27105         
27106         return true;
27107         
27108     },
27109     
27110     onRotateLeft : function(e)
27111     {   
27112         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27113             
27114             var minScale = this.thumbEl.getWidth() / this.minWidth;
27115             
27116             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27117             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27118             
27119             this.startScale = this.scale;
27120             
27121             while (this.getScaleLevel() < minScale){
27122             
27123                 this.scale = this.scale + 1;
27124                 
27125                 if(!this.zoomable()){
27126                     break;
27127                 }
27128                 
27129                 if(
27130                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27131                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27132                 ){
27133                     continue;
27134                 }
27135                 
27136                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27137
27138                 this.draw();
27139                 
27140                 return;
27141             }
27142             
27143             this.scale = this.startScale;
27144             
27145             this.onRotateFail();
27146             
27147             return false;
27148         }
27149         
27150         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27151
27152         if(this.isDocument){
27153             this.setThumbBoxSize();
27154             this.setThumbBoxPosition();
27155             this.setCanvasPosition();
27156         }
27157         
27158         this.draw();
27159         
27160         this.fireEvent('rotate', this, 'left');
27161         
27162     },
27163     
27164     onRotateRight : function(e)
27165     {
27166         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27167             
27168             var minScale = this.thumbEl.getWidth() / this.minWidth;
27169         
27170             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27171             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27172             
27173             this.startScale = this.scale;
27174             
27175             while (this.getScaleLevel() < minScale){
27176             
27177                 this.scale = this.scale + 1;
27178                 
27179                 if(!this.zoomable()){
27180                     break;
27181                 }
27182                 
27183                 if(
27184                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27185                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27186                 ){
27187                     continue;
27188                 }
27189                 
27190                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27191
27192                 this.draw();
27193                 
27194                 return;
27195             }
27196             
27197             this.scale = this.startScale;
27198             
27199             this.onRotateFail();
27200             
27201             return false;
27202         }
27203         
27204         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27205
27206         if(this.isDocument){
27207             this.setThumbBoxSize();
27208             this.setThumbBoxPosition();
27209             this.setCanvasPosition();
27210         }
27211         
27212         this.draw();
27213         
27214         this.fireEvent('rotate', this, 'right');
27215     },
27216     
27217     onRotateFail : function()
27218     {
27219         this.errorEl.show(true);
27220         
27221         var _this = this;
27222         
27223         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27224     },
27225     
27226     draw : function()
27227     {
27228         this.previewEl.dom.innerHTML = '';
27229         
27230         var canvasEl = document.createElement("canvas");
27231         
27232         var contextEl = canvasEl.getContext("2d");
27233         
27234         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27235         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27236         var center = this.imageEl.OriginWidth / 2;
27237         
27238         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27239             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27240             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27241             center = this.imageEl.OriginHeight / 2;
27242         }
27243         
27244         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27245         
27246         contextEl.translate(center, center);
27247         contextEl.rotate(this.rotate * Math.PI / 180);
27248
27249         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27250         
27251         this.canvasEl = document.createElement("canvas");
27252         
27253         this.contextEl = this.canvasEl.getContext("2d");
27254         
27255         switch (this.rotate) {
27256             case 0 :
27257                 
27258                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27259                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27260                 
27261                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27262                 
27263                 break;
27264             case 90 : 
27265                 
27266                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27267                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27268                 
27269                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27270                     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);
27271                     break;
27272                 }
27273                 
27274                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27275                 
27276                 break;
27277             case 180 :
27278                 
27279                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27280                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27281                 
27282                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27283                     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);
27284                     break;
27285                 }
27286                 
27287                 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);
27288                 
27289                 break;
27290             case 270 :
27291                 
27292                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27293                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27294         
27295                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27296                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27297                     break;
27298                 }
27299                 
27300                 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);
27301                 
27302                 break;
27303             default : 
27304                 break;
27305         }
27306         
27307         this.previewEl.appendChild(this.canvasEl);
27308         
27309         this.setCanvasPosition();
27310     },
27311     
27312     crop : function()
27313     {
27314         if(!this.canvasLoaded){
27315             return;
27316         }
27317         
27318         var imageCanvas = document.createElement("canvas");
27319         
27320         var imageContext = imageCanvas.getContext("2d");
27321         
27322         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27323         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27324         
27325         var center = imageCanvas.width / 2;
27326         
27327         imageContext.translate(center, center);
27328         
27329         imageContext.rotate(this.rotate * Math.PI / 180);
27330         
27331         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27332         
27333         var canvas = document.createElement("canvas");
27334         
27335         var context = canvas.getContext("2d");
27336                 
27337         canvas.width = this.minWidth;
27338         canvas.height = this.minHeight;
27339
27340         switch (this.rotate) {
27341             case 0 :
27342                 
27343                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27344                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27345                 
27346                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27347                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27348                 
27349                 var targetWidth = this.minWidth - 2 * x;
27350                 var targetHeight = this.minHeight - 2 * y;
27351                 
27352                 var scale = 1;
27353                 
27354                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27355                     scale = targetWidth / width;
27356                 }
27357                 
27358                 if(x > 0 && y == 0){
27359                     scale = targetHeight / height;
27360                 }
27361                 
27362                 if(x > 0 && y > 0){
27363                     scale = targetWidth / width;
27364                     
27365                     if(width < height){
27366                         scale = targetHeight / height;
27367                     }
27368                 }
27369                 
27370                 context.scale(scale, scale);
27371                 
27372                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27373                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27374
27375                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27376                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27377
27378                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27379                 
27380                 break;
27381             case 90 : 
27382                 
27383                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27384                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27385                 
27386                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27387                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27388                 
27389                 var targetWidth = this.minWidth - 2 * x;
27390                 var targetHeight = this.minHeight - 2 * y;
27391                 
27392                 var scale = 1;
27393                 
27394                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27395                     scale = targetWidth / width;
27396                 }
27397                 
27398                 if(x > 0 && y == 0){
27399                     scale = targetHeight / height;
27400                 }
27401                 
27402                 if(x > 0 && y > 0){
27403                     scale = targetWidth / width;
27404                     
27405                     if(width < height){
27406                         scale = targetHeight / height;
27407                     }
27408                 }
27409                 
27410                 context.scale(scale, scale);
27411                 
27412                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27413                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27414
27415                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27416                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27417                 
27418                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27419                 
27420                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27421                 
27422                 break;
27423             case 180 :
27424                 
27425                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27426                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27427                 
27428                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27429                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27430                 
27431                 var targetWidth = this.minWidth - 2 * x;
27432                 var targetHeight = this.minHeight - 2 * y;
27433                 
27434                 var scale = 1;
27435                 
27436                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27437                     scale = targetWidth / width;
27438                 }
27439                 
27440                 if(x > 0 && y == 0){
27441                     scale = targetHeight / height;
27442                 }
27443                 
27444                 if(x > 0 && y > 0){
27445                     scale = targetWidth / width;
27446                     
27447                     if(width < height){
27448                         scale = targetHeight / height;
27449                     }
27450                 }
27451                 
27452                 context.scale(scale, scale);
27453                 
27454                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27455                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27456
27457                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27458                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27459
27460                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27461                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27462                 
27463                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27464                 
27465                 break;
27466             case 270 :
27467                 
27468                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27469                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27470                 
27471                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27472                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27473                 
27474                 var targetWidth = this.minWidth - 2 * x;
27475                 var targetHeight = this.minHeight - 2 * y;
27476                 
27477                 var scale = 1;
27478                 
27479                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27480                     scale = targetWidth / width;
27481                 }
27482                 
27483                 if(x > 0 && y == 0){
27484                     scale = targetHeight / height;
27485                 }
27486                 
27487                 if(x > 0 && y > 0){
27488                     scale = targetWidth / width;
27489                     
27490                     if(width < height){
27491                         scale = targetHeight / height;
27492                     }
27493                 }
27494                 
27495                 context.scale(scale, scale);
27496                 
27497                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27498                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27499
27500                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27501                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27502                 
27503                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27504                 
27505                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27506                 
27507                 break;
27508             default : 
27509                 break;
27510         }
27511         
27512         this.cropData = canvas.toDataURL(this.cropType);
27513         
27514         if(this.fireEvent('crop', this, this.cropData) !== false){
27515             this.process(this.file, this.cropData);
27516         }
27517         
27518         return;
27519         
27520     },
27521     
27522     setThumbBoxSize : function()
27523     {
27524         var width, height;
27525         
27526         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27527             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27528             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27529             
27530             this.minWidth = width;
27531             this.minHeight = height;
27532             
27533             if(this.rotate == 90 || this.rotate == 270){
27534                 this.minWidth = height;
27535                 this.minHeight = width;
27536             }
27537         }
27538         
27539         height = 300;
27540         width = Math.ceil(this.minWidth * height / this.minHeight);
27541         
27542         if(this.minWidth > this.minHeight){
27543             width = 300;
27544             height = Math.ceil(this.minHeight * width / this.minWidth);
27545         }
27546         
27547         this.thumbEl.setStyle({
27548             width : width + 'px',
27549             height : height + 'px'
27550         });
27551
27552         return;
27553             
27554     },
27555     
27556     setThumbBoxPosition : function()
27557     {
27558         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27559         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27560         
27561         this.thumbEl.setLeft(x);
27562         this.thumbEl.setTop(y);
27563         
27564     },
27565     
27566     baseRotateLevel : function()
27567     {
27568         this.baseRotate = 1;
27569         
27570         if(
27571                 typeof(this.exif) != 'undefined' &&
27572                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27573                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27574         ){
27575             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27576         }
27577         
27578         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27579         
27580     },
27581     
27582     baseScaleLevel : function()
27583     {
27584         var width, height;
27585         
27586         if(this.isDocument){
27587             
27588             if(this.baseRotate == 6 || this.baseRotate == 8){
27589             
27590                 height = this.thumbEl.getHeight();
27591                 this.baseScale = height / this.imageEl.OriginWidth;
27592
27593                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27594                     width = this.thumbEl.getWidth();
27595                     this.baseScale = width / this.imageEl.OriginHeight;
27596                 }
27597
27598                 return;
27599             }
27600
27601             height = this.thumbEl.getHeight();
27602             this.baseScale = height / this.imageEl.OriginHeight;
27603
27604             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27605                 width = this.thumbEl.getWidth();
27606                 this.baseScale = width / this.imageEl.OriginWidth;
27607             }
27608
27609             return;
27610         }
27611         
27612         if(this.baseRotate == 6 || this.baseRotate == 8){
27613             
27614             width = this.thumbEl.getHeight();
27615             this.baseScale = width / this.imageEl.OriginHeight;
27616             
27617             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27618                 height = this.thumbEl.getWidth();
27619                 this.baseScale = height / this.imageEl.OriginHeight;
27620             }
27621             
27622             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27623                 height = this.thumbEl.getWidth();
27624                 this.baseScale = height / this.imageEl.OriginHeight;
27625                 
27626                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27627                     width = this.thumbEl.getHeight();
27628                     this.baseScale = width / this.imageEl.OriginWidth;
27629                 }
27630             }
27631             
27632             return;
27633         }
27634         
27635         width = this.thumbEl.getWidth();
27636         this.baseScale = width / this.imageEl.OriginWidth;
27637         
27638         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27639             height = this.thumbEl.getHeight();
27640             this.baseScale = height / this.imageEl.OriginHeight;
27641         }
27642         
27643         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27644             
27645             height = this.thumbEl.getHeight();
27646             this.baseScale = height / this.imageEl.OriginHeight;
27647             
27648             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27649                 width = this.thumbEl.getWidth();
27650                 this.baseScale = width / this.imageEl.OriginWidth;
27651             }
27652             
27653         }
27654         
27655         return;
27656     },
27657     
27658     getScaleLevel : function()
27659     {
27660         return this.baseScale * Math.pow(1.1, this.scale);
27661     },
27662     
27663     onTouchStart : function(e)
27664     {
27665         if(!this.canvasLoaded){
27666             this.beforeSelectFile(e);
27667             return;
27668         }
27669         
27670         var touches = e.browserEvent.touches;
27671         
27672         if(!touches){
27673             return;
27674         }
27675         
27676         if(touches.length == 1){
27677             this.onMouseDown(e);
27678             return;
27679         }
27680         
27681         if(touches.length != 2){
27682             return;
27683         }
27684         
27685         var coords = [];
27686         
27687         for(var i = 0, finger; finger = touches[i]; i++){
27688             coords.push(finger.pageX, finger.pageY);
27689         }
27690         
27691         var x = Math.pow(coords[0] - coords[2], 2);
27692         var y = Math.pow(coords[1] - coords[3], 2);
27693         
27694         this.startDistance = Math.sqrt(x + y);
27695         
27696         this.startScale = this.scale;
27697         
27698         this.pinching = true;
27699         this.dragable = false;
27700         
27701     },
27702     
27703     onTouchMove : function(e)
27704     {
27705         if(!this.pinching && !this.dragable){
27706             return;
27707         }
27708         
27709         var touches = e.browserEvent.touches;
27710         
27711         if(!touches){
27712             return;
27713         }
27714         
27715         if(this.dragable){
27716             this.onMouseMove(e);
27717             return;
27718         }
27719         
27720         var coords = [];
27721         
27722         for(var i = 0, finger; finger = touches[i]; i++){
27723             coords.push(finger.pageX, finger.pageY);
27724         }
27725         
27726         var x = Math.pow(coords[0] - coords[2], 2);
27727         var y = Math.pow(coords[1] - coords[3], 2);
27728         
27729         this.endDistance = Math.sqrt(x + y);
27730         
27731         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27732         
27733         if(!this.zoomable()){
27734             this.scale = this.startScale;
27735             return;
27736         }
27737         
27738         this.draw();
27739         
27740     },
27741     
27742     onTouchEnd : function(e)
27743     {
27744         this.pinching = false;
27745         this.dragable = false;
27746         
27747     },
27748     
27749     process : function(file, crop)
27750     {
27751         if(this.loadMask){
27752             this.maskEl.mask(this.loadingText);
27753         }
27754         
27755         this.xhr = new XMLHttpRequest();
27756         
27757         file.xhr = this.xhr;
27758
27759         this.xhr.open(this.method, this.url, true);
27760         
27761         var headers = {
27762             "Accept": "application/json",
27763             "Cache-Control": "no-cache",
27764             "X-Requested-With": "XMLHttpRequest"
27765         };
27766         
27767         for (var headerName in headers) {
27768             var headerValue = headers[headerName];
27769             if (headerValue) {
27770                 this.xhr.setRequestHeader(headerName, headerValue);
27771             }
27772         }
27773         
27774         var _this = this;
27775         
27776         this.xhr.onload = function()
27777         {
27778             _this.xhrOnLoad(_this.xhr);
27779         }
27780         
27781         this.xhr.onerror = function()
27782         {
27783             _this.xhrOnError(_this.xhr);
27784         }
27785         
27786         var formData = new FormData();
27787
27788         formData.append('returnHTML', 'NO');
27789         
27790         if(crop){
27791             formData.append('crop', crop);
27792         }
27793         
27794         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27795             formData.append(this.paramName, file, file.name);
27796         }
27797         
27798         if(typeof(file.filename) != 'undefined'){
27799             formData.append('filename', file.filename);
27800         }
27801         
27802         if(typeof(file.mimetype) != 'undefined'){
27803             formData.append('mimetype', file.mimetype);
27804         }
27805         
27806         if(this.fireEvent('arrange', this, formData) != false){
27807             this.xhr.send(formData);
27808         };
27809     },
27810     
27811     xhrOnLoad : function(xhr)
27812     {
27813         if(this.loadMask){
27814             this.maskEl.unmask();
27815         }
27816         
27817         if (xhr.readyState !== 4) {
27818             this.fireEvent('exception', this, xhr);
27819             return;
27820         }
27821
27822         var response = Roo.decode(xhr.responseText);
27823         
27824         if(!response.success){
27825             this.fireEvent('exception', this, xhr);
27826             return;
27827         }
27828         
27829         var response = Roo.decode(xhr.responseText);
27830         
27831         this.fireEvent('upload', this, response);
27832         
27833     },
27834     
27835     xhrOnError : function()
27836     {
27837         if(this.loadMask){
27838             this.maskEl.unmask();
27839         }
27840         
27841         Roo.log('xhr on error');
27842         
27843         var response = Roo.decode(xhr.responseText);
27844           
27845         Roo.log(response);
27846         
27847     },
27848     
27849     prepare : function(file)
27850     {   
27851         if(this.loadMask){
27852             this.maskEl.mask(this.loadingText);
27853         }
27854         
27855         this.file = false;
27856         this.exif = {};
27857         
27858         if(typeof(file) === 'string'){
27859             this.loadCanvas(file);
27860             return;
27861         }
27862         
27863         if(!file || !this.urlAPI){
27864             return;
27865         }
27866         
27867         this.file = file;
27868         this.cropType = file.type;
27869         
27870         var _this = this;
27871         
27872         if(this.fireEvent('prepare', this, this.file) != false){
27873             
27874             var reader = new FileReader();
27875             
27876             reader.onload = function (e) {
27877                 if (e.target.error) {
27878                     Roo.log(e.target.error);
27879                     return;
27880                 }
27881                 
27882                 var buffer = e.target.result,
27883                     dataView = new DataView(buffer),
27884                     offset = 2,
27885                     maxOffset = dataView.byteLength - 4,
27886                     markerBytes,
27887                     markerLength;
27888                 
27889                 if (dataView.getUint16(0) === 0xffd8) {
27890                     while (offset < maxOffset) {
27891                         markerBytes = dataView.getUint16(offset);
27892                         
27893                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27894                             markerLength = dataView.getUint16(offset + 2) + 2;
27895                             if (offset + markerLength > dataView.byteLength) {
27896                                 Roo.log('Invalid meta data: Invalid segment size.');
27897                                 break;
27898                             }
27899                             
27900                             if(markerBytes == 0xffe1){
27901                                 _this.parseExifData(
27902                                     dataView,
27903                                     offset,
27904                                     markerLength
27905                                 );
27906                             }
27907                             
27908                             offset += markerLength;
27909                             
27910                             continue;
27911                         }
27912                         
27913                         break;
27914                     }
27915                     
27916                 }
27917                 
27918                 var url = _this.urlAPI.createObjectURL(_this.file);
27919                 
27920                 _this.loadCanvas(url);
27921                 
27922                 return;
27923             }
27924             
27925             reader.readAsArrayBuffer(this.file);
27926             
27927         }
27928         
27929     },
27930     
27931     parseExifData : function(dataView, offset, length)
27932     {
27933         var tiffOffset = offset + 10,
27934             littleEndian,
27935             dirOffset;
27936     
27937         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27938             // No Exif data, might be XMP data instead
27939             return;
27940         }
27941         
27942         // Check for the ASCII code for "Exif" (0x45786966):
27943         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27944             // No Exif data, might be XMP data instead
27945             return;
27946         }
27947         if (tiffOffset + 8 > dataView.byteLength) {
27948             Roo.log('Invalid Exif data: Invalid segment size.');
27949             return;
27950         }
27951         // Check for the two null bytes:
27952         if (dataView.getUint16(offset + 8) !== 0x0000) {
27953             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27954             return;
27955         }
27956         // Check the byte alignment:
27957         switch (dataView.getUint16(tiffOffset)) {
27958         case 0x4949:
27959             littleEndian = true;
27960             break;
27961         case 0x4D4D:
27962             littleEndian = false;
27963             break;
27964         default:
27965             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27966             return;
27967         }
27968         // Check for the TIFF tag marker (0x002A):
27969         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27970             Roo.log('Invalid Exif data: Missing TIFF marker.');
27971             return;
27972         }
27973         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27974         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27975         
27976         this.parseExifTags(
27977             dataView,
27978             tiffOffset,
27979             tiffOffset + dirOffset,
27980             littleEndian
27981         );
27982     },
27983     
27984     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27985     {
27986         var tagsNumber,
27987             dirEndOffset,
27988             i;
27989         if (dirOffset + 6 > dataView.byteLength) {
27990             Roo.log('Invalid Exif data: Invalid directory offset.');
27991             return;
27992         }
27993         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27994         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27995         if (dirEndOffset + 4 > dataView.byteLength) {
27996             Roo.log('Invalid Exif data: Invalid directory size.');
27997             return;
27998         }
27999         for (i = 0; i < tagsNumber; i += 1) {
28000             this.parseExifTag(
28001                 dataView,
28002                 tiffOffset,
28003                 dirOffset + 2 + 12 * i, // tag offset
28004                 littleEndian
28005             );
28006         }
28007         // Return the offset to the next directory:
28008         return dataView.getUint32(dirEndOffset, littleEndian);
28009     },
28010     
28011     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28012     {
28013         var tag = dataView.getUint16(offset, littleEndian);
28014         
28015         this.exif[tag] = this.getExifValue(
28016             dataView,
28017             tiffOffset,
28018             offset,
28019             dataView.getUint16(offset + 2, littleEndian), // tag type
28020             dataView.getUint32(offset + 4, littleEndian), // tag length
28021             littleEndian
28022         );
28023     },
28024     
28025     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28026     {
28027         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28028             tagSize,
28029             dataOffset,
28030             values,
28031             i,
28032             str,
28033             c;
28034     
28035         if (!tagType) {
28036             Roo.log('Invalid Exif data: Invalid tag type.');
28037             return;
28038         }
28039         
28040         tagSize = tagType.size * length;
28041         // Determine if the value is contained in the dataOffset bytes,
28042         // or if the value at the dataOffset is a pointer to the actual data:
28043         dataOffset = tagSize > 4 ?
28044                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28045         if (dataOffset + tagSize > dataView.byteLength) {
28046             Roo.log('Invalid Exif data: Invalid data offset.');
28047             return;
28048         }
28049         if (length === 1) {
28050             return tagType.getValue(dataView, dataOffset, littleEndian);
28051         }
28052         values = [];
28053         for (i = 0; i < length; i += 1) {
28054             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28055         }
28056         
28057         if (tagType.ascii) {
28058             str = '';
28059             // Concatenate the chars:
28060             for (i = 0; i < values.length; i += 1) {
28061                 c = values[i];
28062                 // Ignore the terminating NULL byte(s):
28063                 if (c === '\u0000') {
28064                     break;
28065                 }
28066                 str += c;
28067             }
28068             return str;
28069         }
28070         return values;
28071     }
28072     
28073 });
28074
28075 Roo.apply(Roo.bootstrap.UploadCropbox, {
28076     tags : {
28077         'Orientation': 0x0112
28078     },
28079     
28080     Orientation: {
28081             1: 0, //'top-left',
28082 //            2: 'top-right',
28083             3: 180, //'bottom-right',
28084 //            4: 'bottom-left',
28085 //            5: 'left-top',
28086             6: 90, //'right-top',
28087 //            7: 'right-bottom',
28088             8: 270 //'left-bottom'
28089     },
28090     
28091     exifTagTypes : {
28092         // byte, 8-bit unsigned int:
28093         1: {
28094             getValue: function (dataView, dataOffset) {
28095                 return dataView.getUint8(dataOffset);
28096             },
28097             size: 1
28098         },
28099         // ascii, 8-bit byte:
28100         2: {
28101             getValue: function (dataView, dataOffset) {
28102                 return String.fromCharCode(dataView.getUint8(dataOffset));
28103             },
28104             size: 1,
28105             ascii: true
28106         },
28107         // short, 16 bit int:
28108         3: {
28109             getValue: function (dataView, dataOffset, littleEndian) {
28110                 return dataView.getUint16(dataOffset, littleEndian);
28111             },
28112             size: 2
28113         },
28114         // long, 32 bit int:
28115         4: {
28116             getValue: function (dataView, dataOffset, littleEndian) {
28117                 return dataView.getUint32(dataOffset, littleEndian);
28118             },
28119             size: 4
28120         },
28121         // rational = two long values, first is numerator, second is denominator:
28122         5: {
28123             getValue: function (dataView, dataOffset, littleEndian) {
28124                 return dataView.getUint32(dataOffset, littleEndian) /
28125                     dataView.getUint32(dataOffset + 4, littleEndian);
28126             },
28127             size: 8
28128         },
28129         // slong, 32 bit signed int:
28130         9: {
28131             getValue: function (dataView, dataOffset, littleEndian) {
28132                 return dataView.getInt32(dataOffset, littleEndian);
28133             },
28134             size: 4
28135         },
28136         // srational, two slongs, first is numerator, second is denominator:
28137         10: {
28138             getValue: function (dataView, dataOffset, littleEndian) {
28139                 return dataView.getInt32(dataOffset, littleEndian) /
28140                     dataView.getInt32(dataOffset + 4, littleEndian);
28141             },
28142             size: 8
28143         }
28144     },
28145     
28146     footer : {
28147         STANDARD : [
28148             {
28149                 tag : 'div',
28150                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28151                 action : 'rotate-left',
28152                 cn : [
28153                     {
28154                         tag : 'button',
28155                         cls : 'btn btn-default',
28156                         html : '<i class="fa fa-undo"></i>'
28157                     }
28158                 ]
28159             },
28160             {
28161                 tag : 'div',
28162                 cls : 'btn-group roo-upload-cropbox-picture',
28163                 action : 'picture',
28164                 cn : [
28165                     {
28166                         tag : 'button',
28167                         cls : 'btn btn-default',
28168                         html : '<i class="fa fa-picture-o"></i>'
28169                     }
28170                 ]
28171             },
28172             {
28173                 tag : 'div',
28174                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28175                 action : 'rotate-right',
28176                 cn : [
28177                     {
28178                         tag : 'button',
28179                         cls : 'btn btn-default',
28180                         html : '<i class="fa fa-repeat"></i>'
28181                     }
28182                 ]
28183             }
28184         ],
28185         DOCUMENT : [
28186             {
28187                 tag : 'div',
28188                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28189                 action : 'rotate-left',
28190                 cn : [
28191                     {
28192                         tag : 'button',
28193                         cls : 'btn btn-default',
28194                         html : '<i class="fa fa-undo"></i>'
28195                     }
28196                 ]
28197             },
28198             {
28199                 tag : 'div',
28200                 cls : 'btn-group roo-upload-cropbox-download',
28201                 action : 'download',
28202                 cn : [
28203                     {
28204                         tag : 'button',
28205                         cls : 'btn btn-default',
28206                         html : '<i class="fa fa-download"></i>'
28207                     }
28208                 ]
28209             },
28210             {
28211                 tag : 'div',
28212                 cls : 'btn-group roo-upload-cropbox-crop',
28213                 action : 'crop',
28214                 cn : [
28215                     {
28216                         tag : 'button',
28217                         cls : 'btn btn-default',
28218                         html : '<i class="fa fa-crop"></i>'
28219                     }
28220                 ]
28221             },
28222             {
28223                 tag : 'div',
28224                 cls : 'btn-group roo-upload-cropbox-trash',
28225                 action : 'trash',
28226                 cn : [
28227                     {
28228                         tag : 'button',
28229                         cls : 'btn btn-default',
28230                         html : '<i class="fa fa-trash"></i>'
28231                     }
28232                 ]
28233             },
28234             {
28235                 tag : 'div',
28236                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28237                 action : 'rotate-right',
28238                 cn : [
28239                     {
28240                         tag : 'button',
28241                         cls : 'btn btn-default',
28242                         html : '<i class="fa fa-repeat"></i>'
28243                     }
28244                 ]
28245             }
28246         ],
28247         ROTATOR : [
28248             {
28249                 tag : 'div',
28250                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28251                 action : 'rotate-left',
28252                 cn : [
28253                     {
28254                         tag : 'button',
28255                         cls : 'btn btn-default',
28256                         html : '<i class="fa fa-undo"></i>'
28257                     }
28258                 ]
28259             },
28260             {
28261                 tag : 'div',
28262                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28263                 action : 'rotate-right',
28264                 cn : [
28265                     {
28266                         tag : 'button',
28267                         cls : 'btn btn-default',
28268                         html : '<i class="fa fa-repeat"></i>'
28269                     }
28270                 ]
28271             }
28272         ]
28273     }
28274 });
28275
28276 /*
28277 * Licence: LGPL
28278 */
28279
28280 /**
28281  * @class Roo.bootstrap.DocumentManager
28282  * @extends Roo.bootstrap.Component
28283  * Bootstrap DocumentManager class
28284  * @cfg {String} paramName default 'imageUpload'
28285  * @cfg {String} toolTipName default 'filename'
28286  * @cfg {String} method default POST
28287  * @cfg {String} url action url
28288  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28289  * @cfg {Boolean} multiple multiple upload default true
28290  * @cfg {Number} thumbSize default 300
28291  * @cfg {String} fieldLabel
28292  * @cfg {Number} labelWidth default 4
28293  * @cfg {String} labelAlign (left|top) default left
28294  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28295 * @cfg {Number} labellg set the width of label (1-12)
28296  * @cfg {Number} labelmd set the width of label (1-12)
28297  * @cfg {Number} labelsm set the width of label (1-12)
28298  * @cfg {Number} labelxs set the width of label (1-12)
28299  * 
28300  * @constructor
28301  * Create a new DocumentManager
28302  * @param {Object} config The config object
28303  */
28304
28305 Roo.bootstrap.DocumentManager = function(config){
28306     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28307     
28308     this.files = [];
28309     this.delegates = [];
28310     
28311     this.addEvents({
28312         /**
28313          * @event initial
28314          * Fire when initial the DocumentManager
28315          * @param {Roo.bootstrap.DocumentManager} this
28316          */
28317         "initial" : true,
28318         /**
28319          * @event inspect
28320          * inspect selected file
28321          * @param {Roo.bootstrap.DocumentManager} this
28322          * @param {File} file
28323          */
28324         "inspect" : true,
28325         /**
28326          * @event exception
28327          * Fire when xhr load exception
28328          * @param {Roo.bootstrap.DocumentManager} this
28329          * @param {XMLHttpRequest} xhr
28330          */
28331         "exception" : true,
28332         /**
28333          * @event afterupload
28334          * Fire when xhr load exception
28335          * @param {Roo.bootstrap.DocumentManager} this
28336          * @param {XMLHttpRequest} xhr
28337          */
28338         "afterupload" : true,
28339         /**
28340          * @event prepare
28341          * prepare the form data
28342          * @param {Roo.bootstrap.DocumentManager} this
28343          * @param {Object} formData
28344          */
28345         "prepare" : true,
28346         /**
28347          * @event remove
28348          * Fire when remove the file
28349          * @param {Roo.bootstrap.DocumentManager} this
28350          * @param {Object} file
28351          */
28352         "remove" : true,
28353         /**
28354          * @event refresh
28355          * Fire after refresh the file
28356          * @param {Roo.bootstrap.DocumentManager} this
28357          */
28358         "refresh" : true,
28359         /**
28360          * @event click
28361          * Fire after click the image
28362          * @param {Roo.bootstrap.DocumentManager} this
28363          * @param {Object} file
28364          */
28365         "click" : true,
28366         /**
28367          * @event edit
28368          * Fire when upload a image and editable set to true
28369          * @param {Roo.bootstrap.DocumentManager} this
28370          * @param {Object} file
28371          */
28372         "edit" : true,
28373         /**
28374          * @event beforeselectfile
28375          * Fire before select file
28376          * @param {Roo.bootstrap.DocumentManager} this
28377          */
28378         "beforeselectfile" : true,
28379         /**
28380          * @event process
28381          * Fire before process file
28382          * @param {Roo.bootstrap.DocumentManager} this
28383          * @param {Object} file
28384          */
28385         "process" : true
28386         
28387     });
28388 };
28389
28390 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28391     
28392     boxes : 0,
28393     inputName : '',
28394     thumbSize : 300,
28395     multiple : true,
28396     files : false,
28397     method : 'POST',
28398     url : '',
28399     paramName : 'imageUpload',
28400     toolTipName : 'filename',
28401     fieldLabel : '',
28402     labelWidth : 4,
28403     labelAlign : 'left',
28404     editable : true,
28405     delegates : false,
28406     xhr : false, 
28407     
28408     labellg : 0,
28409     labelmd : 0,
28410     labelsm : 0,
28411     labelxs : 0,
28412     
28413     getAutoCreate : function()
28414     {   
28415         var managerWidget = {
28416             tag : 'div',
28417             cls : 'roo-document-manager',
28418             cn : [
28419                 {
28420                     tag : 'input',
28421                     cls : 'roo-document-manager-selector',
28422                     type : 'file'
28423                 },
28424                 {
28425                     tag : 'div',
28426                     cls : 'roo-document-manager-uploader',
28427                     cn : [
28428                         {
28429                             tag : 'div',
28430                             cls : 'roo-document-manager-upload-btn',
28431                             html : '<i class="fa fa-plus"></i>'
28432                         }
28433                     ]
28434                     
28435                 }
28436             ]
28437         };
28438         
28439         var content = [
28440             {
28441                 tag : 'div',
28442                 cls : 'column col-md-12',
28443                 cn : managerWidget
28444             }
28445         ];
28446         
28447         if(this.fieldLabel.length){
28448             
28449             content = [
28450                 {
28451                     tag : 'div',
28452                     cls : 'column col-md-12',
28453                     html : this.fieldLabel
28454                 },
28455                 {
28456                     tag : 'div',
28457                     cls : 'column col-md-12',
28458                     cn : managerWidget
28459                 }
28460             ];
28461
28462             if(this.labelAlign == 'left'){
28463                 content = [
28464                     {
28465                         tag : 'div',
28466                         cls : 'column',
28467                         html : this.fieldLabel
28468                     },
28469                     {
28470                         tag : 'div',
28471                         cls : 'column',
28472                         cn : managerWidget
28473                     }
28474                 ];
28475                 
28476                 if(this.labelWidth > 12){
28477                     content[0].style = "width: " + this.labelWidth + 'px';
28478                 }
28479
28480                 if(this.labelWidth < 13 && this.labelmd == 0){
28481                     this.labelmd = this.labelWidth;
28482                 }
28483
28484                 if(this.labellg > 0){
28485                     content[0].cls += ' col-lg-' + this.labellg;
28486                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28487                 }
28488
28489                 if(this.labelmd > 0){
28490                     content[0].cls += ' col-md-' + this.labelmd;
28491                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28492                 }
28493
28494                 if(this.labelsm > 0){
28495                     content[0].cls += ' col-sm-' + this.labelsm;
28496                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28497                 }
28498
28499                 if(this.labelxs > 0){
28500                     content[0].cls += ' col-xs-' + this.labelxs;
28501                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28502                 }
28503                 
28504             }
28505         }
28506         
28507         var cfg = {
28508             tag : 'div',
28509             cls : 'row clearfix',
28510             cn : content
28511         };
28512         
28513         return cfg;
28514         
28515     },
28516     
28517     initEvents : function()
28518     {
28519         this.managerEl = this.el.select('.roo-document-manager', true).first();
28520         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28521         
28522         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28523         this.selectorEl.hide();
28524         
28525         if(this.multiple){
28526             this.selectorEl.attr('multiple', 'multiple');
28527         }
28528         
28529         this.selectorEl.on('change', this.onFileSelected, this);
28530         
28531         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28532         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28533         
28534         this.uploader.on('click', this.onUploaderClick, this);
28535         
28536         this.renderProgressDialog();
28537         
28538         var _this = this;
28539         
28540         window.addEventListener("resize", function() { _this.refresh(); } );
28541         
28542         this.fireEvent('initial', this);
28543     },
28544     
28545     renderProgressDialog : function()
28546     {
28547         var _this = this;
28548         
28549         this.progressDialog = new Roo.bootstrap.Modal({
28550             cls : 'roo-document-manager-progress-dialog',
28551             allow_close : false,
28552             title : '',
28553             buttons : [
28554                 {
28555                     name  :'cancel',
28556                     weight : 'danger',
28557                     html : 'Cancel'
28558                 }
28559             ], 
28560             listeners : { 
28561                 btnclick : function() {
28562                     _this.uploadCancel();
28563                     this.hide();
28564                 }
28565             }
28566         });
28567          
28568         this.progressDialog.render(Roo.get(document.body));
28569          
28570         this.progress = new Roo.bootstrap.Progress({
28571             cls : 'roo-document-manager-progress',
28572             active : true,
28573             striped : true
28574         });
28575         
28576         this.progress.render(this.progressDialog.getChildContainer());
28577         
28578         this.progressBar = new Roo.bootstrap.ProgressBar({
28579             cls : 'roo-document-manager-progress-bar',
28580             aria_valuenow : 0,
28581             aria_valuemin : 0,
28582             aria_valuemax : 12,
28583             panel : 'success'
28584         });
28585         
28586         this.progressBar.render(this.progress.getChildContainer());
28587     },
28588     
28589     onUploaderClick : function(e)
28590     {
28591         e.preventDefault();
28592      
28593         if(this.fireEvent('beforeselectfile', this) != false){
28594             this.selectorEl.dom.click();
28595         }
28596         
28597     },
28598     
28599     onFileSelected : function(e)
28600     {
28601         e.preventDefault();
28602         
28603         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28604             return;
28605         }
28606         
28607         Roo.each(this.selectorEl.dom.files, function(file){
28608             if(this.fireEvent('inspect', this, file) != false){
28609                 this.files.push(file);
28610             }
28611         }, this);
28612         
28613         this.queue();
28614         
28615     },
28616     
28617     queue : function()
28618     {
28619         this.selectorEl.dom.value = '';
28620         
28621         if(!this.files.length){
28622             return;
28623         }
28624         
28625         if(this.boxes > 0 && this.files.length > this.boxes){
28626             this.files = this.files.slice(0, this.boxes);
28627         }
28628         
28629         this.uploader.show();
28630         
28631         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28632             this.uploader.hide();
28633         }
28634         
28635         var _this = this;
28636         
28637         var files = [];
28638         
28639         var docs = [];
28640         
28641         Roo.each(this.files, function(file){
28642             
28643             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28644                 var f = this.renderPreview(file);
28645                 files.push(f);
28646                 return;
28647             }
28648             
28649             if(file.type.indexOf('image') != -1){
28650                 this.delegates.push(
28651                     (function(){
28652                         _this.process(file);
28653                     }).createDelegate(this)
28654                 );
28655         
28656                 return;
28657             }
28658             
28659             docs.push(
28660                 (function(){
28661                     _this.process(file);
28662                 }).createDelegate(this)
28663             );
28664             
28665         }, this);
28666         
28667         this.files = files;
28668         
28669         this.delegates = this.delegates.concat(docs);
28670         
28671         if(!this.delegates.length){
28672             this.refresh();
28673             return;
28674         }
28675         
28676         this.progressBar.aria_valuemax = this.delegates.length;
28677         
28678         this.arrange();
28679         
28680         return;
28681     },
28682     
28683     arrange : function()
28684     {
28685         if(!this.delegates.length){
28686             this.progressDialog.hide();
28687             this.refresh();
28688             return;
28689         }
28690         
28691         var delegate = this.delegates.shift();
28692         
28693         this.progressDialog.show();
28694         
28695         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28696         
28697         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28698         
28699         delegate();
28700     },
28701     
28702     refresh : function()
28703     {
28704         this.uploader.show();
28705         
28706         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28707             this.uploader.hide();
28708         }
28709         
28710         Roo.isTouch ? this.closable(false) : this.closable(true);
28711         
28712         this.fireEvent('refresh', this);
28713     },
28714     
28715     onRemove : function(e, el, o)
28716     {
28717         e.preventDefault();
28718         
28719         this.fireEvent('remove', this, o);
28720         
28721     },
28722     
28723     remove : function(o)
28724     {
28725         var files = [];
28726         
28727         Roo.each(this.files, function(file){
28728             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28729                 files.push(file);
28730                 return;
28731             }
28732
28733             o.target.remove();
28734
28735         }, this);
28736         
28737         this.files = files;
28738         
28739         this.refresh();
28740     },
28741     
28742     clear : function()
28743     {
28744         Roo.each(this.files, function(file){
28745             if(!file.target){
28746                 return;
28747             }
28748             
28749             file.target.remove();
28750
28751         }, this);
28752         
28753         this.files = [];
28754         
28755         this.refresh();
28756     },
28757     
28758     onClick : function(e, el, o)
28759     {
28760         e.preventDefault();
28761         
28762         this.fireEvent('click', this, o);
28763         
28764     },
28765     
28766     closable : function(closable)
28767     {
28768         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28769             
28770             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28771             
28772             if(closable){
28773                 el.show();
28774                 return;
28775             }
28776             
28777             el.hide();
28778             
28779         }, this);
28780     },
28781     
28782     xhrOnLoad : function(xhr)
28783     {
28784         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28785             el.remove();
28786         }, this);
28787         
28788         if (xhr.readyState !== 4) {
28789             this.arrange();
28790             this.fireEvent('exception', this, xhr);
28791             return;
28792         }
28793
28794         var response = Roo.decode(xhr.responseText);
28795         
28796         if(!response.success){
28797             this.arrange();
28798             this.fireEvent('exception', this, xhr);
28799             return;
28800         }
28801         
28802         var file = this.renderPreview(response.data);
28803         
28804         this.files.push(file);
28805         
28806         this.arrange();
28807         
28808         this.fireEvent('afterupload', this, xhr);
28809         
28810     },
28811     
28812     xhrOnError : function(xhr)
28813     {
28814         Roo.log('xhr on error');
28815         
28816         var response = Roo.decode(xhr.responseText);
28817           
28818         Roo.log(response);
28819         
28820         this.arrange();
28821     },
28822     
28823     process : function(file)
28824     {
28825         if(this.fireEvent('process', this, file) !== false){
28826             if(this.editable && file.type.indexOf('image') != -1){
28827                 this.fireEvent('edit', this, file);
28828                 return;
28829             }
28830
28831             this.uploadStart(file, false);
28832
28833             return;
28834         }
28835         
28836     },
28837     
28838     uploadStart : function(file, crop)
28839     {
28840         this.xhr = new XMLHttpRequest();
28841         
28842         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28843             this.arrange();
28844             return;
28845         }
28846         
28847         file.xhr = this.xhr;
28848             
28849         this.managerEl.createChild({
28850             tag : 'div',
28851             cls : 'roo-document-manager-loading',
28852             cn : [
28853                 {
28854                     tag : 'div',
28855                     tooltip : file.name,
28856                     cls : 'roo-document-manager-thumb',
28857                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28858                 }
28859             ]
28860
28861         });
28862
28863         this.xhr.open(this.method, this.url, true);
28864         
28865         var headers = {
28866             "Accept": "application/json",
28867             "Cache-Control": "no-cache",
28868             "X-Requested-With": "XMLHttpRequest"
28869         };
28870         
28871         for (var headerName in headers) {
28872             var headerValue = headers[headerName];
28873             if (headerValue) {
28874                 this.xhr.setRequestHeader(headerName, headerValue);
28875             }
28876         }
28877         
28878         var _this = this;
28879         
28880         this.xhr.onload = function()
28881         {
28882             _this.xhrOnLoad(_this.xhr);
28883         }
28884         
28885         this.xhr.onerror = function()
28886         {
28887             _this.xhrOnError(_this.xhr);
28888         }
28889         
28890         var formData = new FormData();
28891
28892         formData.append('returnHTML', 'NO');
28893         
28894         if(crop){
28895             formData.append('crop', crop);
28896         }
28897         
28898         formData.append(this.paramName, file, file.name);
28899         
28900         var options = {
28901             file : file, 
28902             manually : false
28903         };
28904         
28905         if(this.fireEvent('prepare', this, formData, options) != false){
28906             
28907             if(options.manually){
28908                 return;
28909             }
28910             
28911             this.xhr.send(formData);
28912             return;
28913         };
28914         
28915         this.uploadCancel();
28916     },
28917     
28918     uploadCancel : function()
28919     {
28920         if (this.xhr) {
28921             this.xhr.abort();
28922         }
28923         
28924         this.delegates = [];
28925         
28926         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28927             el.remove();
28928         }, this);
28929         
28930         this.arrange();
28931     },
28932     
28933     renderPreview : function(file)
28934     {
28935         if(typeof(file.target) != 'undefined' && file.target){
28936             return file;
28937         }
28938         
28939         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28940         
28941         var previewEl = this.managerEl.createChild({
28942             tag : 'div',
28943             cls : 'roo-document-manager-preview',
28944             cn : [
28945                 {
28946                     tag : 'div',
28947                     tooltip : file[this.toolTipName],
28948                     cls : 'roo-document-manager-thumb',
28949                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28950                 },
28951                 {
28952                     tag : 'button',
28953                     cls : 'close',
28954                     html : '<i class="fa fa-times-circle"></i>'
28955                 }
28956             ]
28957         });
28958
28959         var close = previewEl.select('button.close', true).first();
28960
28961         close.on('click', this.onRemove, this, file);
28962
28963         file.target = previewEl;
28964
28965         var image = previewEl.select('img', true).first();
28966         
28967         var _this = this;
28968         
28969         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28970         
28971         image.on('click', this.onClick, this, file);
28972         
28973         return file;
28974         
28975     },
28976     
28977     onPreviewLoad : function(file, image)
28978     {
28979         if(typeof(file.target) == 'undefined' || !file.target){
28980             return;
28981         }
28982         
28983         var width = image.dom.naturalWidth || image.dom.width;
28984         var height = image.dom.naturalHeight || image.dom.height;
28985         
28986         if(width > height){
28987             file.target.addClass('wide');
28988             return;
28989         }
28990         
28991         file.target.addClass('tall');
28992         return;
28993         
28994     },
28995     
28996     uploadFromSource : function(file, crop)
28997     {
28998         this.xhr = new XMLHttpRequest();
28999         
29000         this.managerEl.createChild({
29001             tag : 'div',
29002             cls : 'roo-document-manager-loading',
29003             cn : [
29004                 {
29005                     tag : 'div',
29006                     tooltip : file.name,
29007                     cls : 'roo-document-manager-thumb',
29008                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29009                 }
29010             ]
29011
29012         });
29013
29014         this.xhr.open(this.method, this.url, true);
29015         
29016         var headers = {
29017             "Accept": "application/json",
29018             "Cache-Control": "no-cache",
29019             "X-Requested-With": "XMLHttpRequest"
29020         };
29021         
29022         for (var headerName in headers) {
29023             var headerValue = headers[headerName];
29024             if (headerValue) {
29025                 this.xhr.setRequestHeader(headerName, headerValue);
29026             }
29027         }
29028         
29029         var _this = this;
29030         
29031         this.xhr.onload = function()
29032         {
29033             _this.xhrOnLoad(_this.xhr);
29034         }
29035         
29036         this.xhr.onerror = function()
29037         {
29038             _this.xhrOnError(_this.xhr);
29039         }
29040         
29041         var formData = new FormData();
29042
29043         formData.append('returnHTML', 'NO');
29044         
29045         formData.append('crop', crop);
29046         
29047         if(typeof(file.filename) != 'undefined'){
29048             formData.append('filename', file.filename);
29049         }
29050         
29051         if(typeof(file.mimetype) != 'undefined'){
29052             formData.append('mimetype', file.mimetype);
29053         }
29054         
29055         Roo.log(formData);
29056         
29057         if(this.fireEvent('prepare', this, formData) != false){
29058             this.xhr.send(formData);
29059         };
29060     }
29061 });
29062
29063 /*
29064 * Licence: LGPL
29065 */
29066
29067 /**
29068  * @class Roo.bootstrap.DocumentViewer
29069  * @extends Roo.bootstrap.Component
29070  * Bootstrap DocumentViewer class
29071  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29072  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29073  * 
29074  * @constructor
29075  * Create a new DocumentViewer
29076  * @param {Object} config The config object
29077  */
29078
29079 Roo.bootstrap.DocumentViewer = function(config){
29080     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29081     
29082     this.addEvents({
29083         /**
29084          * @event initial
29085          * Fire after initEvent
29086          * @param {Roo.bootstrap.DocumentViewer} this
29087          */
29088         "initial" : true,
29089         /**
29090          * @event click
29091          * Fire after click
29092          * @param {Roo.bootstrap.DocumentViewer} this
29093          */
29094         "click" : true,
29095         /**
29096          * @event download
29097          * Fire after download button
29098          * @param {Roo.bootstrap.DocumentViewer} this
29099          */
29100         "download" : true,
29101         /**
29102          * @event trash
29103          * Fire after trash button
29104          * @param {Roo.bootstrap.DocumentViewer} this
29105          */
29106         "trash" : true
29107         
29108     });
29109 };
29110
29111 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29112     
29113     showDownload : true,
29114     
29115     showTrash : true,
29116     
29117     getAutoCreate : function()
29118     {
29119         var cfg = {
29120             tag : 'div',
29121             cls : 'roo-document-viewer',
29122             cn : [
29123                 {
29124                     tag : 'div',
29125                     cls : 'roo-document-viewer-body',
29126                     cn : [
29127                         {
29128                             tag : 'div',
29129                             cls : 'roo-document-viewer-thumb',
29130                             cn : [
29131                                 {
29132                                     tag : 'img',
29133                                     cls : 'roo-document-viewer-image'
29134                                 }
29135                             ]
29136                         }
29137                     ]
29138                 },
29139                 {
29140                     tag : 'div',
29141                     cls : 'roo-document-viewer-footer',
29142                     cn : {
29143                         tag : 'div',
29144                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29145                         cn : [
29146                             {
29147                                 tag : 'div',
29148                                 cls : 'btn-group roo-document-viewer-download',
29149                                 cn : [
29150                                     {
29151                                         tag : 'button',
29152                                         cls : 'btn btn-default',
29153                                         html : '<i class="fa fa-download"></i>'
29154                                     }
29155                                 ]
29156                             },
29157                             {
29158                                 tag : 'div',
29159                                 cls : 'btn-group roo-document-viewer-trash',
29160                                 cn : [
29161                                     {
29162                                         tag : 'button',
29163                                         cls : 'btn btn-default',
29164                                         html : '<i class="fa fa-trash"></i>'
29165                                     }
29166                                 ]
29167                             }
29168                         ]
29169                     }
29170                 }
29171             ]
29172         };
29173         
29174         return cfg;
29175     },
29176     
29177     initEvents : function()
29178     {
29179         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29180         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29181         
29182         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29183         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29184         
29185         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29186         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29187         
29188         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29189         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29190         
29191         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29192         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29193         
29194         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29195         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29196         
29197         this.bodyEl.on('click', this.onClick, this);
29198         this.downloadBtn.on('click', this.onDownload, this);
29199         this.trashBtn.on('click', this.onTrash, this);
29200         
29201         this.downloadBtn.hide();
29202         this.trashBtn.hide();
29203         
29204         if(this.showDownload){
29205             this.downloadBtn.show();
29206         }
29207         
29208         if(this.showTrash){
29209             this.trashBtn.show();
29210         }
29211         
29212         if(!this.showDownload && !this.showTrash) {
29213             this.footerEl.hide();
29214         }
29215         
29216     },
29217     
29218     initial : function()
29219     {
29220         this.fireEvent('initial', this);
29221         
29222     },
29223     
29224     onClick : function(e)
29225     {
29226         e.preventDefault();
29227         
29228         this.fireEvent('click', this);
29229     },
29230     
29231     onDownload : function(e)
29232     {
29233         e.preventDefault();
29234         
29235         this.fireEvent('download', this);
29236     },
29237     
29238     onTrash : function(e)
29239     {
29240         e.preventDefault();
29241         
29242         this.fireEvent('trash', this);
29243     }
29244     
29245 });
29246 /*
29247  * - LGPL
29248  *
29249  * nav progress bar
29250  * 
29251  */
29252
29253 /**
29254  * @class Roo.bootstrap.NavProgressBar
29255  * @extends Roo.bootstrap.Component
29256  * Bootstrap NavProgressBar class
29257  * 
29258  * @constructor
29259  * Create a new nav progress bar
29260  * @param {Object} config The config object
29261  */
29262
29263 Roo.bootstrap.NavProgressBar = function(config){
29264     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29265
29266     this.bullets = this.bullets || [];
29267    
29268 //    Roo.bootstrap.NavProgressBar.register(this);
29269      this.addEvents({
29270         /**
29271              * @event changed
29272              * Fires when the active item changes
29273              * @param {Roo.bootstrap.NavProgressBar} this
29274              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29275              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29276          */
29277         'changed': true
29278      });
29279     
29280 };
29281
29282 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29283     
29284     bullets : [],
29285     barItems : [],
29286     
29287     getAutoCreate : function()
29288     {
29289         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29290         
29291         cfg = {
29292             tag : 'div',
29293             cls : 'roo-navigation-bar-group',
29294             cn : [
29295                 {
29296                     tag : 'div',
29297                     cls : 'roo-navigation-top-bar'
29298                 },
29299                 {
29300                     tag : 'div',
29301                     cls : 'roo-navigation-bullets-bar',
29302                     cn : [
29303                         {
29304                             tag : 'ul',
29305                             cls : 'roo-navigation-bar'
29306                         }
29307                     ]
29308                 },
29309                 
29310                 {
29311                     tag : 'div',
29312                     cls : 'roo-navigation-bottom-bar'
29313                 }
29314             ]
29315             
29316         };
29317         
29318         return cfg;
29319         
29320     },
29321     
29322     initEvents: function() 
29323     {
29324         
29325     },
29326     
29327     onRender : function(ct, position) 
29328     {
29329         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29330         
29331         if(this.bullets.length){
29332             Roo.each(this.bullets, function(b){
29333                this.addItem(b);
29334             }, this);
29335         }
29336         
29337         this.format();
29338         
29339     },
29340     
29341     addItem : function(cfg)
29342     {
29343         var item = new Roo.bootstrap.NavProgressItem(cfg);
29344         
29345         item.parentId = this.id;
29346         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29347         
29348         if(cfg.html){
29349             var top = new Roo.bootstrap.Element({
29350                 tag : 'div',
29351                 cls : 'roo-navigation-bar-text'
29352             });
29353             
29354             var bottom = new Roo.bootstrap.Element({
29355                 tag : 'div',
29356                 cls : 'roo-navigation-bar-text'
29357             });
29358             
29359             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29360             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29361             
29362             var topText = new Roo.bootstrap.Element({
29363                 tag : 'span',
29364                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29365             });
29366             
29367             var bottomText = new Roo.bootstrap.Element({
29368                 tag : 'span',
29369                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29370             });
29371             
29372             topText.onRender(top.el, null);
29373             bottomText.onRender(bottom.el, null);
29374             
29375             item.topEl = top;
29376             item.bottomEl = bottom;
29377         }
29378         
29379         this.barItems.push(item);
29380         
29381         return item;
29382     },
29383     
29384     getActive : function()
29385     {
29386         var active = false;
29387         
29388         Roo.each(this.barItems, function(v){
29389             
29390             if (!v.isActive()) {
29391                 return;
29392             }
29393             
29394             active = v;
29395             return false;
29396             
29397         });
29398         
29399         return active;
29400     },
29401     
29402     setActiveItem : function(item)
29403     {
29404         var prev = false;
29405         
29406         Roo.each(this.barItems, function(v){
29407             if (v.rid == item.rid) {
29408                 return ;
29409             }
29410             
29411             if (v.isActive()) {
29412                 v.setActive(false);
29413                 prev = v;
29414             }
29415         });
29416
29417         item.setActive(true);
29418         
29419         this.fireEvent('changed', this, item, prev);
29420     },
29421     
29422     getBarItem: function(rid)
29423     {
29424         var ret = false;
29425         
29426         Roo.each(this.barItems, function(e) {
29427             if (e.rid != rid) {
29428                 return;
29429             }
29430             
29431             ret =  e;
29432             return false;
29433         });
29434         
29435         return ret;
29436     },
29437     
29438     indexOfItem : function(item)
29439     {
29440         var index = false;
29441         
29442         Roo.each(this.barItems, function(v, i){
29443             
29444             if (v.rid != item.rid) {
29445                 return;
29446             }
29447             
29448             index = i;
29449             return false
29450         });
29451         
29452         return index;
29453     },
29454     
29455     setActiveNext : function()
29456     {
29457         var i = this.indexOfItem(this.getActive());
29458         
29459         if (i > this.barItems.length) {
29460             return;
29461         }
29462         
29463         this.setActiveItem(this.barItems[i+1]);
29464     },
29465     
29466     setActivePrev : function()
29467     {
29468         var i = this.indexOfItem(this.getActive());
29469         
29470         if (i  < 1) {
29471             return;
29472         }
29473         
29474         this.setActiveItem(this.barItems[i-1]);
29475     },
29476     
29477     format : function()
29478     {
29479         if(!this.barItems.length){
29480             return;
29481         }
29482      
29483         var width = 100 / this.barItems.length;
29484         
29485         Roo.each(this.barItems, function(i){
29486             i.el.setStyle('width', width + '%');
29487             i.topEl.el.setStyle('width', width + '%');
29488             i.bottomEl.el.setStyle('width', width + '%');
29489         }, this);
29490         
29491     }
29492     
29493 });
29494 /*
29495  * - LGPL
29496  *
29497  * Nav Progress Item
29498  * 
29499  */
29500
29501 /**
29502  * @class Roo.bootstrap.NavProgressItem
29503  * @extends Roo.bootstrap.Component
29504  * Bootstrap NavProgressItem class
29505  * @cfg {String} rid the reference id
29506  * @cfg {Boolean} active (true|false) Is item active default false
29507  * @cfg {Boolean} disabled (true|false) Is item active default false
29508  * @cfg {String} html
29509  * @cfg {String} position (top|bottom) text position default bottom
29510  * @cfg {String} icon show icon instead of number
29511  * 
29512  * @constructor
29513  * Create a new NavProgressItem
29514  * @param {Object} config The config object
29515  */
29516 Roo.bootstrap.NavProgressItem = function(config){
29517     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29518     this.addEvents({
29519         // raw events
29520         /**
29521          * @event click
29522          * The raw click event for the entire grid.
29523          * @param {Roo.bootstrap.NavProgressItem} this
29524          * @param {Roo.EventObject} e
29525          */
29526         "click" : true
29527     });
29528    
29529 };
29530
29531 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29532     
29533     rid : '',
29534     active : false,
29535     disabled : false,
29536     html : '',
29537     position : 'bottom',
29538     icon : false,
29539     
29540     getAutoCreate : function()
29541     {
29542         var iconCls = 'roo-navigation-bar-item-icon';
29543         
29544         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29545         
29546         var cfg = {
29547             tag: 'li',
29548             cls: 'roo-navigation-bar-item',
29549             cn : [
29550                 {
29551                     tag : 'i',
29552                     cls : iconCls
29553                 }
29554             ]
29555         };
29556         
29557         if(this.active){
29558             cfg.cls += ' active';
29559         }
29560         if(this.disabled){
29561             cfg.cls += ' disabled';
29562         }
29563         
29564         return cfg;
29565     },
29566     
29567     disable : function()
29568     {
29569         this.setDisabled(true);
29570     },
29571     
29572     enable : function()
29573     {
29574         this.setDisabled(false);
29575     },
29576     
29577     initEvents: function() 
29578     {
29579         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29580         
29581         this.iconEl.on('click', this.onClick, this);
29582     },
29583     
29584     onClick : function(e)
29585     {
29586         e.preventDefault();
29587         
29588         if(this.disabled){
29589             return;
29590         }
29591         
29592         if(this.fireEvent('click', this, e) === false){
29593             return;
29594         };
29595         
29596         this.parent().setActiveItem(this);
29597     },
29598     
29599     isActive: function () 
29600     {
29601         return this.active;
29602     },
29603     
29604     setActive : function(state)
29605     {
29606         if(this.active == state){
29607             return;
29608         }
29609         
29610         this.active = state;
29611         
29612         if (state) {
29613             this.el.addClass('active');
29614             return;
29615         }
29616         
29617         this.el.removeClass('active');
29618         
29619         return;
29620     },
29621     
29622     setDisabled : function(state)
29623     {
29624         if(this.disabled == state){
29625             return;
29626         }
29627         
29628         this.disabled = state;
29629         
29630         if (state) {
29631             this.el.addClass('disabled');
29632             return;
29633         }
29634         
29635         this.el.removeClass('disabled');
29636     },
29637     
29638     tooltipEl : function()
29639     {
29640         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29641     }
29642 });
29643  
29644
29645  /*
29646  * - LGPL
29647  *
29648  * FieldLabel
29649  * 
29650  */
29651
29652 /**
29653  * @class Roo.bootstrap.FieldLabel
29654  * @extends Roo.bootstrap.Component
29655  * Bootstrap FieldLabel class
29656  * @cfg {String} html contents of the element
29657  * @cfg {String} tag tag of the element default label
29658  * @cfg {String} cls class of the element
29659  * @cfg {String} target label target 
29660  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29661  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29662  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29663  * @cfg {String} iconTooltip default "This field is required"
29664  * 
29665  * @constructor
29666  * Create a new FieldLabel
29667  * @param {Object} config The config object
29668  */
29669
29670 Roo.bootstrap.FieldLabel = function(config){
29671     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29672     
29673     this.addEvents({
29674             /**
29675              * @event invalid
29676              * Fires after the field has been marked as invalid.
29677              * @param {Roo.form.FieldLabel} this
29678              * @param {String} msg The validation message
29679              */
29680             invalid : true,
29681             /**
29682              * @event valid
29683              * Fires after the field has been validated with no errors.
29684              * @param {Roo.form.FieldLabel} this
29685              */
29686             valid : true
29687         });
29688 };
29689
29690 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29691     
29692     tag: 'label',
29693     cls: '',
29694     html: '',
29695     target: '',
29696     allowBlank : true,
29697     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29698     validClass : 'text-success fa fa-lg fa-check',
29699     iconTooltip : 'This field is required',
29700     
29701     getAutoCreate : function(){
29702         
29703         var cfg = {
29704             tag : this.tag,
29705             cls : 'roo-bootstrap-field-label ' + this.cls,
29706             for : this.target,
29707             cn : [
29708                 {
29709                     tag : 'i',
29710                     cls : '',
29711                     tooltip : this.iconTooltip
29712                 },
29713                 {
29714                     tag : 'span',
29715                     html : this.html
29716                 }
29717             ] 
29718         };
29719         
29720         return cfg;
29721     },
29722     
29723     initEvents: function() 
29724     {
29725         Roo.bootstrap.Element.superclass.initEvents.call(this);
29726         
29727         this.iconEl = this.el.select('i', true).first();
29728         
29729         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29730         
29731         Roo.bootstrap.FieldLabel.register(this);
29732     },
29733     
29734     /**
29735      * Mark this field as valid
29736      */
29737     markValid : function()
29738     {
29739         this.iconEl.show();
29740         
29741         this.iconEl.removeClass(this.invalidClass);
29742         
29743         this.iconEl.addClass(this.validClass);
29744         
29745         this.fireEvent('valid', this);
29746     },
29747     
29748     /**
29749      * Mark this field as invalid
29750      * @param {String} msg The validation message
29751      */
29752     markInvalid : function(msg)
29753     {
29754         this.iconEl.show();
29755         
29756         this.iconEl.removeClass(this.validClass);
29757         
29758         this.iconEl.addClass(this.invalidClass);
29759         
29760         this.fireEvent('invalid', this, msg);
29761     }
29762     
29763    
29764 });
29765
29766 Roo.apply(Roo.bootstrap.FieldLabel, {
29767     
29768     groups: {},
29769     
29770      /**
29771     * register a FieldLabel Group
29772     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29773     */
29774     register : function(label)
29775     {
29776         if(this.groups.hasOwnProperty(label.target)){
29777             return;
29778         }
29779      
29780         this.groups[label.target] = label;
29781         
29782     },
29783     /**
29784     * fetch a FieldLabel Group based on the target
29785     * @param {string} target
29786     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29787     */
29788     get: function(target) {
29789         if (typeof(this.groups[target]) == 'undefined') {
29790             return false;
29791         }
29792         
29793         return this.groups[target] ;
29794     }
29795 });
29796
29797  
29798
29799  /*
29800  * - LGPL
29801  *
29802  * page DateSplitField.
29803  * 
29804  */
29805
29806
29807 /**
29808  * @class Roo.bootstrap.DateSplitField
29809  * @extends Roo.bootstrap.Component
29810  * Bootstrap DateSplitField class
29811  * @cfg {string} fieldLabel - the label associated
29812  * @cfg {Number} labelWidth set the width of label (0-12)
29813  * @cfg {String} labelAlign (top|left)
29814  * @cfg {Boolean} dayAllowBlank (true|false) default false
29815  * @cfg {Boolean} monthAllowBlank (true|false) default false
29816  * @cfg {Boolean} yearAllowBlank (true|false) default false
29817  * @cfg {string} dayPlaceholder 
29818  * @cfg {string} monthPlaceholder
29819  * @cfg {string} yearPlaceholder
29820  * @cfg {string} dayFormat default 'd'
29821  * @cfg {string} monthFormat default 'm'
29822  * @cfg {string} yearFormat default 'Y'
29823  * @cfg {Number} labellg set the width of label (1-12)
29824  * @cfg {Number} labelmd set the width of label (1-12)
29825  * @cfg {Number} labelsm set the width of label (1-12)
29826  * @cfg {Number} labelxs set the width of label (1-12)
29827
29828  *     
29829  * @constructor
29830  * Create a new DateSplitField
29831  * @param {Object} config The config object
29832  */
29833
29834 Roo.bootstrap.DateSplitField = function(config){
29835     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29836     
29837     this.addEvents({
29838         // raw events
29839          /**
29840          * @event years
29841          * getting the data of years
29842          * @param {Roo.bootstrap.DateSplitField} this
29843          * @param {Object} years
29844          */
29845         "years" : true,
29846         /**
29847          * @event days
29848          * getting the data of days
29849          * @param {Roo.bootstrap.DateSplitField} this
29850          * @param {Object} days
29851          */
29852         "days" : true,
29853         /**
29854          * @event invalid
29855          * Fires after the field has been marked as invalid.
29856          * @param {Roo.form.Field} this
29857          * @param {String} msg The validation message
29858          */
29859         invalid : true,
29860        /**
29861          * @event valid
29862          * Fires after the field has been validated with no errors.
29863          * @param {Roo.form.Field} this
29864          */
29865         valid : true
29866     });
29867 };
29868
29869 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29870     
29871     fieldLabel : '',
29872     labelAlign : 'top',
29873     labelWidth : 3,
29874     dayAllowBlank : false,
29875     monthAllowBlank : false,
29876     yearAllowBlank : false,
29877     dayPlaceholder : '',
29878     monthPlaceholder : '',
29879     yearPlaceholder : '',
29880     dayFormat : 'd',
29881     monthFormat : 'm',
29882     yearFormat : 'Y',
29883     isFormField : true,
29884     labellg : 0,
29885     labelmd : 0,
29886     labelsm : 0,
29887     labelxs : 0,
29888     
29889     getAutoCreate : function()
29890     {
29891         var cfg = {
29892             tag : 'div',
29893             cls : 'row roo-date-split-field-group',
29894             cn : [
29895                 {
29896                     tag : 'input',
29897                     type : 'hidden',
29898                     cls : 'form-hidden-field roo-date-split-field-group-value',
29899                     name : this.name
29900                 }
29901             ]
29902         };
29903         
29904         var labelCls = 'col-md-12';
29905         var contentCls = 'col-md-4';
29906         
29907         if(this.fieldLabel){
29908             
29909             var label = {
29910                 tag : 'div',
29911                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29912                 cn : [
29913                     {
29914                         tag : 'label',
29915                         html : this.fieldLabel
29916                     }
29917                 ]
29918             };
29919             
29920             if(this.labelAlign == 'left'){
29921             
29922                 if(this.labelWidth > 12){
29923                     label.style = "width: " + this.labelWidth + 'px';
29924                 }
29925
29926                 if(this.labelWidth < 13 && this.labelmd == 0){
29927                     this.labelmd = this.labelWidth;
29928                 }
29929
29930                 if(this.labellg > 0){
29931                     labelCls = ' col-lg-' + this.labellg;
29932                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29933                 }
29934
29935                 if(this.labelmd > 0){
29936                     labelCls = ' col-md-' + this.labelmd;
29937                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29938                 }
29939
29940                 if(this.labelsm > 0){
29941                     labelCls = ' col-sm-' + this.labelsm;
29942                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29943                 }
29944
29945                 if(this.labelxs > 0){
29946                     labelCls = ' col-xs-' + this.labelxs;
29947                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29948                 }
29949             }
29950             
29951             label.cls += ' ' + labelCls;
29952             
29953             cfg.cn.push(label);
29954         }
29955         
29956         Roo.each(['day', 'month', 'year'], function(t){
29957             cfg.cn.push({
29958                 tag : 'div',
29959                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29960             });
29961         }, this);
29962         
29963         return cfg;
29964     },
29965     
29966     inputEl: function ()
29967     {
29968         return this.el.select('.roo-date-split-field-group-value', true).first();
29969     },
29970     
29971     onRender : function(ct, position) 
29972     {
29973         var _this = this;
29974         
29975         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29976         
29977         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29978         
29979         this.dayField = new Roo.bootstrap.ComboBox({
29980             allowBlank : this.dayAllowBlank,
29981             alwaysQuery : true,
29982             displayField : 'value',
29983             editable : false,
29984             fieldLabel : '',
29985             forceSelection : true,
29986             mode : 'local',
29987             placeholder : this.dayPlaceholder,
29988             selectOnFocus : true,
29989             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29990             triggerAction : 'all',
29991             typeAhead : true,
29992             valueField : 'value',
29993             store : new Roo.data.SimpleStore({
29994                 data : (function() {    
29995                     var days = [];
29996                     _this.fireEvent('days', _this, days);
29997                     return days;
29998                 })(),
29999                 fields : [ 'value' ]
30000             }),
30001             listeners : {
30002                 select : function (_self, record, index)
30003                 {
30004                     _this.setValue(_this.getValue());
30005                 }
30006             }
30007         });
30008
30009         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30010         
30011         this.monthField = new Roo.bootstrap.MonthField({
30012             after : '<i class=\"fa fa-calendar\"></i>',
30013             allowBlank : this.monthAllowBlank,
30014             placeholder : this.monthPlaceholder,
30015             readOnly : true,
30016             listeners : {
30017                 render : function (_self)
30018                 {
30019                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30020                         e.preventDefault();
30021                         _self.focus();
30022                     });
30023                 },
30024                 select : function (_self, oldvalue, newvalue)
30025                 {
30026                     _this.setValue(_this.getValue());
30027                 }
30028             }
30029         });
30030         
30031         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30032         
30033         this.yearField = new Roo.bootstrap.ComboBox({
30034             allowBlank : this.yearAllowBlank,
30035             alwaysQuery : true,
30036             displayField : 'value',
30037             editable : false,
30038             fieldLabel : '',
30039             forceSelection : true,
30040             mode : 'local',
30041             placeholder : this.yearPlaceholder,
30042             selectOnFocus : true,
30043             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30044             triggerAction : 'all',
30045             typeAhead : true,
30046             valueField : 'value',
30047             store : new Roo.data.SimpleStore({
30048                 data : (function() {
30049                     var years = [];
30050                     _this.fireEvent('years', _this, years);
30051                     return years;
30052                 })(),
30053                 fields : [ 'value' ]
30054             }),
30055             listeners : {
30056                 select : function (_self, record, index)
30057                 {
30058                     _this.setValue(_this.getValue());
30059                 }
30060             }
30061         });
30062
30063         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30064     },
30065     
30066     setValue : function(v, format)
30067     {
30068         this.inputEl.dom.value = v;
30069         
30070         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30071         
30072         var d = Date.parseDate(v, f);
30073         
30074         if(!d){
30075             this.validate();
30076             return;
30077         }
30078         
30079         this.setDay(d.format(this.dayFormat));
30080         this.setMonth(d.format(this.monthFormat));
30081         this.setYear(d.format(this.yearFormat));
30082         
30083         this.validate();
30084         
30085         return;
30086     },
30087     
30088     setDay : function(v)
30089     {
30090         this.dayField.setValue(v);
30091         this.inputEl.dom.value = this.getValue();
30092         this.validate();
30093         return;
30094     },
30095     
30096     setMonth : function(v)
30097     {
30098         this.monthField.setValue(v, true);
30099         this.inputEl.dom.value = this.getValue();
30100         this.validate();
30101         return;
30102     },
30103     
30104     setYear : function(v)
30105     {
30106         this.yearField.setValue(v);
30107         this.inputEl.dom.value = this.getValue();
30108         this.validate();
30109         return;
30110     },
30111     
30112     getDay : function()
30113     {
30114         return this.dayField.getValue();
30115     },
30116     
30117     getMonth : function()
30118     {
30119         return this.monthField.getValue();
30120     },
30121     
30122     getYear : function()
30123     {
30124         return this.yearField.getValue();
30125     },
30126     
30127     getValue : function()
30128     {
30129         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30130         
30131         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30132         
30133         return date;
30134     },
30135     
30136     reset : function()
30137     {
30138         this.setDay('');
30139         this.setMonth('');
30140         this.setYear('');
30141         this.inputEl.dom.value = '';
30142         this.validate();
30143         return;
30144     },
30145     
30146     validate : function()
30147     {
30148         var d = this.dayField.validate();
30149         var m = this.monthField.validate();
30150         var y = this.yearField.validate();
30151         
30152         var valid = true;
30153         
30154         if(
30155                 (!this.dayAllowBlank && !d) ||
30156                 (!this.monthAllowBlank && !m) ||
30157                 (!this.yearAllowBlank && !y)
30158         ){
30159             valid = false;
30160         }
30161         
30162         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30163             return valid;
30164         }
30165         
30166         if(valid){
30167             this.markValid();
30168             return valid;
30169         }
30170         
30171         this.markInvalid();
30172         
30173         return valid;
30174     },
30175     
30176     markValid : function()
30177     {
30178         
30179         var label = this.el.select('label', true).first();
30180         var icon = this.el.select('i.fa-star', true).first();
30181
30182         if(label && icon){
30183             icon.remove();
30184         }
30185         
30186         this.fireEvent('valid', this);
30187     },
30188     
30189      /**
30190      * Mark this field as invalid
30191      * @param {String} msg The validation message
30192      */
30193     markInvalid : function(msg)
30194     {
30195         
30196         var label = this.el.select('label', true).first();
30197         var icon = this.el.select('i.fa-star', true).first();
30198
30199         if(label && !icon){
30200             this.el.select('.roo-date-split-field-label', true).createChild({
30201                 tag : 'i',
30202                 cls : 'text-danger fa fa-lg fa-star',
30203                 tooltip : 'This field is required',
30204                 style : 'margin-right:5px;'
30205             }, label, true);
30206         }
30207         
30208         this.fireEvent('invalid', this, msg);
30209     },
30210     
30211     clearInvalid : function()
30212     {
30213         var label = this.el.select('label', true).first();
30214         var icon = this.el.select('i.fa-star', true).first();
30215
30216         if(label && icon){
30217             icon.remove();
30218         }
30219         
30220         this.fireEvent('valid', this);
30221     },
30222     
30223     getName: function()
30224     {
30225         return this.name;
30226     }
30227     
30228 });
30229
30230  /**
30231  *
30232  * This is based on 
30233  * http://masonry.desandro.com
30234  *
30235  * The idea is to render all the bricks based on vertical width...
30236  *
30237  * The original code extends 'outlayer' - we might need to use that....
30238  * 
30239  */
30240
30241
30242 /**
30243  * @class Roo.bootstrap.LayoutMasonry
30244  * @extends Roo.bootstrap.Component
30245  * Bootstrap Layout Masonry class
30246  * 
30247  * @constructor
30248  * Create a new Element
30249  * @param {Object} config The config object
30250  */
30251
30252 Roo.bootstrap.LayoutMasonry = function(config){
30253     
30254     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30255     
30256     this.bricks = [];
30257     
30258     Roo.bootstrap.LayoutMasonry.register(this);
30259     
30260     this.addEvents({
30261         // raw events
30262         /**
30263          * @event layout
30264          * Fire after layout the items
30265          * @param {Roo.bootstrap.LayoutMasonry} this
30266          * @param {Roo.EventObject} e
30267          */
30268         "layout" : true
30269     });
30270     
30271 };
30272
30273 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30274     
30275     /**
30276      * @cfg {Boolean} isLayoutInstant = no animation?
30277      */   
30278     isLayoutInstant : false, // needed?
30279    
30280     /**
30281      * @cfg {Number} boxWidth  width of the columns
30282      */   
30283     boxWidth : 450,
30284     
30285       /**
30286      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30287      */   
30288     boxHeight : 0,
30289     
30290     /**
30291      * @cfg {Number} padWidth padding below box..
30292      */   
30293     padWidth : 10, 
30294     
30295     /**
30296      * @cfg {Number} gutter gutter width..
30297      */   
30298     gutter : 10,
30299     
30300      /**
30301      * @cfg {Number} maxCols maximum number of columns
30302      */   
30303     
30304     maxCols: 0,
30305     
30306     /**
30307      * @cfg {Boolean} isAutoInitial defalut true
30308      */   
30309     isAutoInitial : true, 
30310     
30311     containerWidth: 0,
30312     
30313     /**
30314      * @cfg {Boolean} isHorizontal defalut false
30315      */   
30316     isHorizontal : false, 
30317
30318     currentSize : null,
30319     
30320     tag: 'div',
30321     
30322     cls: '',
30323     
30324     bricks: null, //CompositeElement
30325     
30326     cols : 1,
30327     
30328     _isLayoutInited : false,
30329     
30330 //    isAlternative : false, // only use for vertical layout...
30331     
30332     /**
30333      * @cfg {Number} alternativePadWidth padding below box..
30334      */   
30335     alternativePadWidth : 50,
30336     
30337     selectedBrick : [],
30338     
30339     getAutoCreate : function(){
30340         
30341         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30342         
30343         var cfg = {
30344             tag: this.tag,
30345             cls: 'blog-masonary-wrapper ' + this.cls,
30346             cn : {
30347                 cls : 'mas-boxes masonary'
30348             }
30349         };
30350         
30351         return cfg;
30352     },
30353     
30354     getChildContainer: function( )
30355     {
30356         if (this.boxesEl) {
30357             return this.boxesEl;
30358         }
30359         
30360         this.boxesEl = this.el.select('.mas-boxes').first();
30361         
30362         return this.boxesEl;
30363     },
30364     
30365     
30366     initEvents : function()
30367     {
30368         var _this = this;
30369         
30370         if(this.isAutoInitial){
30371             Roo.log('hook children rendered');
30372             this.on('childrenrendered', function() {
30373                 Roo.log('children rendered');
30374                 _this.initial();
30375             } ,this);
30376         }
30377     },
30378     
30379     initial : function()
30380     {
30381         this.selectedBrick = [];
30382         
30383         this.currentSize = this.el.getBox(true);
30384         
30385         Roo.EventManager.onWindowResize(this.resize, this); 
30386
30387         if(!this.isAutoInitial){
30388             this.layout();
30389             return;
30390         }
30391         
30392         this.layout();
30393         
30394         return;
30395         //this.layout.defer(500,this);
30396         
30397     },
30398     
30399     resize : function()
30400     {
30401         var cs = this.el.getBox(true);
30402         
30403         if (
30404                 this.currentSize.width == cs.width && 
30405                 this.currentSize.x == cs.x && 
30406                 this.currentSize.height == cs.height && 
30407                 this.currentSize.y == cs.y 
30408         ) {
30409             Roo.log("no change in with or X or Y");
30410             return;
30411         }
30412         
30413         this.currentSize = cs;
30414         
30415         this.layout();
30416         
30417     },
30418     
30419     layout : function()
30420     {   
30421         this._resetLayout();
30422         
30423         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30424         
30425         this.layoutItems( isInstant );
30426       
30427         this._isLayoutInited = true;
30428         
30429         this.fireEvent('layout', this);
30430         
30431     },
30432     
30433     _resetLayout : function()
30434     {
30435         if(this.isHorizontal){
30436             this.horizontalMeasureColumns();
30437             return;
30438         }
30439         
30440         this.verticalMeasureColumns();
30441         
30442     },
30443     
30444     verticalMeasureColumns : function()
30445     {
30446         this.getContainerWidth();
30447         
30448 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30449 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30450 //            return;
30451 //        }
30452         
30453         var boxWidth = this.boxWidth + this.padWidth;
30454         
30455         if(this.containerWidth < this.boxWidth){
30456             boxWidth = this.containerWidth
30457         }
30458         
30459         var containerWidth = this.containerWidth;
30460         
30461         var cols = Math.floor(containerWidth / boxWidth);
30462         
30463         this.cols = Math.max( cols, 1 );
30464         
30465         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30466         
30467         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30468         
30469         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30470         
30471         this.colWidth = boxWidth + avail - this.padWidth;
30472         
30473         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30474         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30475     },
30476     
30477     horizontalMeasureColumns : function()
30478     {
30479         this.getContainerWidth();
30480         
30481         var boxWidth = this.boxWidth;
30482         
30483         if(this.containerWidth < boxWidth){
30484             boxWidth = this.containerWidth;
30485         }
30486         
30487         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30488         
30489         this.el.setHeight(boxWidth);
30490         
30491     },
30492     
30493     getContainerWidth : function()
30494     {
30495         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30496     },
30497     
30498     layoutItems : function( isInstant )
30499     {
30500         Roo.log(this.bricks);
30501         
30502         var items = Roo.apply([], this.bricks);
30503         
30504         if(this.isHorizontal){
30505             this._horizontalLayoutItems( items , isInstant );
30506             return;
30507         }
30508         
30509 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30510 //            this._verticalAlternativeLayoutItems( items , isInstant );
30511 //            return;
30512 //        }
30513         
30514         this._verticalLayoutItems( items , isInstant );
30515         
30516     },
30517     
30518     _verticalLayoutItems : function ( items , isInstant)
30519     {
30520         if ( !items || !items.length ) {
30521             return;
30522         }
30523         
30524         var standard = [
30525             ['xs', 'xs', 'xs', 'tall'],
30526             ['xs', 'xs', 'tall'],
30527             ['xs', 'xs', 'sm'],
30528             ['xs', 'xs', 'xs'],
30529             ['xs', 'tall'],
30530             ['xs', 'sm'],
30531             ['xs', 'xs'],
30532             ['xs'],
30533             
30534             ['sm', 'xs', 'xs'],
30535             ['sm', 'xs'],
30536             ['sm'],
30537             
30538             ['tall', 'xs', 'xs', 'xs'],
30539             ['tall', 'xs', 'xs'],
30540             ['tall', 'xs'],
30541             ['tall']
30542             
30543         ];
30544         
30545         var queue = [];
30546         
30547         var boxes = [];
30548         
30549         var box = [];
30550         
30551         Roo.each(items, function(item, k){
30552             
30553             switch (item.size) {
30554                 // these layouts take up a full box,
30555                 case 'md' :
30556                 case 'md-left' :
30557                 case 'md-right' :
30558                 case 'wide' :
30559                     
30560                     if(box.length){
30561                         boxes.push(box);
30562                         box = [];
30563                     }
30564                     
30565                     boxes.push([item]);
30566                     
30567                     break;
30568                     
30569                 case 'xs' :
30570                 case 'sm' :
30571                 case 'tall' :
30572                     
30573                     box.push(item);
30574                     
30575                     break;
30576                 default :
30577                     break;
30578                     
30579             }
30580             
30581         }, this);
30582         
30583         if(box.length){
30584             boxes.push(box);
30585             box = [];
30586         }
30587         
30588         var filterPattern = function(box, length)
30589         {
30590             if(!box.length){
30591                 return;
30592             }
30593             
30594             var match = false;
30595             
30596             var pattern = box.slice(0, length);
30597             
30598             var format = [];
30599             
30600             Roo.each(pattern, function(i){
30601                 format.push(i.size);
30602             }, this);
30603             
30604             Roo.each(standard, function(s){
30605                 
30606                 if(String(s) != String(format)){
30607                     return;
30608                 }
30609                 
30610                 match = true;
30611                 return false;
30612                 
30613             }, this);
30614             
30615             if(!match && length == 1){
30616                 return;
30617             }
30618             
30619             if(!match){
30620                 filterPattern(box, length - 1);
30621                 return;
30622             }
30623                 
30624             queue.push(pattern);
30625
30626             box = box.slice(length, box.length);
30627
30628             filterPattern(box, 4);
30629
30630             return;
30631             
30632         }
30633         
30634         Roo.each(boxes, function(box, k){
30635             
30636             if(!box.length){
30637                 return;
30638             }
30639             
30640             if(box.length == 1){
30641                 queue.push(box);
30642                 return;
30643             }
30644             
30645             filterPattern(box, 4);
30646             
30647         }, this);
30648         
30649         this._processVerticalLayoutQueue( queue, isInstant );
30650         
30651     },
30652     
30653 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30654 //    {
30655 //        if ( !items || !items.length ) {
30656 //            return;
30657 //        }
30658 //
30659 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30660 //        
30661 //    },
30662     
30663     _horizontalLayoutItems : function ( items , isInstant)
30664     {
30665         if ( !items || !items.length || items.length < 3) {
30666             return;
30667         }
30668         
30669         items.reverse();
30670         
30671         var eItems = items.slice(0, 3);
30672         
30673         items = items.slice(3, items.length);
30674         
30675         var standard = [
30676             ['xs', 'xs', 'xs', 'wide'],
30677             ['xs', 'xs', 'wide'],
30678             ['xs', 'xs', 'sm'],
30679             ['xs', 'xs', 'xs'],
30680             ['xs', 'wide'],
30681             ['xs', 'sm'],
30682             ['xs', 'xs'],
30683             ['xs'],
30684             
30685             ['sm', 'xs', 'xs'],
30686             ['sm', 'xs'],
30687             ['sm'],
30688             
30689             ['wide', 'xs', 'xs', 'xs'],
30690             ['wide', 'xs', 'xs'],
30691             ['wide', 'xs'],
30692             ['wide'],
30693             
30694             ['wide-thin']
30695         ];
30696         
30697         var queue = [];
30698         
30699         var boxes = [];
30700         
30701         var box = [];
30702         
30703         Roo.each(items, function(item, k){
30704             
30705             switch (item.size) {
30706                 case 'md' :
30707                 case 'md-left' :
30708                 case 'md-right' :
30709                 case 'tall' :
30710                     
30711                     if(box.length){
30712                         boxes.push(box);
30713                         box = [];
30714                     }
30715                     
30716                     boxes.push([item]);
30717                     
30718                     break;
30719                     
30720                 case 'xs' :
30721                 case 'sm' :
30722                 case 'wide' :
30723                 case 'wide-thin' :
30724                     
30725                     box.push(item);
30726                     
30727                     break;
30728                 default :
30729                     break;
30730                     
30731             }
30732             
30733         }, this);
30734         
30735         if(box.length){
30736             boxes.push(box);
30737             box = [];
30738         }
30739         
30740         var filterPattern = function(box, length)
30741         {
30742             if(!box.length){
30743                 return;
30744             }
30745             
30746             var match = false;
30747             
30748             var pattern = box.slice(0, length);
30749             
30750             var format = [];
30751             
30752             Roo.each(pattern, function(i){
30753                 format.push(i.size);
30754             }, this);
30755             
30756             Roo.each(standard, function(s){
30757                 
30758                 if(String(s) != String(format)){
30759                     return;
30760                 }
30761                 
30762                 match = true;
30763                 return false;
30764                 
30765             }, this);
30766             
30767             if(!match && length == 1){
30768                 return;
30769             }
30770             
30771             if(!match){
30772                 filterPattern(box, length - 1);
30773                 return;
30774             }
30775                 
30776             queue.push(pattern);
30777
30778             box = box.slice(length, box.length);
30779
30780             filterPattern(box, 4);
30781
30782             return;
30783             
30784         }
30785         
30786         Roo.each(boxes, function(box, k){
30787             
30788             if(!box.length){
30789                 return;
30790             }
30791             
30792             if(box.length == 1){
30793                 queue.push(box);
30794                 return;
30795             }
30796             
30797             filterPattern(box, 4);
30798             
30799         }, this);
30800         
30801         
30802         var prune = [];
30803         
30804         var pos = this.el.getBox(true);
30805         
30806         var minX = pos.x;
30807         
30808         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30809         
30810         var hit_end = false;
30811         
30812         Roo.each(queue, function(box){
30813             
30814             if(hit_end){
30815                 
30816                 Roo.each(box, function(b){
30817                 
30818                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30819                     b.el.hide();
30820
30821                 }, this);
30822
30823                 return;
30824             }
30825             
30826             var mx = 0;
30827             
30828             Roo.each(box, function(b){
30829                 
30830                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30831                 b.el.show();
30832
30833                 mx = Math.max(mx, b.x);
30834                 
30835             }, this);
30836             
30837             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30838             
30839             if(maxX < minX){
30840                 
30841                 Roo.each(box, function(b){
30842                 
30843                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30844                     b.el.hide();
30845                     
30846                 }, this);
30847                 
30848                 hit_end = true;
30849                 
30850                 return;
30851             }
30852             
30853             prune.push(box);
30854             
30855         }, this);
30856         
30857         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30858     },
30859     
30860     /** Sets position of item in DOM
30861     * @param {Element} item
30862     * @param {Number} x - horizontal position
30863     * @param {Number} y - vertical position
30864     * @param {Boolean} isInstant - disables transitions
30865     */
30866     _processVerticalLayoutQueue : function( queue, isInstant )
30867     {
30868         var pos = this.el.getBox(true);
30869         var x = pos.x;
30870         var y = pos.y;
30871         var maxY = [];
30872         
30873         for (var i = 0; i < this.cols; i++){
30874             maxY[i] = pos.y;
30875         }
30876         
30877         Roo.each(queue, function(box, k){
30878             
30879             var col = k % this.cols;
30880             
30881             Roo.each(box, function(b,kk){
30882                 
30883                 b.el.position('absolute');
30884                 
30885                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30886                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30887                 
30888                 if(b.size == 'md-left' || b.size == 'md-right'){
30889                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30890                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30891                 }
30892                 
30893                 b.el.setWidth(width);
30894                 b.el.setHeight(height);
30895                 // iframe?
30896                 b.el.select('iframe',true).setSize(width,height);
30897                 
30898             }, this);
30899             
30900             for (var i = 0; i < this.cols; i++){
30901                 
30902                 if(maxY[i] < maxY[col]){
30903                     col = i;
30904                     continue;
30905                 }
30906                 
30907                 col = Math.min(col, i);
30908                 
30909             }
30910             
30911             x = pos.x + col * (this.colWidth + this.padWidth);
30912             
30913             y = maxY[col];
30914             
30915             var positions = [];
30916             
30917             switch (box.length){
30918                 case 1 :
30919                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30920                     break;
30921                 case 2 :
30922                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30923                     break;
30924                 case 3 :
30925                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30926                     break;
30927                 case 4 :
30928                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30929                     break;
30930                 default :
30931                     break;
30932             }
30933             
30934             Roo.each(box, function(b,kk){
30935                 
30936                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30937                 
30938                 var sz = b.el.getSize();
30939                 
30940                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30941                 
30942             }, this);
30943             
30944         }, this);
30945         
30946         var mY = 0;
30947         
30948         for (var i = 0; i < this.cols; i++){
30949             mY = Math.max(mY, maxY[i]);
30950         }
30951         
30952         this.el.setHeight(mY - pos.y);
30953         
30954     },
30955     
30956 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30957 //    {
30958 //        var pos = this.el.getBox(true);
30959 //        var x = pos.x;
30960 //        var y = pos.y;
30961 //        var maxX = pos.right;
30962 //        
30963 //        var maxHeight = 0;
30964 //        
30965 //        Roo.each(items, function(item, k){
30966 //            
30967 //            var c = k % 2;
30968 //            
30969 //            item.el.position('absolute');
30970 //                
30971 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30972 //
30973 //            item.el.setWidth(width);
30974 //
30975 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30976 //
30977 //            item.el.setHeight(height);
30978 //            
30979 //            if(c == 0){
30980 //                item.el.setXY([x, y], isInstant ? false : true);
30981 //            } else {
30982 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30983 //            }
30984 //            
30985 //            y = y + height + this.alternativePadWidth;
30986 //            
30987 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30988 //            
30989 //        }, this);
30990 //        
30991 //        this.el.setHeight(maxHeight);
30992 //        
30993 //    },
30994     
30995     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30996     {
30997         var pos = this.el.getBox(true);
30998         
30999         var minX = pos.x;
31000         var minY = pos.y;
31001         
31002         var maxX = pos.right;
31003         
31004         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31005         
31006         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31007         
31008         Roo.each(queue, function(box, k){
31009             
31010             Roo.each(box, function(b, kk){
31011                 
31012                 b.el.position('absolute');
31013                 
31014                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31015                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31016                 
31017                 if(b.size == 'md-left' || b.size == 'md-right'){
31018                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31019                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31020                 }
31021                 
31022                 b.el.setWidth(width);
31023                 b.el.setHeight(height);
31024                 
31025             }, this);
31026             
31027             if(!box.length){
31028                 return;
31029             }
31030             
31031             var positions = [];
31032             
31033             switch (box.length){
31034                 case 1 :
31035                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31036                     break;
31037                 case 2 :
31038                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31039                     break;
31040                 case 3 :
31041                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31042                     break;
31043                 case 4 :
31044                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31045                     break;
31046                 default :
31047                     break;
31048             }
31049             
31050             Roo.each(box, function(b,kk){
31051                 
31052                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31053                 
31054                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31055                 
31056             }, this);
31057             
31058         }, this);
31059         
31060     },
31061     
31062     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31063     {
31064         Roo.each(eItems, function(b,k){
31065             
31066             b.size = (k == 0) ? 'sm' : 'xs';
31067             b.x = (k == 0) ? 2 : 1;
31068             b.y = (k == 0) ? 2 : 1;
31069             
31070             b.el.position('absolute');
31071             
31072             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31073                 
31074             b.el.setWidth(width);
31075             
31076             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31077             
31078             b.el.setHeight(height);
31079             
31080         }, this);
31081
31082         var positions = [];
31083         
31084         positions.push({
31085             x : maxX - this.unitWidth * 2 - this.gutter,
31086             y : minY
31087         });
31088         
31089         positions.push({
31090             x : maxX - this.unitWidth,
31091             y : minY + (this.unitWidth + this.gutter) * 2
31092         });
31093         
31094         positions.push({
31095             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31096             y : minY
31097         });
31098         
31099         Roo.each(eItems, function(b,k){
31100             
31101             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31102
31103         }, this);
31104         
31105     },
31106     
31107     getVerticalOneBoxColPositions : function(x, y, box)
31108     {
31109         var pos = [];
31110         
31111         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31112         
31113         if(box[0].size == 'md-left'){
31114             rand = 0;
31115         }
31116         
31117         if(box[0].size == 'md-right'){
31118             rand = 1;
31119         }
31120         
31121         pos.push({
31122             x : x + (this.unitWidth + this.gutter) * rand,
31123             y : y
31124         });
31125         
31126         return pos;
31127     },
31128     
31129     getVerticalTwoBoxColPositions : function(x, y, box)
31130     {
31131         var pos = [];
31132         
31133         if(box[0].size == 'xs'){
31134             
31135             pos.push({
31136                 x : x,
31137                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31138             });
31139
31140             pos.push({
31141                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31142                 y : y
31143             });
31144             
31145             return pos;
31146             
31147         }
31148         
31149         pos.push({
31150             x : x,
31151             y : y
31152         });
31153
31154         pos.push({
31155             x : x + (this.unitWidth + this.gutter) * 2,
31156             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31157         });
31158         
31159         return pos;
31160         
31161     },
31162     
31163     getVerticalThreeBoxColPositions : function(x, y, box)
31164     {
31165         var pos = [];
31166         
31167         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31168             
31169             pos.push({
31170                 x : x,
31171                 y : y
31172             });
31173
31174             pos.push({
31175                 x : x + (this.unitWidth + this.gutter) * 1,
31176                 y : y
31177             });
31178             
31179             pos.push({
31180                 x : x + (this.unitWidth + this.gutter) * 2,
31181                 y : y
31182             });
31183             
31184             return pos;
31185             
31186         }
31187         
31188         if(box[0].size == 'xs' && box[1].size == 'xs'){
31189             
31190             pos.push({
31191                 x : x,
31192                 y : y
31193             });
31194
31195             pos.push({
31196                 x : x,
31197                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31198             });
31199             
31200             pos.push({
31201                 x : x + (this.unitWidth + this.gutter) * 1,
31202                 y : y
31203             });
31204             
31205             return pos;
31206             
31207         }
31208         
31209         pos.push({
31210             x : x,
31211             y : y
31212         });
31213
31214         pos.push({
31215             x : x + (this.unitWidth + this.gutter) * 2,
31216             y : y
31217         });
31218
31219         pos.push({
31220             x : x + (this.unitWidth + this.gutter) * 2,
31221             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31222         });
31223             
31224         return pos;
31225         
31226     },
31227     
31228     getVerticalFourBoxColPositions : function(x, y, box)
31229     {
31230         var pos = [];
31231         
31232         if(box[0].size == 'xs'){
31233             
31234             pos.push({
31235                 x : x,
31236                 y : y
31237             });
31238
31239             pos.push({
31240                 x : x,
31241                 y : y + (this.unitHeight + this.gutter) * 1
31242             });
31243             
31244             pos.push({
31245                 x : x,
31246                 y : y + (this.unitHeight + this.gutter) * 2
31247             });
31248             
31249             pos.push({
31250                 x : x + (this.unitWidth + this.gutter) * 1,
31251                 y : y
31252             });
31253             
31254             return pos;
31255             
31256         }
31257         
31258         pos.push({
31259             x : x,
31260             y : y
31261         });
31262
31263         pos.push({
31264             x : x + (this.unitWidth + this.gutter) * 2,
31265             y : y
31266         });
31267
31268         pos.push({
31269             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31270             y : y + (this.unitHeight + this.gutter) * 1
31271         });
31272
31273         pos.push({
31274             x : x + (this.unitWidth + this.gutter) * 2,
31275             y : y + (this.unitWidth + this.gutter) * 2
31276         });
31277
31278         return pos;
31279         
31280     },
31281     
31282     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31283     {
31284         var pos = [];
31285         
31286         if(box[0].size == 'md-left'){
31287             pos.push({
31288                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31289                 y : minY
31290             });
31291             
31292             return pos;
31293         }
31294         
31295         if(box[0].size == 'md-right'){
31296             pos.push({
31297                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31298                 y : minY + (this.unitWidth + this.gutter) * 1
31299             });
31300             
31301             return pos;
31302         }
31303         
31304         var rand = Math.floor(Math.random() * (4 - box[0].y));
31305         
31306         pos.push({
31307             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31308             y : minY + (this.unitWidth + this.gutter) * rand
31309         });
31310         
31311         return pos;
31312         
31313     },
31314     
31315     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31316     {
31317         var pos = [];
31318         
31319         if(box[0].size == 'xs'){
31320             
31321             pos.push({
31322                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31323                 y : minY
31324             });
31325
31326             pos.push({
31327                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31328                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31329             });
31330             
31331             return pos;
31332             
31333         }
31334         
31335         pos.push({
31336             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31337             y : minY
31338         });
31339
31340         pos.push({
31341             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31342             y : minY + (this.unitWidth + this.gutter) * 2
31343         });
31344         
31345         return pos;
31346         
31347     },
31348     
31349     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31350     {
31351         var pos = [];
31352         
31353         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31354             
31355             pos.push({
31356                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31357                 y : minY
31358             });
31359
31360             pos.push({
31361                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31362                 y : minY + (this.unitWidth + this.gutter) * 1
31363             });
31364             
31365             pos.push({
31366                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31367                 y : minY + (this.unitWidth + this.gutter) * 2
31368             });
31369             
31370             return pos;
31371             
31372         }
31373         
31374         if(box[0].size == 'xs' && box[1].size == 'xs'){
31375             
31376             pos.push({
31377                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31378                 y : minY
31379             });
31380
31381             pos.push({
31382                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31383                 y : minY
31384             });
31385             
31386             pos.push({
31387                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31388                 y : minY + (this.unitWidth + this.gutter) * 1
31389             });
31390             
31391             return pos;
31392             
31393         }
31394         
31395         pos.push({
31396             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31397             y : minY
31398         });
31399
31400         pos.push({
31401             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31402             y : minY + (this.unitWidth + this.gutter) * 2
31403         });
31404
31405         pos.push({
31406             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31407             y : minY + (this.unitWidth + this.gutter) * 2
31408         });
31409             
31410         return pos;
31411         
31412     },
31413     
31414     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31415     {
31416         var pos = [];
31417         
31418         if(box[0].size == 'xs'){
31419             
31420             pos.push({
31421                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31422                 y : minY
31423             });
31424
31425             pos.push({
31426                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31427                 y : minY
31428             });
31429             
31430             pos.push({
31431                 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),
31432                 y : minY
31433             });
31434             
31435             pos.push({
31436                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31437                 y : minY + (this.unitWidth + this.gutter) * 1
31438             });
31439             
31440             return pos;
31441             
31442         }
31443         
31444         pos.push({
31445             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31446             y : minY
31447         });
31448         
31449         pos.push({
31450             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31451             y : minY + (this.unitWidth + this.gutter) * 2
31452         });
31453         
31454         pos.push({
31455             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31456             y : minY + (this.unitWidth + this.gutter) * 2
31457         });
31458         
31459         pos.push({
31460             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),
31461             y : minY + (this.unitWidth + this.gutter) * 2
31462         });
31463
31464         return pos;
31465         
31466     },
31467     
31468     /**
31469     * remove a Masonry Brick
31470     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31471     */
31472     removeBrick : function(brick_id)
31473     {
31474         if (!brick_id) {
31475             return;
31476         }
31477         
31478         for (var i = 0; i<this.bricks.length; i++) {
31479             if (this.bricks[i].id == brick_id) {
31480                 this.bricks.splice(i,1);
31481                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31482                 this.initial();
31483             }
31484         }
31485     },
31486     
31487     /**
31488     * adds a Masonry Brick
31489     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31490     */
31491     addBrick : function(cfg)
31492     {
31493         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31494         //this.register(cn);
31495         cn.parentId = this.id;
31496         cn.onRender(this.el, null);
31497         return cn;
31498     },
31499     
31500     /**
31501     * register a Masonry Brick
31502     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31503     */
31504     
31505     register : function(brick)
31506     {
31507         this.bricks.push(brick);
31508         brick.masonryId = this.id;
31509     },
31510     
31511     /**
31512     * clear all the Masonry Brick
31513     */
31514     clearAll : function()
31515     {
31516         this.bricks = [];
31517         //this.getChildContainer().dom.innerHTML = "";
31518         this.el.dom.innerHTML = '';
31519     },
31520     
31521     getSelected : function()
31522     {
31523         if (!this.selectedBrick) {
31524             return false;
31525         }
31526         
31527         return this.selectedBrick;
31528     }
31529 });
31530
31531 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31532     
31533     groups: {},
31534      /**
31535     * register a Masonry Layout
31536     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31537     */
31538     
31539     register : function(layout)
31540     {
31541         this.groups[layout.id] = layout;
31542     },
31543     /**
31544     * fetch a  Masonry Layout based on the masonry layout ID
31545     * @param {string} the masonry layout to add
31546     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31547     */
31548     
31549     get: function(layout_id) {
31550         if (typeof(this.groups[layout_id]) == 'undefined') {
31551             return false;
31552         }
31553         return this.groups[layout_id] ;
31554     }
31555     
31556     
31557     
31558 });
31559
31560  
31561
31562  /**
31563  *
31564  * This is based on 
31565  * http://masonry.desandro.com
31566  *
31567  * The idea is to render all the bricks based on vertical width...
31568  *
31569  * The original code extends 'outlayer' - we might need to use that....
31570  * 
31571  */
31572
31573
31574 /**
31575  * @class Roo.bootstrap.LayoutMasonryAuto
31576  * @extends Roo.bootstrap.Component
31577  * Bootstrap Layout Masonry class
31578  * 
31579  * @constructor
31580  * Create a new Element
31581  * @param {Object} config The config object
31582  */
31583
31584 Roo.bootstrap.LayoutMasonryAuto = function(config){
31585     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31586 };
31587
31588 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31589     
31590       /**
31591      * @cfg {Boolean} isFitWidth  - resize the width..
31592      */   
31593     isFitWidth : false,  // options..
31594     /**
31595      * @cfg {Boolean} isOriginLeft = left align?
31596      */   
31597     isOriginLeft : true,
31598     /**
31599      * @cfg {Boolean} isOriginTop = top align?
31600      */   
31601     isOriginTop : false,
31602     /**
31603      * @cfg {Boolean} isLayoutInstant = no animation?
31604      */   
31605     isLayoutInstant : false, // needed?
31606     /**
31607      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31608      */   
31609     isResizingContainer : true,
31610     /**
31611      * @cfg {Number} columnWidth  width of the columns 
31612      */   
31613     
31614     columnWidth : 0,
31615     
31616     /**
31617      * @cfg {Number} maxCols maximum number of columns
31618      */   
31619     
31620     maxCols: 0,
31621     /**
31622      * @cfg {Number} padHeight padding below box..
31623      */   
31624     
31625     padHeight : 10, 
31626     
31627     /**
31628      * @cfg {Boolean} isAutoInitial defalut true
31629      */   
31630     
31631     isAutoInitial : true, 
31632     
31633     // private?
31634     gutter : 0,
31635     
31636     containerWidth: 0,
31637     initialColumnWidth : 0,
31638     currentSize : null,
31639     
31640     colYs : null, // array.
31641     maxY : 0,
31642     padWidth: 10,
31643     
31644     
31645     tag: 'div',
31646     cls: '',
31647     bricks: null, //CompositeElement
31648     cols : 0, // array?
31649     // element : null, // wrapped now this.el
31650     _isLayoutInited : null, 
31651     
31652     
31653     getAutoCreate : function(){
31654         
31655         var cfg = {
31656             tag: this.tag,
31657             cls: 'blog-masonary-wrapper ' + this.cls,
31658             cn : {
31659                 cls : 'mas-boxes masonary'
31660             }
31661         };
31662         
31663         return cfg;
31664     },
31665     
31666     getChildContainer: function( )
31667     {
31668         if (this.boxesEl) {
31669             return this.boxesEl;
31670         }
31671         
31672         this.boxesEl = this.el.select('.mas-boxes').first();
31673         
31674         return this.boxesEl;
31675     },
31676     
31677     
31678     initEvents : function()
31679     {
31680         var _this = this;
31681         
31682         if(this.isAutoInitial){
31683             Roo.log('hook children rendered');
31684             this.on('childrenrendered', function() {
31685                 Roo.log('children rendered');
31686                 _this.initial();
31687             } ,this);
31688         }
31689         
31690     },
31691     
31692     initial : function()
31693     {
31694         this.reloadItems();
31695
31696         this.currentSize = this.el.getBox(true);
31697
31698         /// was window resize... - let's see if this works..
31699         Roo.EventManager.onWindowResize(this.resize, this); 
31700
31701         if(!this.isAutoInitial){
31702             this.layout();
31703             return;
31704         }
31705         
31706         this.layout.defer(500,this);
31707     },
31708     
31709     reloadItems: function()
31710     {
31711         this.bricks = this.el.select('.masonry-brick', true);
31712         
31713         this.bricks.each(function(b) {
31714             //Roo.log(b.getSize());
31715             if (!b.attr('originalwidth')) {
31716                 b.attr('originalwidth',  b.getSize().width);
31717             }
31718             
31719         });
31720         
31721         Roo.log(this.bricks.elements.length);
31722     },
31723     
31724     resize : function()
31725     {
31726         Roo.log('resize');
31727         var cs = this.el.getBox(true);
31728         
31729         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31730             Roo.log("no change in with or X");
31731             return;
31732         }
31733         this.currentSize = cs;
31734         this.layout();
31735     },
31736     
31737     layout : function()
31738     {
31739          Roo.log('layout');
31740         this._resetLayout();
31741         //this._manageStamps();
31742       
31743         // don't animate first layout
31744         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31745         this.layoutItems( isInstant );
31746       
31747         // flag for initalized
31748         this._isLayoutInited = true;
31749     },
31750     
31751     layoutItems : function( isInstant )
31752     {
31753         //var items = this._getItemsForLayout( this.items );
31754         // original code supports filtering layout items.. we just ignore it..
31755         
31756         this._layoutItems( this.bricks , isInstant );
31757       
31758         this._postLayout();
31759     },
31760     _layoutItems : function ( items , isInstant)
31761     {
31762        //this.fireEvent( 'layout', this, items );
31763     
31764
31765         if ( !items || !items.elements.length ) {
31766           // no items, emit event with empty array
31767             return;
31768         }
31769
31770         var queue = [];
31771         items.each(function(item) {
31772             Roo.log("layout item");
31773             Roo.log(item);
31774             // get x/y object from method
31775             var position = this._getItemLayoutPosition( item );
31776             // enqueue
31777             position.item = item;
31778             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31779             queue.push( position );
31780         }, this);
31781       
31782         this._processLayoutQueue( queue );
31783     },
31784     /** Sets position of item in DOM
31785     * @param {Element} item
31786     * @param {Number} x - horizontal position
31787     * @param {Number} y - vertical position
31788     * @param {Boolean} isInstant - disables transitions
31789     */
31790     _processLayoutQueue : function( queue )
31791     {
31792         for ( var i=0, len = queue.length; i < len; i++ ) {
31793             var obj = queue[i];
31794             obj.item.position('absolute');
31795             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31796         }
31797     },
31798       
31799     
31800     /**
31801     * Any logic you want to do after each layout,
31802     * i.e. size the container
31803     */
31804     _postLayout : function()
31805     {
31806         this.resizeContainer();
31807     },
31808     
31809     resizeContainer : function()
31810     {
31811         if ( !this.isResizingContainer ) {
31812             return;
31813         }
31814         var size = this._getContainerSize();
31815         if ( size ) {
31816             this.el.setSize(size.width,size.height);
31817             this.boxesEl.setSize(size.width,size.height);
31818         }
31819     },
31820     
31821     
31822     
31823     _resetLayout : function()
31824     {
31825         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31826         this.colWidth = this.el.getWidth();
31827         //this.gutter = this.el.getWidth(); 
31828         
31829         this.measureColumns();
31830
31831         // reset column Y
31832         var i = this.cols;
31833         this.colYs = [];
31834         while (i--) {
31835             this.colYs.push( 0 );
31836         }
31837     
31838         this.maxY = 0;
31839     },
31840
31841     measureColumns : function()
31842     {
31843         this.getContainerWidth();
31844       // if columnWidth is 0, default to outerWidth of first item
31845         if ( !this.columnWidth ) {
31846             var firstItem = this.bricks.first();
31847             Roo.log(firstItem);
31848             this.columnWidth  = this.containerWidth;
31849             if (firstItem && firstItem.attr('originalwidth') ) {
31850                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31851             }
31852             // columnWidth fall back to item of first element
31853             Roo.log("set column width?");
31854                         this.initialColumnWidth = this.columnWidth  ;
31855
31856             // if first elem has no width, default to size of container
31857             
31858         }
31859         
31860         
31861         if (this.initialColumnWidth) {
31862             this.columnWidth = this.initialColumnWidth;
31863         }
31864         
31865         
31866             
31867         // column width is fixed at the top - however if container width get's smaller we should
31868         // reduce it...
31869         
31870         // this bit calcs how man columns..
31871             
31872         var columnWidth = this.columnWidth += this.gutter;
31873       
31874         // calculate columns
31875         var containerWidth = this.containerWidth + this.gutter;
31876         
31877         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31878         // fix rounding errors, typically with gutters
31879         var excess = columnWidth - containerWidth % columnWidth;
31880         
31881         
31882         // if overshoot is less than a pixel, round up, otherwise floor it
31883         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31884         cols = Math[ mathMethod ]( cols );
31885         this.cols = Math.max( cols, 1 );
31886         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31887         
31888          // padding positioning..
31889         var totalColWidth = this.cols * this.columnWidth;
31890         var padavail = this.containerWidth - totalColWidth;
31891         // so for 2 columns - we need 3 'pads'
31892         
31893         var padNeeded = (1+this.cols) * this.padWidth;
31894         
31895         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31896         
31897         this.columnWidth += padExtra
31898         //this.padWidth = Math.floor(padavail /  ( this.cols));
31899         
31900         // adjust colum width so that padding is fixed??
31901         
31902         // we have 3 columns ... total = width * 3
31903         // we have X left over... that should be used by 
31904         
31905         //if (this.expandC) {
31906             
31907         //}
31908         
31909         
31910         
31911     },
31912     
31913     getContainerWidth : function()
31914     {
31915        /* // container is parent if fit width
31916         var container = this.isFitWidth ? this.element.parentNode : this.element;
31917         // check that this.size and size are there
31918         // IE8 triggers resize on body size change, so they might not be
31919         
31920         var size = getSize( container );  //FIXME
31921         this.containerWidth = size && size.innerWidth; //FIXME
31922         */
31923          
31924         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31925         
31926     },
31927     
31928     _getItemLayoutPosition : function( item )  // what is item?
31929     {
31930         // we resize the item to our columnWidth..
31931       
31932         item.setWidth(this.columnWidth);
31933         item.autoBoxAdjust  = false;
31934         
31935         var sz = item.getSize();
31936  
31937         // how many columns does this brick span
31938         var remainder = this.containerWidth % this.columnWidth;
31939         
31940         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31941         // round if off by 1 pixel, otherwise use ceil
31942         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31943         colSpan = Math.min( colSpan, this.cols );
31944         
31945         // normally this should be '1' as we dont' currently allow multi width columns..
31946         
31947         var colGroup = this._getColGroup( colSpan );
31948         // get the minimum Y value from the columns
31949         var minimumY = Math.min.apply( Math, colGroup );
31950         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31951         
31952         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31953          
31954         // position the brick
31955         var position = {
31956             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31957             y: this.currentSize.y + minimumY + this.padHeight
31958         };
31959         
31960         Roo.log(position);
31961         // apply setHeight to necessary columns
31962         var setHeight = minimumY + sz.height + this.padHeight;
31963         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31964         
31965         var setSpan = this.cols + 1 - colGroup.length;
31966         for ( var i = 0; i < setSpan; i++ ) {
31967           this.colYs[ shortColIndex + i ] = setHeight ;
31968         }
31969       
31970         return position;
31971     },
31972     
31973     /**
31974      * @param {Number} colSpan - number of columns the element spans
31975      * @returns {Array} colGroup
31976      */
31977     _getColGroup : function( colSpan )
31978     {
31979         if ( colSpan < 2 ) {
31980           // if brick spans only one column, use all the column Ys
31981           return this.colYs;
31982         }
31983       
31984         var colGroup = [];
31985         // how many different places could this brick fit horizontally
31986         var groupCount = this.cols + 1 - colSpan;
31987         // for each group potential horizontal position
31988         for ( var i = 0; i < groupCount; i++ ) {
31989           // make an array of colY values for that one group
31990           var groupColYs = this.colYs.slice( i, i + colSpan );
31991           // and get the max value of the array
31992           colGroup[i] = Math.max.apply( Math, groupColYs );
31993         }
31994         return colGroup;
31995     },
31996     /*
31997     _manageStamp : function( stamp )
31998     {
31999         var stampSize =  stamp.getSize();
32000         var offset = stamp.getBox();
32001         // get the columns that this stamp affects
32002         var firstX = this.isOriginLeft ? offset.x : offset.right;
32003         var lastX = firstX + stampSize.width;
32004         var firstCol = Math.floor( firstX / this.columnWidth );
32005         firstCol = Math.max( 0, firstCol );
32006         
32007         var lastCol = Math.floor( lastX / this.columnWidth );
32008         // lastCol should not go over if multiple of columnWidth #425
32009         lastCol -= lastX % this.columnWidth ? 0 : 1;
32010         lastCol = Math.min( this.cols - 1, lastCol );
32011         
32012         // set colYs to bottom of the stamp
32013         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32014             stampSize.height;
32015             
32016         for ( var i = firstCol; i <= lastCol; i++ ) {
32017           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32018         }
32019     },
32020     */
32021     
32022     _getContainerSize : function()
32023     {
32024         this.maxY = Math.max.apply( Math, this.colYs );
32025         var size = {
32026             height: this.maxY
32027         };
32028       
32029         if ( this.isFitWidth ) {
32030             size.width = this._getContainerFitWidth();
32031         }
32032       
32033         return size;
32034     },
32035     
32036     _getContainerFitWidth : function()
32037     {
32038         var unusedCols = 0;
32039         // count unused columns
32040         var i = this.cols;
32041         while ( --i ) {
32042           if ( this.colYs[i] !== 0 ) {
32043             break;
32044           }
32045           unusedCols++;
32046         }
32047         // fit container to columns that have been used
32048         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32049     },
32050     
32051     needsResizeLayout : function()
32052     {
32053         var previousWidth = this.containerWidth;
32054         this.getContainerWidth();
32055         return previousWidth !== this.containerWidth;
32056     }
32057  
32058 });
32059
32060  
32061
32062  /*
32063  * - LGPL
32064  *
32065  * element
32066  * 
32067  */
32068
32069 /**
32070  * @class Roo.bootstrap.MasonryBrick
32071  * @extends Roo.bootstrap.Component
32072  * Bootstrap MasonryBrick class
32073  * 
32074  * @constructor
32075  * Create a new MasonryBrick
32076  * @param {Object} config The config object
32077  */
32078
32079 Roo.bootstrap.MasonryBrick = function(config){
32080     
32081     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32082     
32083     Roo.bootstrap.MasonryBrick.register(this);
32084     
32085     this.addEvents({
32086         // raw events
32087         /**
32088          * @event click
32089          * When a MasonryBrick is clcik
32090          * @param {Roo.bootstrap.MasonryBrick} this
32091          * @param {Roo.EventObject} e
32092          */
32093         "click" : true
32094     });
32095 };
32096
32097 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32098     
32099     /**
32100      * @cfg {String} title
32101      */   
32102     title : '',
32103     /**
32104      * @cfg {String} html
32105      */   
32106     html : '',
32107     /**
32108      * @cfg {String} bgimage
32109      */   
32110     bgimage : '',
32111     /**
32112      * @cfg {String} videourl
32113      */   
32114     videourl : '',
32115     /**
32116      * @cfg {String} cls
32117      */   
32118     cls : '',
32119     /**
32120      * @cfg {String} href
32121      */   
32122     href : '',
32123     /**
32124      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32125      */   
32126     size : 'xs',
32127     
32128     /**
32129      * @cfg {String} placetitle (center|bottom)
32130      */   
32131     placetitle : '',
32132     
32133     /**
32134      * @cfg {Boolean} isFitContainer defalut true
32135      */   
32136     isFitContainer : true, 
32137     
32138     /**
32139      * @cfg {Boolean} preventDefault defalut false
32140      */   
32141     preventDefault : false, 
32142     
32143     /**
32144      * @cfg {Boolean} inverse defalut false
32145      */   
32146     maskInverse : false, 
32147     
32148     getAutoCreate : function()
32149     {
32150         if(!this.isFitContainer){
32151             return this.getSplitAutoCreate();
32152         }
32153         
32154         var cls = 'masonry-brick masonry-brick-full';
32155         
32156         if(this.href.length){
32157             cls += ' masonry-brick-link';
32158         }
32159         
32160         if(this.bgimage.length){
32161             cls += ' masonry-brick-image';
32162         }
32163         
32164         if(this.maskInverse){
32165             cls += ' mask-inverse';
32166         }
32167         
32168         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32169             cls += ' enable-mask';
32170         }
32171         
32172         if(this.size){
32173             cls += ' masonry-' + this.size + '-brick';
32174         }
32175         
32176         if(this.placetitle.length){
32177             
32178             switch (this.placetitle) {
32179                 case 'center' :
32180                     cls += ' masonry-center-title';
32181                     break;
32182                 case 'bottom' :
32183                     cls += ' masonry-bottom-title';
32184                     break;
32185                 default:
32186                     break;
32187             }
32188             
32189         } else {
32190             if(!this.html.length && !this.bgimage.length){
32191                 cls += ' masonry-center-title';
32192             }
32193
32194             if(!this.html.length && this.bgimage.length){
32195                 cls += ' masonry-bottom-title';
32196             }
32197         }
32198         
32199         if(this.cls){
32200             cls += ' ' + this.cls;
32201         }
32202         
32203         var cfg = {
32204             tag: (this.href.length) ? 'a' : 'div',
32205             cls: cls,
32206             cn: [
32207                 {
32208                     tag: 'div',
32209                     cls: 'masonry-brick-mask'
32210                 },
32211                 {
32212                     tag: 'div',
32213                     cls: 'masonry-brick-paragraph',
32214                     cn: []
32215                 }
32216             ]
32217         };
32218         
32219         if(this.href.length){
32220             cfg.href = this.href;
32221         }
32222         
32223         var cn = cfg.cn[1].cn;
32224         
32225         if(this.title.length){
32226             cn.push({
32227                 tag: 'h4',
32228                 cls: 'masonry-brick-title',
32229                 html: this.title
32230             });
32231         }
32232         
32233         if(this.html.length){
32234             cn.push({
32235                 tag: 'p',
32236                 cls: 'masonry-brick-text',
32237                 html: this.html
32238             });
32239         }
32240         
32241         if (!this.title.length && !this.html.length) {
32242             cfg.cn[1].cls += ' hide';
32243         }
32244         
32245         if(this.bgimage.length){
32246             cfg.cn.push({
32247                 tag: 'img',
32248                 cls: 'masonry-brick-image-view',
32249                 src: this.bgimage
32250             });
32251         }
32252         
32253         if(this.videourl.length){
32254             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32255             // youtube support only?
32256             cfg.cn.push({
32257                 tag: 'iframe',
32258                 cls: 'masonry-brick-image-view',
32259                 src: vurl,
32260                 frameborder : 0,
32261                 allowfullscreen : true
32262             });
32263         }
32264         
32265         return cfg;
32266         
32267     },
32268     
32269     getSplitAutoCreate : function()
32270     {
32271         var cls = 'masonry-brick masonry-brick-split';
32272         
32273         if(this.href.length){
32274             cls += ' masonry-brick-link';
32275         }
32276         
32277         if(this.bgimage.length){
32278             cls += ' masonry-brick-image';
32279         }
32280         
32281         if(this.size){
32282             cls += ' masonry-' + this.size + '-brick';
32283         }
32284         
32285         switch (this.placetitle) {
32286             case 'center' :
32287                 cls += ' masonry-center-title';
32288                 break;
32289             case 'bottom' :
32290                 cls += ' masonry-bottom-title';
32291                 break;
32292             default:
32293                 if(!this.bgimage.length){
32294                     cls += ' masonry-center-title';
32295                 }
32296
32297                 if(this.bgimage.length){
32298                     cls += ' masonry-bottom-title';
32299                 }
32300                 break;
32301         }
32302         
32303         if(this.cls){
32304             cls += ' ' + this.cls;
32305         }
32306         
32307         var cfg = {
32308             tag: (this.href.length) ? 'a' : 'div',
32309             cls: cls,
32310             cn: [
32311                 {
32312                     tag: 'div',
32313                     cls: 'masonry-brick-split-head',
32314                     cn: [
32315                         {
32316                             tag: 'div',
32317                             cls: 'masonry-brick-paragraph',
32318                             cn: []
32319                         }
32320                     ]
32321                 },
32322                 {
32323                     tag: 'div',
32324                     cls: 'masonry-brick-split-body',
32325                     cn: []
32326                 }
32327             ]
32328         };
32329         
32330         if(this.href.length){
32331             cfg.href = this.href;
32332         }
32333         
32334         if(this.title.length){
32335             cfg.cn[0].cn[0].cn.push({
32336                 tag: 'h4',
32337                 cls: 'masonry-brick-title',
32338                 html: this.title
32339             });
32340         }
32341         
32342         if(this.html.length){
32343             cfg.cn[1].cn.push({
32344                 tag: 'p',
32345                 cls: 'masonry-brick-text',
32346                 html: this.html
32347             });
32348         }
32349
32350         if(this.bgimage.length){
32351             cfg.cn[0].cn.push({
32352                 tag: 'img',
32353                 cls: 'masonry-brick-image-view',
32354                 src: this.bgimage
32355             });
32356         }
32357         
32358         if(this.videourl.length){
32359             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32360             // youtube support only?
32361             cfg.cn[0].cn.cn.push({
32362                 tag: 'iframe',
32363                 cls: 'masonry-brick-image-view',
32364                 src: vurl,
32365                 frameborder : 0,
32366                 allowfullscreen : true
32367             });
32368         }
32369         
32370         return cfg;
32371     },
32372     
32373     initEvents: function() 
32374     {
32375         switch (this.size) {
32376             case 'xs' :
32377                 this.x = 1;
32378                 this.y = 1;
32379                 break;
32380             case 'sm' :
32381                 this.x = 2;
32382                 this.y = 2;
32383                 break;
32384             case 'md' :
32385             case 'md-left' :
32386             case 'md-right' :
32387                 this.x = 3;
32388                 this.y = 3;
32389                 break;
32390             case 'tall' :
32391                 this.x = 2;
32392                 this.y = 3;
32393                 break;
32394             case 'wide' :
32395                 this.x = 3;
32396                 this.y = 2;
32397                 break;
32398             case 'wide-thin' :
32399                 this.x = 3;
32400                 this.y = 1;
32401                 break;
32402                         
32403             default :
32404                 break;
32405         }
32406         
32407         if(Roo.isTouch){
32408             this.el.on('touchstart', this.onTouchStart, this);
32409             this.el.on('touchmove', this.onTouchMove, this);
32410             this.el.on('touchend', this.onTouchEnd, this);
32411             this.el.on('contextmenu', this.onContextMenu, this);
32412         } else {
32413             this.el.on('mouseenter'  ,this.enter, this);
32414             this.el.on('mouseleave', this.leave, this);
32415             this.el.on('click', this.onClick, this);
32416         }
32417         
32418         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32419             this.parent().bricks.push(this);   
32420         }
32421         
32422     },
32423     
32424     onClick: function(e, el)
32425     {
32426         var time = this.endTimer - this.startTimer;
32427         // Roo.log(e.preventDefault());
32428         if(Roo.isTouch){
32429             if(time > 1000){
32430                 e.preventDefault();
32431                 return;
32432             }
32433         }
32434         
32435         if(!this.preventDefault){
32436             return;
32437         }
32438         
32439         e.preventDefault();
32440         
32441         if (this.activcClass != '') {
32442             this.selectBrick();
32443         }
32444         
32445         this.fireEvent('click', this);
32446     },
32447     
32448     enter: function(e, el)
32449     {
32450         e.preventDefault();
32451         
32452         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32453             return;
32454         }
32455         
32456         if(this.bgimage.length && this.html.length){
32457             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32458         }
32459     },
32460     
32461     leave: function(e, el)
32462     {
32463         e.preventDefault();
32464         
32465         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32466             return;
32467         }
32468         
32469         if(this.bgimage.length && this.html.length){
32470             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32471         }
32472     },
32473     
32474     onTouchStart: function(e, el)
32475     {
32476 //        e.preventDefault();
32477         
32478         this.touchmoved = false;
32479         
32480         if(!this.isFitContainer){
32481             return;
32482         }
32483         
32484         if(!this.bgimage.length || !this.html.length){
32485             return;
32486         }
32487         
32488         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32489         
32490         this.timer = new Date().getTime();
32491         
32492     },
32493     
32494     onTouchMove: function(e, el)
32495     {
32496         this.touchmoved = true;
32497     },
32498     
32499     onContextMenu : function(e,el)
32500     {
32501         e.preventDefault();
32502         e.stopPropagation();
32503         return false;
32504     },
32505     
32506     onTouchEnd: function(e, el)
32507     {
32508 //        e.preventDefault();
32509         
32510         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32511         
32512             this.leave(e,el);
32513             
32514             return;
32515         }
32516         
32517         if(!this.bgimage.length || !this.html.length){
32518             
32519             if(this.href.length){
32520                 window.location.href = this.href;
32521             }
32522             
32523             return;
32524         }
32525         
32526         if(!this.isFitContainer){
32527             return;
32528         }
32529         
32530         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32531         
32532         window.location.href = this.href;
32533     },
32534     
32535     //selection on single brick only
32536     selectBrick : function() {
32537         
32538         if (!this.parentId) {
32539             return;
32540         }
32541         
32542         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32543         var index = m.selectedBrick.indexOf(this.id);
32544         
32545         if ( index > -1) {
32546             m.selectedBrick.splice(index,1);
32547             this.el.removeClass(this.activeClass);
32548             return;
32549         }
32550         
32551         for(var i = 0; i < m.selectedBrick.length; i++) {
32552             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32553             b.el.removeClass(b.activeClass);
32554         }
32555         
32556         m.selectedBrick = [];
32557         
32558         m.selectedBrick.push(this.id);
32559         this.el.addClass(this.activeClass);
32560         return;
32561     }
32562     
32563 });
32564
32565 Roo.apply(Roo.bootstrap.MasonryBrick, {
32566     
32567     //groups: {},
32568     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32569      /**
32570     * register a Masonry Brick
32571     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32572     */
32573     
32574     register : function(brick)
32575     {
32576         //this.groups[brick.id] = brick;
32577         this.groups.add(brick.id, brick);
32578     },
32579     /**
32580     * fetch a  masonry brick based on the masonry brick ID
32581     * @param {string} the masonry brick to add
32582     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32583     */
32584     
32585     get: function(brick_id) 
32586     {
32587         // if (typeof(this.groups[brick_id]) == 'undefined') {
32588         //     return false;
32589         // }
32590         // return this.groups[brick_id] ;
32591         
32592         if(this.groups.key(brick_id)) {
32593             return this.groups.key(brick_id);
32594         }
32595         
32596         return false;
32597     }
32598     
32599     
32600     
32601 });
32602
32603  /*
32604  * - LGPL
32605  *
32606  * element
32607  * 
32608  */
32609
32610 /**
32611  * @class Roo.bootstrap.Brick
32612  * @extends Roo.bootstrap.Component
32613  * Bootstrap Brick class
32614  * 
32615  * @constructor
32616  * Create a new Brick
32617  * @param {Object} config The config object
32618  */
32619
32620 Roo.bootstrap.Brick = function(config){
32621     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32622     
32623     this.addEvents({
32624         // raw events
32625         /**
32626          * @event click
32627          * When a Brick is click
32628          * @param {Roo.bootstrap.Brick} this
32629          * @param {Roo.EventObject} e
32630          */
32631         "click" : true
32632     });
32633 };
32634
32635 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32636     
32637     /**
32638      * @cfg {String} title
32639      */   
32640     title : '',
32641     /**
32642      * @cfg {String} html
32643      */   
32644     html : '',
32645     /**
32646      * @cfg {String} bgimage
32647      */   
32648     bgimage : '',
32649     /**
32650      * @cfg {String} cls
32651      */   
32652     cls : '',
32653     /**
32654      * @cfg {String} href
32655      */   
32656     href : '',
32657     /**
32658      * @cfg {String} video
32659      */   
32660     video : '',
32661     /**
32662      * @cfg {Boolean} square
32663      */   
32664     square : true,
32665     
32666     getAutoCreate : function()
32667     {
32668         var cls = 'roo-brick';
32669         
32670         if(this.href.length){
32671             cls += ' roo-brick-link';
32672         }
32673         
32674         if(this.bgimage.length){
32675             cls += ' roo-brick-image';
32676         }
32677         
32678         if(!this.html.length && !this.bgimage.length){
32679             cls += ' roo-brick-center-title';
32680         }
32681         
32682         if(!this.html.length && this.bgimage.length){
32683             cls += ' roo-brick-bottom-title';
32684         }
32685         
32686         if(this.cls){
32687             cls += ' ' + this.cls;
32688         }
32689         
32690         var cfg = {
32691             tag: (this.href.length) ? 'a' : 'div',
32692             cls: cls,
32693             cn: [
32694                 {
32695                     tag: 'div',
32696                     cls: 'roo-brick-paragraph',
32697                     cn: []
32698                 }
32699             ]
32700         };
32701         
32702         if(this.href.length){
32703             cfg.href = this.href;
32704         }
32705         
32706         var cn = cfg.cn[0].cn;
32707         
32708         if(this.title.length){
32709             cn.push({
32710                 tag: 'h4',
32711                 cls: 'roo-brick-title',
32712                 html: this.title
32713             });
32714         }
32715         
32716         if(this.html.length){
32717             cn.push({
32718                 tag: 'p',
32719                 cls: 'roo-brick-text',
32720                 html: this.html
32721             });
32722         } else {
32723             cn.cls += ' hide';
32724         }
32725         
32726         if(this.bgimage.length){
32727             cfg.cn.push({
32728                 tag: 'img',
32729                 cls: 'roo-brick-image-view',
32730                 src: this.bgimage
32731             });
32732         }
32733         
32734         return cfg;
32735     },
32736     
32737     initEvents: function() 
32738     {
32739         if(this.title.length || this.html.length){
32740             this.el.on('mouseenter'  ,this.enter, this);
32741             this.el.on('mouseleave', this.leave, this);
32742         }
32743         
32744         Roo.EventManager.onWindowResize(this.resize, this); 
32745         
32746         if(this.bgimage.length){
32747             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32748             this.imageEl.on('load', this.onImageLoad, this);
32749             return;
32750         }
32751         
32752         this.resize();
32753     },
32754     
32755     onImageLoad : function()
32756     {
32757         this.resize();
32758     },
32759     
32760     resize : function()
32761     {
32762         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32763         
32764         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32765         
32766         if(this.bgimage.length){
32767             var image = this.el.select('.roo-brick-image-view', true).first();
32768             
32769             image.setWidth(paragraph.getWidth());
32770             
32771             if(this.square){
32772                 image.setHeight(paragraph.getWidth());
32773             }
32774             
32775             this.el.setHeight(image.getHeight());
32776             paragraph.setHeight(image.getHeight());
32777             
32778         }
32779         
32780     },
32781     
32782     enter: function(e, el)
32783     {
32784         e.preventDefault();
32785         
32786         if(this.bgimage.length){
32787             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32788             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32789         }
32790     },
32791     
32792     leave: function(e, el)
32793     {
32794         e.preventDefault();
32795         
32796         if(this.bgimage.length){
32797             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32798             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32799         }
32800     }
32801     
32802 });
32803
32804  
32805
32806  /*
32807  * - LGPL
32808  *
32809  * Input
32810  * 
32811  */
32812
32813 /**
32814  * @class Roo.bootstrap.NumberField
32815  * @extends Roo.bootstrap.Input
32816  * Bootstrap NumberField class
32817  * 
32818  * 
32819  * 
32820  * 
32821  * @constructor
32822  * Create a new NumberField
32823  * @param {Object} config The config object
32824  */
32825
32826 Roo.bootstrap.NumberField = function(config){
32827     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32828 };
32829
32830 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32831     
32832     /**
32833      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32834      */
32835     allowDecimals : true,
32836     /**
32837      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32838      */
32839     decimalSeparator : ".",
32840     /**
32841      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32842      */
32843     decimalPrecision : 2,
32844     /**
32845      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32846      */
32847     allowNegative : true,
32848     /**
32849      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32850      */
32851     minValue : Number.NEGATIVE_INFINITY,
32852     /**
32853      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32854      */
32855     maxValue : Number.MAX_VALUE,
32856     /**
32857      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32858      */
32859     minText : "The minimum value for this field is {0}",
32860     /**
32861      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32862      */
32863     maxText : "The maximum value for this field is {0}",
32864     /**
32865      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32866      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32867      */
32868     nanText : "{0} is not a valid number",
32869     /**
32870      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32871      */
32872     castInt : true,
32873
32874     // private
32875     initEvents : function()
32876     {   
32877         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32878         
32879         var allowed = "0123456789";
32880         
32881         if(this.allowDecimals){
32882             allowed += this.decimalSeparator;
32883         }
32884         
32885         if(this.allowNegative){
32886             allowed += "-";
32887         }
32888         
32889         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32890         
32891         var keyPress = function(e){
32892             
32893             var k = e.getKey();
32894             
32895             var c = e.getCharCode();
32896             
32897             if(
32898                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32899                     allowed.indexOf(String.fromCharCode(c)) === -1
32900             ){
32901                 e.stopEvent();
32902                 return;
32903             }
32904             
32905             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32906                 return;
32907             }
32908             
32909             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32910                 e.stopEvent();
32911             }
32912         };
32913         
32914         this.el.on("keypress", keyPress, this);
32915     },
32916     
32917     validateValue : function(value)
32918     {
32919         
32920         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32921             return false;
32922         }
32923         
32924         var num = this.parseValue(value);
32925         
32926         if(isNaN(num)){
32927             this.markInvalid(String.format(this.nanText, value));
32928             return false;
32929         }
32930         
32931         if(num < this.minValue){
32932             this.markInvalid(String.format(this.minText, this.minValue));
32933             return false;
32934         }
32935         
32936         if(num > this.maxValue){
32937             this.markInvalid(String.format(this.maxText, this.maxValue));
32938             return false;
32939         }
32940         
32941         return true;
32942     },
32943
32944     getValue : function()
32945     {
32946         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32947     },
32948
32949     parseValue : function(value)
32950     {
32951         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32952         return isNaN(value) ? '' : value;
32953     },
32954
32955     fixPrecision : function(value)
32956     {
32957         var nan = isNaN(value);
32958         
32959         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32960             return nan ? '' : value;
32961         }
32962         return parseFloat(value).toFixed(this.decimalPrecision);
32963     },
32964
32965     setValue : function(v)
32966     {
32967         v = this.fixPrecision(v);
32968         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32969     },
32970
32971     decimalPrecisionFcn : function(v)
32972     {
32973         return Math.floor(v);
32974     },
32975
32976     beforeBlur : function()
32977     {
32978         if(!this.castInt){
32979             return;
32980         }
32981         
32982         var v = this.parseValue(this.getRawValue());
32983         if(v){
32984             this.setValue(v);
32985         }
32986     }
32987     
32988 });
32989
32990  
32991
32992 /*
32993 * Licence: LGPL
32994 */
32995
32996 /**
32997  * @class Roo.bootstrap.DocumentSlider
32998  * @extends Roo.bootstrap.Component
32999  * Bootstrap DocumentSlider class
33000  * 
33001  * @constructor
33002  * Create a new DocumentViewer
33003  * @param {Object} config The config object
33004  */
33005
33006 Roo.bootstrap.DocumentSlider = function(config){
33007     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33008     
33009     this.files = [];
33010     
33011     this.addEvents({
33012         /**
33013          * @event initial
33014          * Fire after initEvent
33015          * @param {Roo.bootstrap.DocumentSlider} this
33016          */
33017         "initial" : true,
33018         /**
33019          * @event update
33020          * Fire after update
33021          * @param {Roo.bootstrap.DocumentSlider} this
33022          */
33023         "update" : true,
33024         /**
33025          * @event click
33026          * Fire after click
33027          * @param {Roo.bootstrap.DocumentSlider} this
33028          */
33029         "click" : true
33030     });
33031 };
33032
33033 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33034     
33035     files : false,
33036     
33037     indicator : 0,
33038     
33039     getAutoCreate : function()
33040     {
33041         var cfg = {
33042             tag : 'div',
33043             cls : 'roo-document-slider',
33044             cn : [
33045                 {
33046                     tag : 'div',
33047                     cls : 'roo-document-slider-header',
33048                     cn : [
33049                         {
33050                             tag : 'div',
33051                             cls : 'roo-document-slider-header-title'
33052                         }
33053                     ]
33054                 },
33055                 {
33056                     tag : 'div',
33057                     cls : 'roo-document-slider-body',
33058                     cn : [
33059                         {
33060                             tag : 'div',
33061                             cls : 'roo-document-slider-prev',
33062                             cn : [
33063                                 {
33064                                     tag : 'i',
33065                                     cls : 'fa fa-chevron-left'
33066                                 }
33067                             ]
33068                         },
33069                         {
33070                             tag : 'div',
33071                             cls : 'roo-document-slider-thumb',
33072                             cn : [
33073                                 {
33074                                     tag : 'img',
33075                                     cls : 'roo-document-slider-image'
33076                                 }
33077                             ]
33078                         },
33079                         {
33080                             tag : 'div',
33081                             cls : 'roo-document-slider-next',
33082                             cn : [
33083                                 {
33084                                     tag : 'i',
33085                                     cls : 'fa fa-chevron-right'
33086                                 }
33087                             ]
33088                         }
33089                     ]
33090                 }
33091             ]
33092         };
33093         
33094         return cfg;
33095     },
33096     
33097     initEvents : function()
33098     {
33099         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33100         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33101         
33102         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33103         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33104         
33105         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33106         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33107         
33108         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33109         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33110         
33111         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33112         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33113         
33114         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33115         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33116         
33117         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33118         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33119         
33120         this.thumbEl.on('click', this.onClick, this);
33121         
33122         this.prevIndicator.on('click', this.prev, this);
33123         
33124         this.nextIndicator.on('click', this.next, this);
33125         
33126     },
33127     
33128     initial : function()
33129     {
33130         if(this.files.length){
33131             this.indicator = 1;
33132             this.update()
33133         }
33134         
33135         this.fireEvent('initial', this);
33136     },
33137     
33138     update : function()
33139     {
33140         this.imageEl.attr('src', this.files[this.indicator - 1]);
33141         
33142         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33143         
33144         this.prevIndicator.show();
33145         
33146         if(this.indicator == 1){
33147             this.prevIndicator.hide();
33148         }
33149         
33150         this.nextIndicator.show();
33151         
33152         if(this.indicator == this.files.length){
33153             this.nextIndicator.hide();
33154         }
33155         
33156         this.thumbEl.scrollTo('top');
33157         
33158         this.fireEvent('update', this);
33159     },
33160     
33161     onClick : function(e)
33162     {
33163         e.preventDefault();
33164         
33165         this.fireEvent('click', this);
33166     },
33167     
33168     prev : function(e)
33169     {
33170         e.preventDefault();
33171         
33172         this.indicator = Math.max(1, this.indicator - 1);
33173         
33174         this.update();
33175     },
33176     
33177     next : function(e)
33178     {
33179         e.preventDefault();
33180         
33181         this.indicator = Math.min(this.files.length, this.indicator + 1);
33182         
33183         this.update();
33184     }
33185 });
33186 /*
33187  * - LGPL
33188  *
33189  * RadioSet
33190  *
33191  *
33192  */
33193
33194 /**
33195  * @class Roo.bootstrap.RadioSet
33196  * @extends Roo.bootstrap.Input
33197  * Bootstrap RadioSet class
33198  * @cfg {String} indicatorpos (left|right) default left
33199  * @cfg {Boolean} inline (true|false) inline the element (default true)
33200  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33201  * @constructor
33202  * Create a new RadioSet
33203  * @param {Object} config The config object
33204  */
33205
33206 Roo.bootstrap.RadioSet = function(config){
33207     
33208     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33209     
33210     this.radioes = [];
33211     
33212     Roo.bootstrap.RadioSet.register(this);
33213     
33214     this.addEvents({
33215         /**
33216         * @event check
33217         * Fires when the element is checked or unchecked.
33218         * @param {Roo.bootstrap.RadioSet} this This radio
33219         * @param {Roo.bootstrap.Radio} item The checked item
33220         */
33221        check : true
33222     });
33223     
33224 };
33225
33226 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33227
33228     radioes : false,
33229     
33230     inline : true,
33231     
33232     weight : '',
33233     
33234     indicatorpos : 'left',
33235     
33236     getAutoCreate : function()
33237     {
33238         var label = {
33239             tag : 'label',
33240             cls : 'roo-radio-set-label',
33241             cn : [
33242                 {
33243                     tag : 'span',
33244                     html : this.fieldLabel
33245                 }
33246             ]
33247         };
33248         
33249         if(this.indicatorpos == 'left'){
33250             label.cn.unshift({
33251                 tag : 'i',
33252                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33253                 tooltip : 'This field is required'
33254             });
33255         } else {
33256             label.cn.push({
33257                 tag : 'i',
33258                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33259                 tooltip : 'This field is required'
33260             });
33261         }
33262         
33263         var items = {
33264             tag : 'div',
33265             cls : 'roo-radio-set-items'
33266         };
33267         
33268         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33269         
33270         if (align === 'left' && this.fieldLabel.length) {
33271             
33272             items = {
33273                 cls : "roo-radio-set-right", 
33274                 cn: [
33275                     items
33276                 ]
33277             };
33278             
33279             if(this.labelWidth > 12){
33280                 label.style = "width: " + this.labelWidth + 'px';
33281             }
33282             
33283             if(this.labelWidth < 13 && this.labelmd == 0){
33284                 this.labelmd = this.labelWidth;
33285             }
33286             
33287             if(this.labellg > 0){
33288                 label.cls += ' col-lg-' + this.labellg;
33289                 items.cls += ' col-lg-' + (12 - this.labellg);
33290             }
33291             
33292             if(this.labelmd > 0){
33293                 label.cls += ' col-md-' + this.labelmd;
33294                 items.cls += ' col-md-' + (12 - this.labelmd);
33295             }
33296             
33297             if(this.labelsm > 0){
33298                 label.cls += ' col-sm-' + this.labelsm;
33299                 items.cls += ' col-sm-' + (12 - this.labelsm);
33300             }
33301             
33302             if(this.labelxs > 0){
33303                 label.cls += ' col-xs-' + this.labelxs;
33304                 items.cls += ' col-xs-' + (12 - this.labelxs);
33305             }
33306         }
33307         
33308         var cfg = {
33309             tag : 'div',
33310             cls : 'roo-radio-set',
33311             cn : [
33312                 {
33313                     tag : 'input',
33314                     cls : 'roo-radio-set-input',
33315                     type : 'hidden',
33316                     name : this.name,
33317                     value : this.value ? this.value :  ''
33318                 },
33319                 label,
33320                 items
33321             ]
33322         };
33323         
33324         if(this.weight.length){
33325             cfg.cls += ' roo-radio-' + this.weight;
33326         }
33327         
33328         if(this.inline) {
33329             cfg.cls += ' roo-radio-set-inline';
33330         }
33331         
33332         var settings=this;
33333         ['xs','sm','md','lg'].map(function(size){
33334             if (settings[size]) {
33335                 cfg.cls += ' col-' + size + '-' + settings[size];
33336             }
33337         });
33338         
33339         return cfg;
33340         
33341     },
33342
33343     initEvents : function()
33344     {
33345         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33346         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33347         
33348         if(!this.fieldLabel.length){
33349             this.labelEl.hide();
33350         }
33351         
33352         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33353         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33354         
33355         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33356         this.indicatorEl().hide();
33357         
33358         this.originalValue = this.getValue();
33359         
33360     },
33361     
33362     inputEl: function ()
33363     {
33364         return this.el.select('.roo-radio-set-input', true).first();
33365     },
33366     
33367     getChildContainer : function()
33368     {
33369         return this.itemsEl;
33370     },
33371     
33372     register : function(item)
33373     {
33374         this.radioes.push(item);
33375         
33376     },
33377     
33378     validate : function()
33379     {   
33380         var valid = false;
33381         
33382         Roo.each(this.radioes, function(i){
33383             if(!i.checked){
33384                 return;
33385             }
33386             
33387             valid = true;
33388             return false;
33389         });
33390         
33391         if(this.allowBlank) {
33392             return true;
33393         }
33394         
33395         if(this.disabled || valid){
33396             this.markValid();
33397             return true;
33398         }
33399         
33400         this.markInvalid();
33401         return false;
33402         
33403     },
33404     
33405     markValid : function()
33406     {
33407         if(this.labelEl.isVisible(true)){
33408             this.indicatorEl().hide();
33409         }
33410         
33411         this.el.removeClass([this.invalidClass, this.validClass]);
33412         this.el.addClass(this.validClass);
33413         
33414         this.fireEvent('valid', this);
33415     },
33416     
33417     markInvalid : function(msg)
33418     {
33419         if(this.allowBlank || this.disabled){
33420             return;
33421         }
33422         
33423         if(this.labelEl.isVisible(true)){
33424             this.indicatorEl().show();
33425         }
33426         
33427         this.el.removeClass([this.invalidClass, this.validClass]);
33428         this.el.addClass(this.invalidClass);
33429         
33430         this.fireEvent('invalid', this, msg);
33431         
33432     },
33433     
33434     setValue : function(v, suppressEvent)
33435     {   
33436         this.value = v;
33437         if(this.rendered){
33438             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33439         }
33440         
33441         Roo.each(this.radioes, function(i){
33442             
33443             i.checked = false;
33444             i.el.removeClass('checked');
33445             
33446             if(i.value === v || i.value.toString() === v.toString()){
33447                 i.checked = true;
33448                 i.el.addClass('checked');
33449                 
33450                 if(suppressEvent !== true){
33451                     this.fireEvent('check', this, i);
33452                 }
33453             }
33454             
33455         }, this);
33456         
33457         this.validate();
33458     },
33459     
33460     clearInvalid : function(){
33461         
33462         if(!this.el || this.preventMark){
33463             return;
33464         }
33465         
33466         this.el.removeClass([this.invalidClass]);
33467         
33468         this.fireEvent('valid', this);
33469     }
33470     
33471 });
33472
33473 Roo.apply(Roo.bootstrap.RadioSet, {
33474     
33475     groups: {},
33476     
33477     register : function(set)
33478     {
33479         this.groups[set.name] = set;
33480     },
33481     
33482     get: function(name) 
33483     {
33484         if (typeof(this.groups[name]) == 'undefined') {
33485             return false;
33486         }
33487         
33488         return this.groups[name] ;
33489     }
33490     
33491 });
33492 /*
33493  * Based on:
33494  * Ext JS Library 1.1.1
33495  * Copyright(c) 2006-2007, Ext JS, LLC.
33496  *
33497  * Originally Released Under LGPL - original licence link has changed is not relivant.
33498  *
33499  * Fork - LGPL
33500  * <script type="text/javascript">
33501  */
33502
33503
33504 /**
33505  * @class Roo.bootstrap.SplitBar
33506  * @extends Roo.util.Observable
33507  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33508  * <br><br>
33509  * Usage:
33510  * <pre><code>
33511 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33512                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33513 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33514 split.minSize = 100;
33515 split.maxSize = 600;
33516 split.animate = true;
33517 split.on('moved', splitterMoved);
33518 </code></pre>
33519  * @constructor
33520  * Create a new SplitBar
33521  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33522  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33523  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33524  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33525                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33526                         position of the SplitBar).
33527  */
33528 Roo.bootstrap.SplitBar = function(cfg){
33529     
33530     /** @private */
33531     
33532     //{
33533     //  dragElement : elm
33534     //  resizingElement: el,
33535         // optional..
33536     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33537     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33538         // existingProxy ???
33539     //}
33540     
33541     this.el = Roo.get(cfg.dragElement, true);
33542     this.el.dom.unselectable = "on";
33543     /** @private */
33544     this.resizingEl = Roo.get(cfg.resizingElement, true);
33545
33546     /**
33547      * @private
33548      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33549      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33550      * @type Number
33551      */
33552     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33553     
33554     /**
33555      * The minimum size of the resizing element. (Defaults to 0)
33556      * @type Number
33557      */
33558     this.minSize = 0;
33559     
33560     /**
33561      * The maximum size of the resizing element. (Defaults to 2000)
33562      * @type Number
33563      */
33564     this.maxSize = 2000;
33565     
33566     /**
33567      * Whether to animate the transition to the new size
33568      * @type Boolean
33569      */
33570     this.animate = false;
33571     
33572     /**
33573      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33574      * @type Boolean
33575      */
33576     this.useShim = false;
33577     
33578     /** @private */
33579     this.shim = null;
33580     
33581     if(!cfg.existingProxy){
33582         /** @private */
33583         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33584     }else{
33585         this.proxy = Roo.get(cfg.existingProxy).dom;
33586     }
33587     /** @private */
33588     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33589     
33590     /** @private */
33591     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33592     
33593     /** @private */
33594     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33595     
33596     /** @private */
33597     this.dragSpecs = {};
33598     
33599     /**
33600      * @private The adapter to use to positon and resize elements
33601      */
33602     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33603     this.adapter.init(this);
33604     
33605     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33606         /** @private */
33607         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33608         this.el.addClass("roo-splitbar-h");
33609     }else{
33610         /** @private */
33611         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33612         this.el.addClass("roo-splitbar-v");
33613     }
33614     
33615     this.addEvents({
33616         /**
33617          * @event resize
33618          * Fires when the splitter is moved (alias for {@link #event-moved})
33619          * @param {Roo.bootstrap.SplitBar} this
33620          * @param {Number} newSize the new width or height
33621          */
33622         "resize" : true,
33623         /**
33624          * @event moved
33625          * Fires when the splitter is moved
33626          * @param {Roo.bootstrap.SplitBar} this
33627          * @param {Number} newSize the new width or height
33628          */
33629         "moved" : true,
33630         /**
33631          * @event beforeresize
33632          * Fires before the splitter is dragged
33633          * @param {Roo.bootstrap.SplitBar} this
33634          */
33635         "beforeresize" : true,
33636
33637         "beforeapply" : true
33638     });
33639
33640     Roo.util.Observable.call(this);
33641 };
33642
33643 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33644     onStartProxyDrag : function(x, y){
33645         this.fireEvent("beforeresize", this);
33646         if(!this.overlay){
33647             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33648             o.unselectable();
33649             o.enableDisplayMode("block");
33650             // all splitbars share the same overlay
33651             Roo.bootstrap.SplitBar.prototype.overlay = o;
33652         }
33653         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33654         this.overlay.show();
33655         Roo.get(this.proxy).setDisplayed("block");
33656         var size = this.adapter.getElementSize(this);
33657         this.activeMinSize = this.getMinimumSize();;
33658         this.activeMaxSize = this.getMaximumSize();;
33659         var c1 = size - this.activeMinSize;
33660         var c2 = Math.max(this.activeMaxSize - size, 0);
33661         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33662             this.dd.resetConstraints();
33663             this.dd.setXConstraint(
33664                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33665                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33666             );
33667             this.dd.setYConstraint(0, 0);
33668         }else{
33669             this.dd.resetConstraints();
33670             this.dd.setXConstraint(0, 0);
33671             this.dd.setYConstraint(
33672                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33673                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33674             );
33675          }
33676         this.dragSpecs.startSize = size;
33677         this.dragSpecs.startPoint = [x, y];
33678         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33679     },
33680     
33681     /** 
33682      * @private Called after the drag operation by the DDProxy
33683      */
33684     onEndProxyDrag : function(e){
33685         Roo.get(this.proxy).setDisplayed(false);
33686         var endPoint = Roo.lib.Event.getXY(e);
33687         if(this.overlay){
33688             this.overlay.hide();
33689         }
33690         var newSize;
33691         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33692             newSize = this.dragSpecs.startSize + 
33693                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33694                     endPoint[0] - this.dragSpecs.startPoint[0] :
33695                     this.dragSpecs.startPoint[0] - endPoint[0]
33696                 );
33697         }else{
33698             newSize = this.dragSpecs.startSize + 
33699                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33700                     endPoint[1] - this.dragSpecs.startPoint[1] :
33701                     this.dragSpecs.startPoint[1] - endPoint[1]
33702                 );
33703         }
33704         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33705         if(newSize != this.dragSpecs.startSize){
33706             if(this.fireEvent('beforeapply', this, newSize) !== false){
33707                 this.adapter.setElementSize(this, newSize);
33708                 this.fireEvent("moved", this, newSize);
33709                 this.fireEvent("resize", this, newSize);
33710             }
33711         }
33712     },
33713     
33714     /**
33715      * Get the adapter this SplitBar uses
33716      * @return The adapter object
33717      */
33718     getAdapter : function(){
33719         return this.adapter;
33720     },
33721     
33722     /**
33723      * Set the adapter this SplitBar uses
33724      * @param {Object} adapter A SplitBar adapter object
33725      */
33726     setAdapter : function(adapter){
33727         this.adapter = adapter;
33728         this.adapter.init(this);
33729     },
33730     
33731     /**
33732      * Gets the minimum size for the resizing element
33733      * @return {Number} The minimum size
33734      */
33735     getMinimumSize : function(){
33736         return this.minSize;
33737     },
33738     
33739     /**
33740      * Sets the minimum size for the resizing element
33741      * @param {Number} minSize The minimum size
33742      */
33743     setMinimumSize : function(minSize){
33744         this.minSize = minSize;
33745     },
33746     
33747     /**
33748      * Gets the maximum size for the resizing element
33749      * @return {Number} The maximum size
33750      */
33751     getMaximumSize : function(){
33752         return this.maxSize;
33753     },
33754     
33755     /**
33756      * Sets the maximum size for the resizing element
33757      * @param {Number} maxSize The maximum size
33758      */
33759     setMaximumSize : function(maxSize){
33760         this.maxSize = maxSize;
33761     },
33762     
33763     /**
33764      * Sets the initialize size for the resizing element
33765      * @param {Number} size The initial size
33766      */
33767     setCurrentSize : function(size){
33768         var oldAnimate = this.animate;
33769         this.animate = false;
33770         this.adapter.setElementSize(this, size);
33771         this.animate = oldAnimate;
33772     },
33773     
33774     /**
33775      * Destroy this splitbar. 
33776      * @param {Boolean} removeEl True to remove the element
33777      */
33778     destroy : function(removeEl){
33779         if(this.shim){
33780             this.shim.remove();
33781         }
33782         this.dd.unreg();
33783         this.proxy.parentNode.removeChild(this.proxy);
33784         if(removeEl){
33785             this.el.remove();
33786         }
33787     }
33788 });
33789
33790 /**
33791  * @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.
33792  */
33793 Roo.bootstrap.SplitBar.createProxy = function(dir){
33794     var proxy = new Roo.Element(document.createElement("div"));
33795     proxy.unselectable();
33796     var cls = 'roo-splitbar-proxy';
33797     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33798     document.body.appendChild(proxy.dom);
33799     return proxy.dom;
33800 };
33801
33802 /** 
33803  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33804  * Default Adapter. It assumes the splitter and resizing element are not positioned
33805  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33806  */
33807 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33808 };
33809
33810 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33811     // do nothing for now
33812     init : function(s){
33813     
33814     },
33815     /**
33816      * Called before drag operations to get the current size of the resizing element. 
33817      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33818      */
33819      getElementSize : function(s){
33820         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33821             return s.resizingEl.getWidth();
33822         }else{
33823             return s.resizingEl.getHeight();
33824         }
33825     },
33826     
33827     /**
33828      * Called after drag operations to set the size of the resizing element.
33829      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33830      * @param {Number} newSize The new size to set
33831      * @param {Function} onComplete A function to be invoked when resizing is complete
33832      */
33833     setElementSize : function(s, newSize, onComplete){
33834         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33835             if(!s.animate){
33836                 s.resizingEl.setWidth(newSize);
33837                 if(onComplete){
33838                     onComplete(s, newSize);
33839                 }
33840             }else{
33841                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33842             }
33843         }else{
33844             
33845             if(!s.animate){
33846                 s.resizingEl.setHeight(newSize);
33847                 if(onComplete){
33848                     onComplete(s, newSize);
33849                 }
33850             }else{
33851                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33852             }
33853         }
33854     }
33855 };
33856
33857 /** 
33858  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33859  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33860  * Adapter that  moves the splitter element to align with the resized sizing element. 
33861  * Used with an absolute positioned SplitBar.
33862  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33863  * document.body, make sure you assign an id to the body element.
33864  */
33865 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33866     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33867     this.container = Roo.get(container);
33868 };
33869
33870 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33871     init : function(s){
33872         this.basic.init(s);
33873     },
33874     
33875     getElementSize : function(s){
33876         return this.basic.getElementSize(s);
33877     },
33878     
33879     setElementSize : function(s, newSize, onComplete){
33880         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33881     },
33882     
33883     moveSplitter : function(s){
33884         var yes = Roo.bootstrap.SplitBar;
33885         switch(s.placement){
33886             case yes.LEFT:
33887                 s.el.setX(s.resizingEl.getRight());
33888                 break;
33889             case yes.RIGHT:
33890                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33891                 break;
33892             case yes.TOP:
33893                 s.el.setY(s.resizingEl.getBottom());
33894                 break;
33895             case yes.BOTTOM:
33896                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33897                 break;
33898         }
33899     }
33900 };
33901
33902 /**
33903  * Orientation constant - Create a vertical SplitBar
33904  * @static
33905  * @type Number
33906  */
33907 Roo.bootstrap.SplitBar.VERTICAL = 1;
33908
33909 /**
33910  * Orientation constant - Create a horizontal SplitBar
33911  * @static
33912  * @type Number
33913  */
33914 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33915
33916 /**
33917  * Placement constant - The resizing element is to the left of the splitter element
33918  * @static
33919  * @type Number
33920  */
33921 Roo.bootstrap.SplitBar.LEFT = 1;
33922
33923 /**
33924  * Placement constant - The resizing element is to the right of the splitter element
33925  * @static
33926  * @type Number
33927  */
33928 Roo.bootstrap.SplitBar.RIGHT = 2;
33929
33930 /**
33931  * Placement constant - The resizing element is positioned above the splitter element
33932  * @static
33933  * @type Number
33934  */
33935 Roo.bootstrap.SplitBar.TOP = 3;
33936
33937 /**
33938  * Placement constant - The resizing element is positioned under splitter element
33939  * @static
33940  * @type Number
33941  */
33942 Roo.bootstrap.SplitBar.BOTTOM = 4;
33943 Roo.namespace("Roo.bootstrap.layout");/*
33944  * Based on:
33945  * Ext JS Library 1.1.1
33946  * Copyright(c) 2006-2007, Ext JS, LLC.
33947  *
33948  * Originally Released Under LGPL - original licence link has changed is not relivant.
33949  *
33950  * Fork - LGPL
33951  * <script type="text/javascript">
33952  */
33953
33954 /**
33955  * @class Roo.bootstrap.layout.Manager
33956  * @extends Roo.bootstrap.Component
33957  * Base class for layout managers.
33958  */
33959 Roo.bootstrap.layout.Manager = function(config)
33960 {
33961     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33962
33963
33964
33965
33966
33967     /** false to disable window resize monitoring @type Boolean */
33968     this.monitorWindowResize = true;
33969     this.regions = {};
33970     this.addEvents({
33971         /**
33972          * @event layout
33973          * Fires when a layout is performed.
33974          * @param {Roo.LayoutManager} this
33975          */
33976         "layout" : true,
33977         /**
33978          * @event regionresized
33979          * Fires when the user resizes a region.
33980          * @param {Roo.LayoutRegion} region The resized region
33981          * @param {Number} newSize The new size (width for east/west, height for north/south)
33982          */
33983         "regionresized" : true,
33984         /**
33985          * @event regioncollapsed
33986          * Fires when a region is collapsed.
33987          * @param {Roo.LayoutRegion} region The collapsed region
33988          */
33989         "regioncollapsed" : true,
33990         /**
33991          * @event regionexpanded
33992          * Fires when a region is expanded.
33993          * @param {Roo.LayoutRegion} region The expanded region
33994          */
33995         "regionexpanded" : true
33996     });
33997     this.updating = false;
33998
33999     if (config.el) {
34000         this.el = Roo.get(config.el);
34001         this.initEvents();
34002     }
34003
34004 };
34005
34006 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34007
34008
34009     regions : null,
34010
34011     monitorWindowResize : true,
34012
34013
34014     updating : false,
34015
34016
34017     onRender : function(ct, position)
34018     {
34019         if(!this.el){
34020             this.el = Roo.get(ct);
34021             this.initEvents();
34022         }
34023         //this.fireEvent('render',this);
34024     },
34025
34026
34027     initEvents: function()
34028     {
34029
34030
34031         // ie scrollbar fix
34032         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34033             document.body.scroll = "no";
34034         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34035             this.el.position('relative');
34036         }
34037         this.id = this.el.id;
34038         this.el.addClass("roo-layout-container");
34039         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34040         if(this.el.dom != document.body ) {
34041             this.el.on('resize', this.layout,this);
34042             this.el.on('show', this.layout,this);
34043         }
34044
34045     },
34046
34047     /**
34048      * Returns true if this layout is currently being updated
34049      * @return {Boolean}
34050      */
34051     isUpdating : function(){
34052         return this.updating;
34053     },
34054
34055     /**
34056      * Suspend the LayoutManager from doing auto-layouts while
34057      * making multiple add or remove calls
34058      */
34059     beginUpdate : function(){
34060         this.updating = true;
34061     },
34062
34063     /**
34064      * Restore auto-layouts and optionally disable the manager from performing a layout
34065      * @param {Boolean} noLayout true to disable a layout update
34066      */
34067     endUpdate : function(noLayout){
34068         this.updating = false;
34069         if(!noLayout){
34070             this.layout();
34071         }
34072     },
34073
34074     layout: function(){
34075         // abstract...
34076     },
34077
34078     onRegionResized : function(region, newSize){
34079         this.fireEvent("regionresized", region, newSize);
34080         this.layout();
34081     },
34082
34083     onRegionCollapsed : function(region){
34084         this.fireEvent("regioncollapsed", region);
34085     },
34086
34087     onRegionExpanded : function(region){
34088         this.fireEvent("regionexpanded", region);
34089     },
34090
34091     /**
34092      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34093      * performs box-model adjustments.
34094      * @return {Object} The size as an object {width: (the width), height: (the height)}
34095      */
34096     getViewSize : function()
34097     {
34098         var size;
34099         if(this.el.dom != document.body){
34100             size = this.el.getSize();
34101         }else{
34102             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34103         }
34104         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34105         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34106         return size;
34107     },
34108
34109     /**
34110      * Returns the Element this layout is bound to.
34111      * @return {Roo.Element}
34112      */
34113     getEl : function(){
34114         return this.el;
34115     },
34116
34117     /**
34118      * Returns the specified region.
34119      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34120      * @return {Roo.LayoutRegion}
34121      */
34122     getRegion : function(target){
34123         return this.regions[target.toLowerCase()];
34124     },
34125
34126     onWindowResize : function(){
34127         if(this.monitorWindowResize){
34128             this.layout();
34129         }
34130     }
34131 });
34132 /*
34133  * Based on:
34134  * Ext JS Library 1.1.1
34135  * Copyright(c) 2006-2007, Ext JS, LLC.
34136  *
34137  * Originally Released Under LGPL - original licence link has changed is not relivant.
34138  *
34139  * Fork - LGPL
34140  * <script type="text/javascript">
34141  */
34142 /**
34143  * @class Roo.bootstrap.layout.Border
34144  * @extends Roo.bootstrap.layout.Manager
34145  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34146  * please see: examples/bootstrap/nested.html<br><br>
34147  
34148 <b>The container the layout is rendered into can be either the body element or any other element.
34149 If it is not the body element, the container needs to either be an absolute positioned element,
34150 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34151 the container size if it is not the body element.</b>
34152
34153 * @constructor
34154 * Create a new Border
34155 * @param {Object} config Configuration options
34156  */
34157 Roo.bootstrap.layout.Border = function(config){
34158     config = config || {};
34159     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34160     
34161     
34162     
34163     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34164         if(config[region]){
34165             config[region].region = region;
34166             this.addRegion(config[region]);
34167         }
34168     },this);
34169     
34170 };
34171
34172 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34173
34174 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34175     /**
34176      * Creates and adds a new region if it doesn't already exist.
34177      * @param {String} target The target region key (north, south, east, west or center).
34178      * @param {Object} config The regions config object
34179      * @return {BorderLayoutRegion} The new region
34180      */
34181     addRegion : function(config)
34182     {
34183         if(!this.regions[config.region]){
34184             var r = this.factory(config);
34185             this.bindRegion(r);
34186         }
34187         return this.regions[config.region];
34188     },
34189
34190     // private (kinda)
34191     bindRegion : function(r){
34192         this.regions[r.config.region] = r;
34193         
34194         r.on("visibilitychange",    this.layout, this);
34195         r.on("paneladded",          this.layout, this);
34196         r.on("panelremoved",        this.layout, this);
34197         r.on("invalidated",         this.layout, this);
34198         r.on("resized",             this.onRegionResized, this);
34199         r.on("collapsed",           this.onRegionCollapsed, this);
34200         r.on("expanded",            this.onRegionExpanded, this);
34201     },
34202
34203     /**
34204      * Performs a layout update.
34205      */
34206     layout : function()
34207     {
34208         if(this.updating) {
34209             return;
34210         }
34211         
34212         // render all the rebions if they have not been done alreayd?
34213         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34214             if(this.regions[region] && !this.regions[region].bodyEl){
34215                 this.regions[region].onRender(this.el)
34216             }
34217         },this);
34218         
34219         var size = this.getViewSize();
34220         var w = size.width;
34221         var h = size.height;
34222         var centerW = w;
34223         var centerH = h;
34224         var centerY = 0;
34225         var centerX = 0;
34226         //var x = 0, y = 0;
34227
34228         var rs = this.regions;
34229         var north = rs["north"];
34230         var south = rs["south"]; 
34231         var west = rs["west"];
34232         var east = rs["east"];
34233         var center = rs["center"];
34234         //if(this.hideOnLayout){ // not supported anymore
34235             //c.el.setStyle("display", "none");
34236         //}
34237         if(north && north.isVisible()){
34238             var b = north.getBox();
34239             var m = north.getMargins();
34240             b.width = w - (m.left+m.right);
34241             b.x = m.left;
34242             b.y = m.top;
34243             centerY = b.height + b.y + m.bottom;
34244             centerH -= centerY;
34245             north.updateBox(this.safeBox(b));
34246         }
34247         if(south && south.isVisible()){
34248             var b = south.getBox();
34249             var m = south.getMargins();
34250             b.width = w - (m.left+m.right);
34251             b.x = m.left;
34252             var totalHeight = (b.height + m.top + m.bottom);
34253             b.y = h - totalHeight + m.top;
34254             centerH -= totalHeight;
34255             south.updateBox(this.safeBox(b));
34256         }
34257         if(west && west.isVisible()){
34258             var b = west.getBox();
34259             var m = west.getMargins();
34260             b.height = centerH - (m.top+m.bottom);
34261             b.x = m.left;
34262             b.y = centerY + m.top;
34263             var totalWidth = (b.width + m.left + m.right);
34264             centerX += totalWidth;
34265             centerW -= totalWidth;
34266             west.updateBox(this.safeBox(b));
34267         }
34268         if(east && east.isVisible()){
34269             var b = east.getBox();
34270             var m = east.getMargins();
34271             b.height = centerH - (m.top+m.bottom);
34272             var totalWidth = (b.width + m.left + m.right);
34273             b.x = w - totalWidth + m.left;
34274             b.y = centerY + m.top;
34275             centerW -= totalWidth;
34276             east.updateBox(this.safeBox(b));
34277         }
34278         if(center){
34279             var m = center.getMargins();
34280             var centerBox = {
34281                 x: centerX + m.left,
34282                 y: centerY + m.top,
34283                 width: centerW - (m.left+m.right),
34284                 height: centerH - (m.top+m.bottom)
34285             };
34286             //if(this.hideOnLayout){
34287                 //center.el.setStyle("display", "block");
34288             //}
34289             center.updateBox(this.safeBox(centerBox));
34290         }
34291         this.el.repaint();
34292         this.fireEvent("layout", this);
34293     },
34294
34295     // private
34296     safeBox : function(box){
34297         box.width = Math.max(0, box.width);
34298         box.height = Math.max(0, box.height);
34299         return box;
34300     },
34301
34302     /**
34303      * Adds a ContentPanel (or subclass) to this layout.
34304      * @param {String} target The target region key (north, south, east, west or center).
34305      * @param {Roo.ContentPanel} panel The panel to add
34306      * @return {Roo.ContentPanel} The added panel
34307      */
34308     add : function(target, panel){
34309          
34310         target = target.toLowerCase();
34311         return this.regions[target].add(panel);
34312     },
34313
34314     /**
34315      * Remove a ContentPanel (or subclass) to this layout.
34316      * @param {String} target The target region key (north, south, east, west or center).
34317      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34318      * @return {Roo.ContentPanel} The removed panel
34319      */
34320     remove : function(target, panel){
34321         target = target.toLowerCase();
34322         return this.regions[target].remove(panel);
34323     },
34324
34325     /**
34326      * Searches all regions for a panel with the specified id
34327      * @param {String} panelId
34328      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34329      */
34330     findPanel : function(panelId){
34331         var rs = this.regions;
34332         for(var target in rs){
34333             if(typeof rs[target] != "function"){
34334                 var p = rs[target].getPanel(panelId);
34335                 if(p){
34336                     return p;
34337                 }
34338             }
34339         }
34340         return null;
34341     },
34342
34343     /**
34344      * Searches all regions for a panel with the specified id and activates (shows) it.
34345      * @param {String/ContentPanel} panelId The panels id or the panel itself
34346      * @return {Roo.ContentPanel} The shown panel or null
34347      */
34348     showPanel : function(panelId) {
34349       var rs = this.regions;
34350       for(var target in rs){
34351          var r = rs[target];
34352          if(typeof r != "function"){
34353             if(r.hasPanel(panelId)){
34354                return r.showPanel(panelId);
34355             }
34356          }
34357       }
34358       return null;
34359    },
34360
34361    /**
34362      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34363      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34364      */
34365    /*
34366     restoreState : function(provider){
34367         if(!provider){
34368             provider = Roo.state.Manager;
34369         }
34370         var sm = new Roo.LayoutStateManager();
34371         sm.init(this, provider);
34372     },
34373 */
34374  
34375  
34376     /**
34377      * Adds a xtype elements to the layout.
34378      * <pre><code>
34379
34380 layout.addxtype({
34381        xtype : 'ContentPanel',
34382        region: 'west',
34383        items: [ .... ]
34384    }
34385 );
34386
34387 layout.addxtype({
34388         xtype : 'NestedLayoutPanel',
34389         region: 'west',
34390         layout: {
34391            center: { },
34392            west: { }   
34393         },
34394         items : [ ... list of content panels or nested layout panels.. ]
34395    }
34396 );
34397 </code></pre>
34398      * @param {Object} cfg Xtype definition of item to add.
34399      */
34400     addxtype : function(cfg)
34401     {
34402         // basically accepts a pannel...
34403         // can accept a layout region..!?!?
34404         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34405         
34406         
34407         // theory?  children can only be panels??
34408         
34409         //if (!cfg.xtype.match(/Panel$/)) {
34410         //    return false;
34411         //}
34412         var ret = false;
34413         
34414         if (typeof(cfg.region) == 'undefined') {
34415             Roo.log("Failed to add Panel, region was not set");
34416             Roo.log(cfg);
34417             return false;
34418         }
34419         var region = cfg.region;
34420         delete cfg.region;
34421         
34422           
34423         var xitems = [];
34424         if (cfg.items) {
34425             xitems = cfg.items;
34426             delete cfg.items;
34427         }
34428         var nb = false;
34429         
34430         switch(cfg.xtype) 
34431         {
34432             case 'Content':  // ContentPanel (el, cfg)
34433             case 'Scroll':  // ContentPanel (el, cfg)
34434             case 'View': 
34435                 cfg.autoCreate = true;
34436                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34437                 //} else {
34438                 //    var el = this.el.createChild();
34439                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34440                 //}
34441                 
34442                 this.add(region, ret);
34443                 break;
34444             
34445             /*
34446             case 'TreePanel': // our new panel!
34447                 cfg.el = this.el.createChild();
34448                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34449                 this.add(region, ret);
34450                 break;
34451             */
34452             
34453             case 'Nest': 
34454                 // create a new Layout (which is  a Border Layout...
34455                 
34456                 var clayout = cfg.layout;
34457                 clayout.el  = this.el.createChild();
34458                 clayout.items   = clayout.items  || [];
34459                 
34460                 delete cfg.layout;
34461                 
34462                 // replace this exitems with the clayout ones..
34463                 xitems = clayout.items;
34464                  
34465                 // force background off if it's in center...
34466                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34467                     cfg.background = false;
34468                 }
34469                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34470                 
34471                 
34472                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34473                 //console.log('adding nested layout panel '  + cfg.toSource());
34474                 this.add(region, ret);
34475                 nb = {}; /// find first...
34476                 break;
34477             
34478             case 'Grid':
34479                 
34480                 // needs grid and region
34481                 
34482                 //var el = this.getRegion(region).el.createChild();
34483                 /*
34484                  *var el = this.el.createChild();
34485                 // create the grid first...
34486                 cfg.grid.container = el;
34487                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34488                 */
34489                 
34490                 if (region == 'center' && this.active ) {
34491                     cfg.background = false;
34492                 }
34493                 
34494                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34495                 
34496                 this.add(region, ret);
34497                 /*
34498                 if (cfg.background) {
34499                     // render grid on panel activation (if panel background)
34500                     ret.on('activate', function(gp) {
34501                         if (!gp.grid.rendered) {
34502                     //        gp.grid.render(el);
34503                         }
34504                     });
34505                 } else {
34506                   //  cfg.grid.render(el);
34507                 }
34508                 */
34509                 break;
34510            
34511            
34512             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34513                 // it was the old xcomponent building that caused this before.
34514                 // espeically if border is the top element in the tree.
34515                 ret = this;
34516                 break; 
34517                 
34518                     
34519                 
34520                 
34521                 
34522             default:
34523                 /*
34524                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34525                     
34526                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34527                     this.add(region, ret);
34528                 } else {
34529                 */
34530                     Roo.log(cfg);
34531                     throw "Can not add '" + cfg.xtype + "' to Border";
34532                     return null;
34533              
34534                                 
34535              
34536         }
34537         this.beginUpdate();
34538         // add children..
34539         var region = '';
34540         var abn = {};
34541         Roo.each(xitems, function(i)  {
34542             region = nb && i.region ? i.region : false;
34543             
34544             var add = ret.addxtype(i);
34545            
34546             if (region) {
34547                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34548                 if (!i.background) {
34549                     abn[region] = nb[region] ;
34550                 }
34551             }
34552             
34553         });
34554         this.endUpdate();
34555
34556         // make the last non-background panel active..
34557         //if (nb) { Roo.log(abn); }
34558         if (nb) {
34559             
34560             for(var r in abn) {
34561                 region = this.getRegion(r);
34562                 if (region) {
34563                     // tried using nb[r], but it does not work..
34564                      
34565                     region.showPanel(abn[r]);
34566                    
34567                 }
34568             }
34569         }
34570         return ret;
34571         
34572     },
34573     
34574     
34575 // private
34576     factory : function(cfg)
34577     {
34578         
34579         var validRegions = Roo.bootstrap.layout.Border.regions;
34580
34581         var target = cfg.region;
34582         cfg.mgr = this;
34583         
34584         var r = Roo.bootstrap.layout;
34585         Roo.log(target);
34586         switch(target){
34587             case "north":
34588                 return new r.North(cfg);
34589             case "south":
34590                 return new r.South(cfg);
34591             case "east":
34592                 return new r.East(cfg);
34593             case "west":
34594                 return new r.West(cfg);
34595             case "center":
34596                 return new r.Center(cfg);
34597         }
34598         throw 'Layout region "'+target+'" not supported.';
34599     }
34600     
34601     
34602 });
34603  /*
34604  * Based on:
34605  * Ext JS Library 1.1.1
34606  * Copyright(c) 2006-2007, Ext JS, LLC.
34607  *
34608  * Originally Released Under LGPL - original licence link has changed is not relivant.
34609  *
34610  * Fork - LGPL
34611  * <script type="text/javascript">
34612  */
34613  
34614 /**
34615  * @class Roo.bootstrap.layout.Basic
34616  * @extends Roo.util.Observable
34617  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34618  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34619  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34620  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34621  * @cfg {string}   region  the region that it inhabits..
34622  * @cfg {bool}   skipConfig skip config?
34623  * 
34624
34625  */
34626 Roo.bootstrap.layout.Basic = function(config){
34627     
34628     this.mgr = config.mgr;
34629     
34630     this.position = config.region;
34631     
34632     var skipConfig = config.skipConfig;
34633     
34634     this.events = {
34635         /**
34636          * @scope Roo.BasicLayoutRegion
34637          */
34638         
34639         /**
34640          * @event beforeremove
34641          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34642          * @param {Roo.LayoutRegion} this
34643          * @param {Roo.ContentPanel} panel The panel
34644          * @param {Object} e The cancel event object
34645          */
34646         "beforeremove" : true,
34647         /**
34648          * @event invalidated
34649          * Fires when the layout for this region is changed.
34650          * @param {Roo.LayoutRegion} this
34651          */
34652         "invalidated" : true,
34653         /**
34654          * @event visibilitychange
34655          * Fires when this region is shown or hidden 
34656          * @param {Roo.LayoutRegion} this
34657          * @param {Boolean} visibility true or false
34658          */
34659         "visibilitychange" : true,
34660         /**
34661          * @event paneladded
34662          * Fires when a panel is added. 
34663          * @param {Roo.LayoutRegion} this
34664          * @param {Roo.ContentPanel} panel The panel
34665          */
34666         "paneladded" : true,
34667         /**
34668          * @event panelremoved
34669          * Fires when a panel is removed. 
34670          * @param {Roo.LayoutRegion} this
34671          * @param {Roo.ContentPanel} panel The panel
34672          */
34673         "panelremoved" : true,
34674         /**
34675          * @event beforecollapse
34676          * Fires when this region before collapse.
34677          * @param {Roo.LayoutRegion} this
34678          */
34679         "beforecollapse" : true,
34680         /**
34681          * @event collapsed
34682          * Fires when this region is collapsed.
34683          * @param {Roo.LayoutRegion} this
34684          */
34685         "collapsed" : true,
34686         /**
34687          * @event expanded
34688          * Fires when this region is expanded.
34689          * @param {Roo.LayoutRegion} this
34690          */
34691         "expanded" : true,
34692         /**
34693          * @event slideshow
34694          * Fires when this region is slid into view.
34695          * @param {Roo.LayoutRegion} this
34696          */
34697         "slideshow" : true,
34698         /**
34699          * @event slidehide
34700          * Fires when this region slides out of view. 
34701          * @param {Roo.LayoutRegion} this
34702          */
34703         "slidehide" : true,
34704         /**
34705          * @event panelactivated
34706          * Fires when a panel is activated. 
34707          * @param {Roo.LayoutRegion} this
34708          * @param {Roo.ContentPanel} panel The activated panel
34709          */
34710         "panelactivated" : true,
34711         /**
34712          * @event resized
34713          * Fires when the user resizes this region. 
34714          * @param {Roo.LayoutRegion} this
34715          * @param {Number} newSize The new size (width for east/west, height for north/south)
34716          */
34717         "resized" : true
34718     };
34719     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34720     this.panels = new Roo.util.MixedCollection();
34721     this.panels.getKey = this.getPanelId.createDelegate(this);
34722     this.box = null;
34723     this.activePanel = null;
34724     // ensure listeners are added...
34725     
34726     if (config.listeners || config.events) {
34727         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34728             listeners : config.listeners || {},
34729             events : config.events || {}
34730         });
34731     }
34732     
34733     if(skipConfig !== true){
34734         this.applyConfig(config);
34735     }
34736 };
34737
34738 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34739 {
34740     getPanelId : function(p){
34741         return p.getId();
34742     },
34743     
34744     applyConfig : function(config){
34745         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34746         this.config = config;
34747         
34748     },
34749     
34750     /**
34751      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34752      * the width, for horizontal (north, south) the height.
34753      * @param {Number} newSize The new width or height
34754      */
34755     resizeTo : function(newSize){
34756         var el = this.el ? this.el :
34757                  (this.activePanel ? this.activePanel.getEl() : null);
34758         if(el){
34759             switch(this.position){
34760                 case "east":
34761                 case "west":
34762                     el.setWidth(newSize);
34763                     this.fireEvent("resized", this, newSize);
34764                 break;
34765                 case "north":
34766                 case "south":
34767                     el.setHeight(newSize);
34768                     this.fireEvent("resized", this, newSize);
34769                 break;                
34770             }
34771         }
34772     },
34773     
34774     getBox : function(){
34775         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34776     },
34777     
34778     getMargins : function(){
34779         return this.margins;
34780     },
34781     
34782     updateBox : function(box){
34783         this.box = box;
34784         var el = this.activePanel.getEl();
34785         el.dom.style.left = box.x + "px";
34786         el.dom.style.top = box.y + "px";
34787         this.activePanel.setSize(box.width, box.height);
34788     },
34789     
34790     /**
34791      * Returns the container element for this region.
34792      * @return {Roo.Element}
34793      */
34794     getEl : function(){
34795         return this.activePanel;
34796     },
34797     
34798     /**
34799      * Returns true if this region is currently visible.
34800      * @return {Boolean}
34801      */
34802     isVisible : function(){
34803         return this.activePanel ? true : false;
34804     },
34805     
34806     setActivePanel : function(panel){
34807         panel = this.getPanel(panel);
34808         if(this.activePanel && this.activePanel != panel){
34809             this.activePanel.setActiveState(false);
34810             this.activePanel.getEl().setLeftTop(-10000,-10000);
34811         }
34812         this.activePanel = panel;
34813         panel.setActiveState(true);
34814         if(this.box){
34815             panel.setSize(this.box.width, this.box.height);
34816         }
34817         this.fireEvent("panelactivated", this, panel);
34818         this.fireEvent("invalidated");
34819     },
34820     
34821     /**
34822      * Show the specified panel.
34823      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34824      * @return {Roo.ContentPanel} The shown panel or null
34825      */
34826     showPanel : function(panel){
34827         panel = this.getPanel(panel);
34828         if(panel){
34829             this.setActivePanel(panel);
34830         }
34831         return panel;
34832     },
34833     
34834     /**
34835      * Get the active panel for this region.
34836      * @return {Roo.ContentPanel} The active panel or null
34837      */
34838     getActivePanel : function(){
34839         return this.activePanel;
34840     },
34841     
34842     /**
34843      * Add the passed ContentPanel(s)
34844      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34845      * @return {Roo.ContentPanel} The panel added (if only one was added)
34846      */
34847     add : function(panel){
34848         if(arguments.length > 1){
34849             for(var i = 0, len = arguments.length; i < len; i++) {
34850                 this.add(arguments[i]);
34851             }
34852             return null;
34853         }
34854         if(this.hasPanel(panel)){
34855             this.showPanel(panel);
34856             return panel;
34857         }
34858         var el = panel.getEl();
34859         if(el.dom.parentNode != this.mgr.el.dom){
34860             this.mgr.el.dom.appendChild(el.dom);
34861         }
34862         if(panel.setRegion){
34863             panel.setRegion(this);
34864         }
34865         this.panels.add(panel);
34866         el.setStyle("position", "absolute");
34867         if(!panel.background){
34868             this.setActivePanel(panel);
34869             if(this.config.initialSize && this.panels.getCount()==1){
34870                 this.resizeTo(this.config.initialSize);
34871             }
34872         }
34873         this.fireEvent("paneladded", this, panel);
34874         return panel;
34875     },
34876     
34877     /**
34878      * Returns true if the panel is in this region.
34879      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34880      * @return {Boolean}
34881      */
34882     hasPanel : function(panel){
34883         if(typeof panel == "object"){ // must be panel obj
34884             panel = panel.getId();
34885         }
34886         return this.getPanel(panel) ? true : false;
34887     },
34888     
34889     /**
34890      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34891      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34892      * @param {Boolean} preservePanel Overrides the config preservePanel option
34893      * @return {Roo.ContentPanel} The panel that was removed
34894      */
34895     remove : function(panel, preservePanel){
34896         panel = this.getPanel(panel);
34897         if(!panel){
34898             return null;
34899         }
34900         var e = {};
34901         this.fireEvent("beforeremove", this, panel, e);
34902         if(e.cancel === true){
34903             return null;
34904         }
34905         var panelId = panel.getId();
34906         this.panels.removeKey(panelId);
34907         return panel;
34908     },
34909     
34910     /**
34911      * Returns the panel specified or null if it's not in this region.
34912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34913      * @return {Roo.ContentPanel}
34914      */
34915     getPanel : function(id){
34916         if(typeof id == "object"){ // must be panel obj
34917             return id;
34918         }
34919         return this.panels.get(id);
34920     },
34921     
34922     /**
34923      * Returns this regions position (north/south/east/west/center).
34924      * @return {String} 
34925      */
34926     getPosition: function(){
34927         return this.position;    
34928     }
34929 });/*
34930  * Based on:
34931  * Ext JS Library 1.1.1
34932  * Copyright(c) 2006-2007, Ext JS, LLC.
34933  *
34934  * Originally Released Under LGPL - original licence link has changed is not relivant.
34935  *
34936  * Fork - LGPL
34937  * <script type="text/javascript">
34938  */
34939  
34940 /**
34941  * @class Roo.bootstrap.layout.Region
34942  * @extends Roo.bootstrap.layout.Basic
34943  * This class represents a region in a layout manager.
34944  
34945  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34946  * @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})
34947  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34948  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34949  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34950  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34951  * @cfg {String}    title           The title for the region (overrides panel titles)
34952  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34953  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34954  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34955  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34956  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34957  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34958  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34959  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34960  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34961  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34962
34963  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34964  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34965  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34966  * @cfg {Number}    width           For East/West panels
34967  * @cfg {Number}    height          For North/South panels
34968  * @cfg {Boolean}   split           To show the splitter
34969  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34970  * 
34971  * @cfg {string}   cls             Extra CSS classes to add to region
34972  * 
34973  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34974  * @cfg {string}   region  the region that it inhabits..
34975  *
34976
34977  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34978  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34979
34980  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34981  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34982  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34983  */
34984 Roo.bootstrap.layout.Region = function(config)
34985 {
34986     this.applyConfig(config);
34987
34988     var mgr = config.mgr;
34989     var pos = config.region;
34990     config.skipConfig = true;
34991     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34992     
34993     if (mgr.el) {
34994         this.onRender(mgr.el);   
34995     }
34996      
34997     this.visible = true;
34998     this.collapsed = false;
34999     this.unrendered_panels = [];
35000 };
35001
35002 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35003
35004     position: '', // set by wrapper (eg. north/south etc..)
35005     unrendered_panels : null,  // unrendered panels.
35006     createBody : function(){
35007         /** This region's body element 
35008         * @type Roo.Element */
35009         this.bodyEl = this.el.createChild({
35010                 tag: "div",
35011                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35012         });
35013     },
35014
35015     onRender: function(ctr, pos)
35016     {
35017         var dh = Roo.DomHelper;
35018         /** This region's container element 
35019         * @type Roo.Element */
35020         this.el = dh.append(ctr.dom, {
35021                 tag: "div",
35022                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35023             }, true);
35024         /** This region's title element 
35025         * @type Roo.Element */
35026     
35027         this.titleEl = dh.append(this.el.dom,
35028             {
35029                     tag: "div",
35030                     unselectable: "on",
35031                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35032                     children:[
35033                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35034                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35035                     ]}, true);
35036         
35037         this.titleEl.enableDisplayMode();
35038         /** This region's title text element 
35039         * @type HTMLElement */
35040         this.titleTextEl = this.titleEl.dom.firstChild;
35041         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35042         /*
35043         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35044         this.closeBtn.enableDisplayMode();
35045         this.closeBtn.on("click", this.closeClicked, this);
35046         this.closeBtn.hide();
35047     */
35048         this.createBody(this.config);
35049         if(this.config.hideWhenEmpty){
35050             this.hide();
35051             this.on("paneladded", this.validateVisibility, this);
35052             this.on("panelremoved", this.validateVisibility, this);
35053         }
35054         if(this.autoScroll){
35055             this.bodyEl.setStyle("overflow", "auto");
35056         }else{
35057             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35058         }
35059         //if(c.titlebar !== false){
35060             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35061                 this.titleEl.hide();
35062             }else{
35063                 this.titleEl.show();
35064                 if(this.config.title){
35065                     this.titleTextEl.innerHTML = this.config.title;
35066                 }
35067             }
35068         //}
35069         if(this.config.collapsed){
35070             this.collapse(true);
35071         }
35072         if(this.config.hidden){
35073             this.hide();
35074         }
35075         
35076         if (this.unrendered_panels && this.unrendered_panels.length) {
35077             for (var i =0;i< this.unrendered_panels.length; i++) {
35078                 this.add(this.unrendered_panels[i]);
35079             }
35080             this.unrendered_panels = null;
35081             
35082         }
35083         
35084     },
35085     
35086     applyConfig : function(c)
35087     {
35088         /*
35089          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35090             var dh = Roo.DomHelper;
35091             if(c.titlebar !== false){
35092                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35093                 this.collapseBtn.on("click", this.collapse, this);
35094                 this.collapseBtn.enableDisplayMode();
35095                 /*
35096                 if(c.showPin === true || this.showPin){
35097                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35098                     this.stickBtn.enableDisplayMode();
35099                     this.stickBtn.on("click", this.expand, this);
35100                     this.stickBtn.hide();
35101                 }
35102                 
35103             }
35104             */
35105             /** This region's collapsed element
35106             * @type Roo.Element */
35107             /*
35108              *
35109             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35110                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35111             ]}, true);
35112             
35113             if(c.floatable !== false){
35114                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35115                this.collapsedEl.on("click", this.collapseClick, this);
35116             }
35117
35118             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35119                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35120                    id: "message", unselectable: "on", style:{"float":"left"}});
35121                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35122              }
35123             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35124             this.expandBtn.on("click", this.expand, this);
35125             
35126         }
35127         
35128         if(this.collapseBtn){
35129             this.collapseBtn.setVisible(c.collapsible == true);
35130         }
35131         
35132         this.cmargins = c.cmargins || this.cmargins ||
35133                          (this.position == "west" || this.position == "east" ?
35134                              {top: 0, left: 2, right:2, bottom: 0} :
35135                              {top: 2, left: 0, right:0, bottom: 2});
35136         */
35137         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35138         
35139         
35140         this.bottomTabs = c.tabPosition != "top";
35141         
35142         this.autoScroll = c.autoScroll || false;
35143         
35144         
35145        
35146         
35147         this.duration = c.duration || .30;
35148         this.slideDuration = c.slideDuration || .45;
35149         this.config = c;
35150        
35151     },
35152     /**
35153      * Returns true if this region is currently visible.
35154      * @return {Boolean}
35155      */
35156     isVisible : function(){
35157         return this.visible;
35158     },
35159
35160     /**
35161      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35162      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35163      */
35164     //setCollapsedTitle : function(title){
35165     //    title = title || "&#160;";
35166      //   if(this.collapsedTitleTextEl){
35167       //      this.collapsedTitleTextEl.innerHTML = title;
35168        // }
35169     //},
35170
35171     getBox : function(){
35172         var b;
35173       //  if(!this.collapsed){
35174             b = this.el.getBox(false, true);
35175        // }else{
35176           //  b = this.collapsedEl.getBox(false, true);
35177         //}
35178         return b;
35179     },
35180
35181     getMargins : function(){
35182         return this.margins;
35183         //return this.collapsed ? this.cmargins : this.margins;
35184     },
35185 /*
35186     highlight : function(){
35187         this.el.addClass("x-layout-panel-dragover");
35188     },
35189
35190     unhighlight : function(){
35191         this.el.removeClass("x-layout-panel-dragover");
35192     },
35193 */
35194     updateBox : function(box)
35195     {
35196         if (!this.bodyEl) {
35197             return; // not rendered yet..
35198         }
35199         
35200         this.box = box;
35201         if(!this.collapsed){
35202             this.el.dom.style.left = box.x + "px";
35203             this.el.dom.style.top = box.y + "px";
35204             this.updateBody(box.width, box.height);
35205         }else{
35206             this.collapsedEl.dom.style.left = box.x + "px";
35207             this.collapsedEl.dom.style.top = box.y + "px";
35208             this.collapsedEl.setSize(box.width, box.height);
35209         }
35210         if(this.tabs){
35211             this.tabs.autoSizeTabs();
35212         }
35213     },
35214
35215     updateBody : function(w, h)
35216     {
35217         if(w !== null){
35218             this.el.setWidth(w);
35219             w -= this.el.getBorderWidth("rl");
35220             if(this.config.adjustments){
35221                 w += this.config.adjustments[0];
35222             }
35223         }
35224         if(h !== null && h > 0){
35225             this.el.setHeight(h);
35226             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35227             h -= this.el.getBorderWidth("tb");
35228             if(this.config.adjustments){
35229                 h += this.config.adjustments[1];
35230             }
35231             this.bodyEl.setHeight(h);
35232             if(this.tabs){
35233                 h = this.tabs.syncHeight(h);
35234             }
35235         }
35236         if(this.panelSize){
35237             w = w !== null ? w : this.panelSize.width;
35238             h = h !== null ? h : this.panelSize.height;
35239         }
35240         if(this.activePanel){
35241             var el = this.activePanel.getEl();
35242             w = w !== null ? w : el.getWidth();
35243             h = h !== null ? h : el.getHeight();
35244             this.panelSize = {width: w, height: h};
35245             this.activePanel.setSize(w, h);
35246         }
35247         if(Roo.isIE && this.tabs){
35248             this.tabs.el.repaint();
35249         }
35250     },
35251
35252     /**
35253      * Returns the container element for this region.
35254      * @return {Roo.Element}
35255      */
35256     getEl : function(){
35257         return this.el;
35258     },
35259
35260     /**
35261      * Hides this region.
35262      */
35263     hide : function(){
35264         //if(!this.collapsed){
35265             this.el.dom.style.left = "-2000px";
35266             this.el.hide();
35267         //}else{
35268          //   this.collapsedEl.dom.style.left = "-2000px";
35269          //   this.collapsedEl.hide();
35270        // }
35271         this.visible = false;
35272         this.fireEvent("visibilitychange", this, false);
35273     },
35274
35275     /**
35276      * Shows this region if it was previously hidden.
35277      */
35278     show : function(){
35279         //if(!this.collapsed){
35280             this.el.show();
35281         //}else{
35282         //    this.collapsedEl.show();
35283        // }
35284         this.visible = true;
35285         this.fireEvent("visibilitychange", this, true);
35286     },
35287 /*
35288     closeClicked : function(){
35289         if(this.activePanel){
35290             this.remove(this.activePanel);
35291         }
35292     },
35293
35294     collapseClick : function(e){
35295         if(this.isSlid){
35296            e.stopPropagation();
35297            this.slideIn();
35298         }else{
35299            e.stopPropagation();
35300            this.slideOut();
35301         }
35302     },
35303 */
35304     /**
35305      * Collapses this region.
35306      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35307      */
35308     /*
35309     collapse : function(skipAnim, skipCheck = false){
35310         if(this.collapsed) {
35311             return;
35312         }
35313         
35314         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35315             
35316             this.collapsed = true;
35317             if(this.split){
35318                 this.split.el.hide();
35319             }
35320             if(this.config.animate && skipAnim !== true){
35321                 this.fireEvent("invalidated", this);
35322                 this.animateCollapse();
35323             }else{
35324                 this.el.setLocation(-20000,-20000);
35325                 this.el.hide();
35326                 this.collapsedEl.show();
35327                 this.fireEvent("collapsed", this);
35328                 this.fireEvent("invalidated", this);
35329             }
35330         }
35331         
35332     },
35333 */
35334     animateCollapse : function(){
35335         // overridden
35336     },
35337
35338     /**
35339      * Expands this region if it was previously collapsed.
35340      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35341      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35342      */
35343     /*
35344     expand : function(e, skipAnim){
35345         if(e) {
35346             e.stopPropagation();
35347         }
35348         if(!this.collapsed || this.el.hasActiveFx()) {
35349             return;
35350         }
35351         if(this.isSlid){
35352             this.afterSlideIn();
35353             skipAnim = true;
35354         }
35355         this.collapsed = false;
35356         if(this.config.animate && skipAnim !== true){
35357             this.animateExpand();
35358         }else{
35359             this.el.show();
35360             if(this.split){
35361                 this.split.el.show();
35362             }
35363             this.collapsedEl.setLocation(-2000,-2000);
35364             this.collapsedEl.hide();
35365             this.fireEvent("invalidated", this);
35366             this.fireEvent("expanded", this);
35367         }
35368     },
35369 */
35370     animateExpand : function(){
35371         // overridden
35372     },
35373
35374     initTabs : function()
35375     {
35376         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35377         
35378         var ts = new Roo.bootstrap.panel.Tabs({
35379                 el: this.bodyEl.dom,
35380                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35381                 disableTooltips: this.config.disableTabTips,
35382                 toolbar : this.config.toolbar
35383             });
35384         
35385         if(this.config.hideTabs){
35386             ts.stripWrap.setDisplayed(false);
35387         }
35388         this.tabs = ts;
35389         ts.resizeTabs = this.config.resizeTabs === true;
35390         ts.minTabWidth = this.config.minTabWidth || 40;
35391         ts.maxTabWidth = this.config.maxTabWidth || 250;
35392         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35393         ts.monitorResize = false;
35394         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35395         ts.bodyEl.addClass('roo-layout-tabs-body');
35396         this.panels.each(this.initPanelAsTab, this);
35397     },
35398
35399     initPanelAsTab : function(panel){
35400         var ti = this.tabs.addTab(
35401             panel.getEl().id,
35402             panel.getTitle(),
35403             null,
35404             this.config.closeOnTab && panel.isClosable(),
35405             panel.tpl
35406         );
35407         if(panel.tabTip !== undefined){
35408             ti.setTooltip(panel.tabTip);
35409         }
35410         ti.on("activate", function(){
35411               this.setActivePanel(panel);
35412         }, this);
35413         
35414         if(this.config.closeOnTab){
35415             ti.on("beforeclose", function(t, e){
35416                 e.cancel = true;
35417                 this.remove(panel);
35418             }, this);
35419         }
35420         
35421         panel.tabItem = ti;
35422         
35423         return ti;
35424     },
35425
35426     updatePanelTitle : function(panel, title)
35427     {
35428         if(this.activePanel == panel){
35429             this.updateTitle(title);
35430         }
35431         if(this.tabs){
35432             var ti = this.tabs.getTab(panel.getEl().id);
35433             ti.setText(title);
35434             if(panel.tabTip !== undefined){
35435                 ti.setTooltip(panel.tabTip);
35436             }
35437         }
35438     },
35439
35440     updateTitle : function(title){
35441         if(this.titleTextEl && !this.config.title){
35442             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35443         }
35444     },
35445
35446     setActivePanel : function(panel)
35447     {
35448         panel = this.getPanel(panel);
35449         if(this.activePanel && this.activePanel != panel){
35450             this.activePanel.setActiveState(false);
35451         }
35452         this.activePanel = panel;
35453         panel.setActiveState(true);
35454         if(this.panelSize){
35455             panel.setSize(this.panelSize.width, this.panelSize.height);
35456         }
35457         if(this.closeBtn){
35458             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35459         }
35460         this.updateTitle(panel.getTitle());
35461         if(this.tabs){
35462             this.fireEvent("invalidated", this);
35463         }
35464         this.fireEvent("panelactivated", this, panel);
35465     },
35466
35467     /**
35468      * Shows the specified panel.
35469      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35470      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35471      */
35472     showPanel : function(panel)
35473     {
35474         panel = this.getPanel(panel);
35475         if(panel){
35476             if(this.tabs){
35477                 var tab = this.tabs.getTab(panel.getEl().id);
35478                 if(tab.isHidden()){
35479                     this.tabs.unhideTab(tab.id);
35480                 }
35481                 tab.activate();
35482             }else{
35483                 this.setActivePanel(panel);
35484             }
35485         }
35486         return panel;
35487     },
35488
35489     /**
35490      * Get the active panel for this region.
35491      * @return {Roo.ContentPanel} The active panel or null
35492      */
35493     getActivePanel : function(){
35494         return this.activePanel;
35495     },
35496
35497     validateVisibility : function(){
35498         if(this.panels.getCount() < 1){
35499             this.updateTitle("&#160;");
35500             this.closeBtn.hide();
35501             this.hide();
35502         }else{
35503             if(!this.isVisible()){
35504                 this.show();
35505             }
35506         }
35507     },
35508
35509     /**
35510      * Adds the passed ContentPanel(s) to this region.
35511      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35512      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35513      */
35514     add : function(panel)
35515     {
35516         if(arguments.length > 1){
35517             for(var i = 0, len = arguments.length; i < len; i++) {
35518                 this.add(arguments[i]);
35519             }
35520             return null;
35521         }
35522         
35523         // if we have not been rendered yet, then we can not really do much of this..
35524         if (!this.bodyEl) {
35525             this.unrendered_panels.push(panel);
35526             return panel;
35527         }
35528         
35529         
35530         
35531         
35532         if(this.hasPanel(panel)){
35533             this.showPanel(panel);
35534             return panel;
35535         }
35536         panel.setRegion(this);
35537         this.panels.add(panel);
35538        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35539             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35540             // and hide them... ???
35541             this.bodyEl.dom.appendChild(panel.getEl().dom);
35542             if(panel.background !== true){
35543                 this.setActivePanel(panel);
35544             }
35545             this.fireEvent("paneladded", this, panel);
35546             return panel;
35547         }
35548         */
35549         if(!this.tabs){
35550             this.initTabs();
35551         }else{
35552             this.initPanelAsTab(panel);
35553         }
35554         
35555         
35556         if(panel.background !== true){
35557             this.tabs.activate(panel.getEl().id);
35558         }
35559         this.fireEvent("paneladded", this, panel);
35560         return panel;
35561     },
35562
35563     /**
35564      * Hides the tab for the specified panel.
35565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35566      */
35567     hidePanel : function(panel){
35568         if(this.tabs && (panel = this.getPanel(panel))){
35569             this.tabs.hideTab(panel.getEl().id);
35570         }
35571     },
35572
35573     /**
35574      * Unhides the tab for a previously hidden panel.
35575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35576      */
35577     unhidePanel : function(panel){
35578         if(this.tabs && (panel = this.getPanel(panel))){
35579             this.tabs.unhideTab(panel.getEl().id);
35580         }
35581     },
35582
35583     clearPanels : function(){
35584         while(this.panels.getCount() > 0){
35585              this.remove(this.panels.first());
35586         }
35587     },
35588
35589     /**
35590      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35591      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35592      * @param {Boolean} preservePanel Overrides the config preservePanel option
35593      * @return {Roo.ContentPanel} The panel that was removed
35594      */
35595     remove : function(panel, preservePanel)
35596     {
35597         panel = this.getPanel(panel);
35598         if(!panel){
35599             return null;
35600         }
35601         var e = {};
35602         this.fireEvent("beforeremove", this, panel, e);
35603         if(e.cancel === true){
35604             return null;
35605         }
35606         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35607         var panelId = panel.getId();
35608         this.panels.removeKey(panelId);
35609         if(preservePanel){
35610             document.body.appendChild(panel.getEl().dom);
35611         }
35612         if(this.tabs){
35613             this.tabs.removeTab(panel.getEl().id);
35614         }else if (!preservePanel){
35615             this.bodyEl.dom.removeChild(panel.getEl().dom);
35616         }
35617         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35618             var p = this.panels.first();
35619             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35620             tempEl.appendChild(p.getEl().dom);
35621             this.bodyEl.update("");
35622             this.bodyEl.dom.appendChild(p.getEl().dom);
35623             tempEl = null;
35624             this.updateTitle(p.getTitle());
35625             this.tabs = null;
35626             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35627             this.setActivePanel(p);
35628         }
35629         panel.setRegion(null);
35630         if(this.activePanel == panel){
35631             this.activePanel = null;
35632         }
35633         if(this.config.autoDestroy !== false && preservePanel !== true){
35634             try{panel.destroy();}catch(e){}
35635         }
35636         this.fireEvent("panelremoved", this, panel);
35637         return panel;
35638     },
35639
35640     /**
35641      * Returns the TabPanel component used by this region
35642      * @return {Roo.TabPanel}
35643      */
35644     getTabs : function(){
35645         return this.tabs;
35646     },
35647
35648     createTool : function(parentEl, className){
35649         var btn = Roo.DomHelper.append(parentEl, {
35650             tag: "div",
35651             cls: "x-layout-tools-button",
35652             children: [ {
35653                 tag: "div",
35654                 cls: "roo-layout-tools-button-inner " + className,
35655                 html: "&#160;"
35656             }]
35657         }, true);
35658         btn.addClassOnOver("roo-layout-tools-button-over");
35659         return btn;
35660     }
35661 });/*
35662  * Based on:
35663  * Ext JS Library 1.1.1
35664  * Copyright(c) 2006-2007, Ext JS, LLC.
35665  *
35666  * Originally Released Under LGPL - original licence link has changed is not relivant.
35667  *
35668  * Fork - LGPL
35669  * <script type="text/javascript">
35670  */
35671  
35672
35673
35674 /**
35675  * @class Roo.SplitLayoutRegion
35676  * @extends Roo.LayoutRegion
35677  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35678  */
35679 Roo.bootstrap.layout.Split = function(config){
35680     this.cursor = config.cursor;
35681     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35682 };
35683
35684 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35685 {
35686     splitTip : "Drag to resize.",
35687     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35688     useSplitTips : false,
35689
35690     applyConfig : function(config){
35691         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35692     },
35693     
35694     onRender : function(ctr,pos) {
35695         
35696         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35697         if(!this.config.split){
35698             return;
35699         }
35700         if(!this.split){
35701             
35702             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35703                             tag: "div",
35704                             id: this.el.id + "-split",
35705                             cls: "roo-layout-split roo-layout-split-"+this.position,
35706                             html: "&#160;"
35707             });
35708             /** The SplitBar for this region 
35709             * @type Roo.SplitBar */
35710             // does not exist yet...
35711             Roo.log([this.position, this.orientation]);
35712             
35713             this.split = new Roo.bootstrap.SplitBar({
35714                 dragElement : splitEl,
35715                 resizingElement: this.el,
35716                 orientation : this.orientation
35717             });
35718             
35719             this.split.on("moved", this.onSplitMove, this);
35720             this.split.useShim = this.config.useShim === true;
35721             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35722             if(this.useSplitTips){
35723                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35724             }
35725             //if(config.collapsible){
35726             //    this.split.el.on("dblclick", this.collapse,  this);
35727             //}
35728         }
35729         if(typeof this.config.minSize != "undefined"){
35730             this.split.minSize = this.config.minSize;
35731         }
35732         if(typeof this.config.maxSize != "undefined"){
35733             this.split.maxSize = this.config.maxSize;
35734         }
35735         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35736             this.hideSplitter();
35737         }
35738         
35739     },
35740
35741     getHMaxSize : function(){
35742          var cmax = this.config.maxSize || 10000;
35743          var center = this.mgr.getRegion("center");
35744          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35745     },
35746
35747     getVMaxSize : function(){
35748          var cmax = this.config.maxSize || 10000;
35749          var center = this.mgr.getRegion("center");
35750          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35751     },
35752
35753     onSplitMove : function(split, newSize){
35754         this.fireEvent("resized", this, newSize);
35755     },
35756     
35757     /** 
35758      * Returns the {@link Roo.SplitBar} for this region.
35759      * @return {Roo.SplitBar}
35760      */
35761     getSplitBar : function(){
35762         return this.split;
35763     },
35764     
35765     hide : function(){
35766         this.hideSplitter();
35767         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35768     },
35769
35770     hideSplitter : function(){
35771         if(this.split){
35772             this.split.el.setLocation(-2000,-2000);
35773             this.split.el.hide();
35774         }
35775     },
35776
35777     show : function(){
35778         if(this.split){
35779             this.split.el.show();
35780         }
35781         Roo.bootstrap.layout.Split.superclass.show.call(this);
35782     },
35783     
35784     beforeSlide: function(){
35785         if(Roo.isGecko){// firefox overflow auto bug workaround
35786             this.bodyEl.clip();
35787             if(this.tabs) {
35788                 this.tabs.bodyEl.clip();
35789             }
35790             if(this.activePanel){
35791                 this.activePanel.getEl().clip();
35792                 
35793                 if(this.activePanel.beforeSlide){
35794                     this.activePanel.beforeSlide();
35795                 }
35796             }
35797         }
35798     },
35799     
35800     afterSlide : function(){
35801         if(Roo.isGecko){// firefox overflow auto bug workaround
35802             this.bodyEl.unclip();
35803             if(this.tabs) {
35804                 this.tabs.bodyEl.unclip();
35805             }
35806             if(this.activePanel){
35807                 this.activePanel.getEl().unclip();
35808                 if(this.activePanel.afterSlide){
35809                     this.activePanel.afterSlide();
35810                 }
35811             }
35812         }
35813     },
35814
35815     initAutoHide : function(){
35816         if(this.autoHide !== false){
35817             if(!this.autoHideHd){
35818                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35819                 this.autoHideHd = {
35820                     "mouseout": function(e){
35821                         if(!e.within(this.el, true)){
35822                             st.delay(500);
35823                         }
35824                     },
35825                     "mouseover" : function(e){
35826                         st.cancel();
35827                     },
35828                     scope : this
35829                 };
35830             }
35831             this.el.on(this.autoHideHd);
35832         }
35833     },
35834
35835     clearAutoHide : function(){
35836         if(this.autoHide !== false){
35837             this.el.un("mouseout", this.autoHideHd.mouseout);
35838             this.el.un("mouseover", this.autoHideHd.mouseover);
35839         }
35840     },
35841
35842     clearMonitor : function(){
35843         Roo.get(document).un("click", this.slideInIf, this);
35844     },
35845
35846     // these names are backwards but not changed for compat
35847     slideOut : function(){
35848         if(this.isSlid || this.el.hasActiveFx()){
35849             return;
35850         }
35851         this.isSlid = true;
35852         if(this.collapseBtn){
35853             this.collapseBtn.hide();
35854         }
35855         this.closeBtnState = this.closeBtn.getStyle('display');
35856         this.closeBtn.hide();
35857         if(this.stickBtn){
35858             this.stickBtn.show();
35859         }
35860         this.el.show();
35861         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35862         this.beforeSlide();
35863         this.el.setStyle("z-index", 10001);
35864         this.el.slideIn(this.getSlideAnchor(), {
35865             callback: function(){
35866                 this.afterSlide();
35867                 this.initAutoHide();
35868                 Roo.get(document).on("click", this.slideInIf, this);
35869                 this.fireEvent("slideshow", this);
35870             },
35871             scope: this,
35872             block: true
35873         });
35874     },
35875
35876     afterSlideIn : function(){
35877         this.clearAutoHide();
35878         this.isSlid = false;
35879         this.clearMonitor();
35880         this.el.setStyle("z-index", "");
35881         if(this.collapseBtn){
35882             this.collapseBtn.show();
35883         }
35884         this.closeBtn.setStyle('display', this.closeBtnState);
35885         if(this.stickBtn){
35886             this.stickBtn.hide();
35887         }
35888         this.fireEvent("slidehide", this);
35889     },
35890
35891     slideIn : function(cb){
35892         if(!this.isSlid || this.el.hasActiveFx()){
35893             Roo.callback(cb);
35894             return;
35895         }
35896         this.isSlid = false;
35897         this.beforeSlide();
35898         this.el.slideOut(this.getSlideAnchor(), {
35899             callback: function(){
35900                 this.el.setLeftTop(-10000, -10000);
35901                 this.afterSlide();
35902                 this.afterSlideIn();
35903                 Roo.callback(cb);
35904             },
35905             scope: this,
35906             block: true
35907         });
35908     },
35909     
35910     slideInIf : function(e){
35911         if(!e.within(this.el)){
35912             this.slideIn();
35913         }
35914     },
35915
35916     animateCollapse : function(){
35917         this.beforeSlide();
35918         this.el.setStyle("z-index", 20000);
35919         var anchor = this.getSlideAnchor();
35920         this.el.slideOut(anchor, {
35921             callback : function(){
35922                 this.el.setStyle("z-index", "");
35923                 this.collapsedEl.slideIn(anchor, {duration:.3});
35924                 this.afterSlide();
35925                 this.el.setLocation(-10000,-10000);
35926                 this.el.hide();
35927                 this.fireEvent("collapsed", this);
35928             },
35929             scope: this,
35930             block: true
35931         });
35932     },
35933
35934     animateExpand : function(){
35935         this.beforeSlide();
35936         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35937         this.el.setStyle("z-index", 20000);
35938         this.collapsedEl.hide({
35939             duration:.1
35940         });
35941         this.el.slideIn(this.getSlideAnchor(), {
35942             callback : function(){
35943                 this.el.setStyle("z-index", "");
35944                 this.afterSlide();
35945                 if(this.split){
35946                     this.split.el.show();
35947                 }
35948                 this.fireEvent("invalidated", this);
35949                 this.fireEvent("expanded", this);
35950             },
35951             scope: this,
35952             block: true
35953         });
35954     },
35955
35956     anchors : {
35957         "west" : "left",
35958         "east" : "right",
35959         "north" : "top",
35960         "south" : "bottom"
35961     },
35962
35963     sanchors : {
35964         "west" : "l",
35965         "east" : "r",
35966         "north" : "t",
35967         "south" : "b"
35968     },
35969
35970     canchors : {
35971         "west" : "tl-tr",
35972         "east" : "tr-tl",
35973         "north" : "tl-bl",
35974         "south" : "bl-tl"
35975     },
35976
35977     getAnchor : function(){
35978         return this.anchors[this.position];
35979     },
35980
35981     getCollapseAnchor : function(){
35982         return this.canchors[this.position];
35983     },
35984
35985     getSlideAnchor : function(){
35986         return this.sanchors[this.position];
35987     },
35988
35989     getAlignAdj : function(){
35990         var cm = this.cmargins;
35991         switch(this.position){
35992             case "west":
35993                 return [0, 0];
35994             break;
35995             case "east":
35996                 return [0, 0];
35997             break;
35998             case "north":
35999                 return [0, 0];
36000             break;
36001             case "south":
36002                 return [0, 0];
36003             break;
36004         }
36005     },
36006
36007     getExpandAdj : function(){
36008         var c = this.collapsedEl, cm = this.cmargins;
36009         switch(this.position){
36010             case "west":
36011                 return [-(cm.right+c.getWidth()+cm.left), 0];
36012             break;
36013             case "east":
36014                 return [cm.right+c.getWidth()+cm.left, 0];
36015             break;
36016             case "north":
36017                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36018             break;
36019             case "south":
36020                 return [0, cm.top+cm.bottom+c.getHeight()];
36021             break;
36022         }
36023     }
36024 });/*
36025  * Based on:
36026  * Ext JS Library 1.1.1
36027  * Copyright(c) 2006-2007, Ext JS, LLC.
36028  *
36029  * Originally Released Under LGPL - original licence link has changed is not relivant.
36030  *
36031  * Fork - LGPL
36032  * <script type="text/javascript">
36033  */
36034 /*
36035  * These classes are private internal classes
36036  */
36037 Roo.bootstrap.layout.Center = function(config){
36038     config.region = "center";
36039     Roo.bootstrap.layout.Region.call(this, config);
36040     this.visible = true;
36041     this.minWidth = config.minWidth || 20;
36042     this.minHeight = config.minHeight || 20;
36043 };
36044
36045 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36046     hide : function(){
36047         // center panel can't be hidden
36048     },
36049     
36050     show : function(){
36051         // center panel can't be hidden
36052     },
36053     
36054     getMinWidth: function(){
36055         return this.minWidth;
36056     },
36057     
36058     getMinHeight: function(){
36059         return this.minHeight;
36060     }
36061 });
36062
36063
36064
36065
36066  
36067
36068
36069
36070
36071
36072 Roo.bootstrap.layout.North = function(config)
36073 {
36074     config.region = 'north';
36075     config.cursor = 'n-resize';
36076     
36077     Roo.bootstrap.layout.Split.call(this, config);
36078     
36079     
36080     if(this.split){
36081         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36082         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36083         this.split.el.addClass("roo-layout-split-v");
36084     }
36085     var size = config.initialSize || config.height;
36086     if(typeof size != "undefined"){
36087         this.el.setHeight(size);
36088     }
36089 };
36090 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36091 {
36092     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36093     
36094     
36095     
36096     getBox : function(){
36097         if(this.collapsed){
36098             return this.collapsedEl.getBox();
36099         }
36100         var box = this.el.getBox();
36101         if(this.split){
36102             box.height += this.split.el.getHeight();
36103         }
36104         return box;
36105     },
36106     
36107     updateBox : function(box){
36108         if(this.split && !this.collapsed){
36109             box.height -= this.split.el.getHeight();
36110             this.split.el.setLeft(box.x);
36111             this.split.el.setTop(box.y+box.height);
36112             this.split.el.setWidth(box.width);
36113         }
36114         if(this.collapsed){
36115             this.updateBody(box.width, null);
36116         }
36117         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36118     }
36119 });
36120
36121
36122
36123
36124
36125 Roo.bootstrap.layout.South = function(config){
36126     config.region = 'south';
36127     config.cursor = 's-resize';
36128     Roo.bootstrap.layout.Split.call(this, config);
36129     if(this.split){
36130         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36131         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36132         this.split.el.addClass("roo-layout-split-v");
36133     }
36134     var size = config.initialSize || config.height;
36135     if(typeof size != "undefined"){
36136         this.el.setHeight(size);
36137     }
36138 };
36139
36140 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36141     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36142     getBox : function(){
36143         if(this.collapsed){
36144             return this.collapsedEl.getBox();
36145         }
36146         var box = this.el.getBox();
36147         if(this.split){
36148             var sh = this.split.el.getHeight();
36149             box.height += sh;
36150             box.y -= sh;
36151         }
36152         return box;
36153     },
36154     
36155     updateBox : function(box){
36156         if(this.split && !this.collapsed){
36157             var sh = this.split.el.getHeight();
36158             box.height -= sh;
36159             box.y += sh;
36160             this.split.el.setLeft(box.x);
36161             this.split.el.setTop(box.y-sh);
36162             this.split.el.setWidth(box.width);
36163         }
36164         if(this.collapsed){
36165             this.updateBody(box.width, null);
36166         }
36167         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36168     }
36169 });
36170
36171 Roo.bootstrap.layout.East = function(config){
36172     config.region = "east";
36173     config.cursor = "e-resize";
36174     Roo.bootstrap.layout.Split.call(this, config);
36175     if(this.split){
36176         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36177         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36178         this.split.el.addClass("roo-layout-split-h");
36179     }
36180     var size = config.initialSize || config.width;
36181     if(typeof size != "undefined"){
36182         this.el.setWidth(size);
36183     }
36184 };
36185 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36186     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36187     getBox : function(){
36188         if(this.collapsed){
36189             return this.collapsedEl.getBox();
36190         }
36191         var box = this.el.getBox();
36192         if(this.split){
36193             var sw = this.split.el.getWidth();
36194             box.width += sw;
36195             box.x -= sw;
36196         }
36197         return box;
36198     },
36199
36200     updateBox : function(box){
36201         if(this.split && !this.collapsed){
36202             var sw = this.split.el.getWidth();
36203             box.width -= sw;
36204             this.split.el.setLeft(box.x);
36205             this.split.el.setTop(box.y);
36206             this.split.el.setHeight(box.height);
36207             box.x += sw;
36208         }
36209         if(this.collapsed){
36210             this.updateBody(null, box.height);
36211         }
36212         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36213     }
36214 });
36215
36216 Roo.bootstrap.layout.West = function(config){
36217     config.region = "west";
36218     config.cursor = "w-resize";
36219     
36220     Roo.bootstrap.layout.Split.call(this, config);
36221     if(this.split){
36222         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36223         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36224         this.split.el.addClass("roo-layout-split-h");
36225     }
36226     
36227 };
36228 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36229     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36230     
36231     onRender: function(ctr, pos)
36232     {
36233         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36234         var size = this.config.initialSize || this.config.width;
36235         if(typeof size != "undefined"){
36236             this.el.setWidth(size);
36237         }
36238     },
36239     
36240     getBox : function(){
36241         if(this.collapsed){
36242             return this.collapsedEl.getBox();
36243         }
36244         var box = this.el.getBox();
36245         if(this.split){
36246             box.width += this.split.el.getWidth();
36247         }
36248         return box;
36249     },
36250     
36251     updateBox : function(box){
36252         if(this.split && !this.collapsed){
36253             var sw = this.split.el.getWidth();
36254             box.width -= sw;
36255             this.split.el.setLeft(box.x+box.width);
36256             this.split.el.setTop(box.y);
36257             this.split.el.setHeight(box.height);
36258         }
36259         if(this.collapsed){
36260             this.updateBody(null, box.height);
36261         }
36262         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36263     }
36264 });
36265 Roo.namespace("Roo.bootstrap.panel");/*
36266  * Based on:
36267  * Ext JS Library 1.1.1
36268  * Copyright(c) 2006-2007, Ext JS, LLC.
36269  *
36270  * Originally Released Under LGPL - original licence link has changed is not relivant.
36271  *
36272  * Fork - LGPL
36273  * <script type="text/javascript">
36274  */
36275 /**
36276  * @class Roo.ContentPanel
36277  * @extends Roo.util.Observable
36278  * A basic ContentPanel element.
36279  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36280  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36281  * @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
36282  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36283  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36284  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36285  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36286  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36287  * @cfg {String} title          The title for this panel
36288  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36289  * @cfg {String} url            Calls {@link #setUrl} with this value
36290  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36291  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36292  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36293  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36294  * @cfg {Boolean} badges render the badges
36295
36296  * @constructor
36297  * Create a new ContentPanel.
36298  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36299  * @param {String/Object} config A string to set only the title or a config object
36300  * @param {String} content (optional) Set the HTML content for this panel
36301  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36302  */
36303 Roo.bootstrap.panel.Content = function( config){
36304     
36305     this.tpl = config.tpl || false;
36306     
36307     var el = config.el;
36308     var content = config.content;
36309
36310     if(config.autoCreate){ // xtype is available if this is called from factory
36311         el = Roo.id();
36312     }
36313     this.el = Roo.get(el);
36314     if(!this.el && config && config.autoCreate){
36315         if(typeof config.autoCreate == "object"){
36316             if(!config.autoCreate.id){
36317                 config.autoCreate.id = config.id||el;
36318             }
36319             this.el = Roo.DomHelper.append(document.body,
36320                         config.autoCreate, true);
36321         }else{
36322             var elcfg =  {   tag: "div",
36323                             cls: "roo-layout-inactive-content",
36324                             id: config.id||el
36325                             };
36326             if (config.html) {
36327                 elcfg.html = config.html;
36328                 
36329             }
36330                         
36331             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36332         }
36333     } 
36334     this.closable = false;
36335     this.loaded = false;
36336     this.active = false;
36337    
36338       
36339     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36340         
36341         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36342         
36343         this.wrapEl = this.el; //this.el.wrap();
36344         var ti = [];
36345         if (config.toolbar.items) {
36346             ti = config.toolbar.items ;
36347             delete config.toolbar.items ;
36348         }
36349         
36350         var nitems = [];
36351         this.toolbar.render(this.wrapEl, 'before');
36352         for(var i =0;i < ti.length;i++) {
36353           //  Roo.log(['add child', items[i]]);
36354             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36355         }
36356         this.toolbar.items = nitems;
36357         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36358         delete config.toolbar;
36359         
36360     }
36361     /*
36362     // xtype created footer. - not sure if will work as we normally have to render first..
36363     if (this.footer && !this.footer.el && this.footer.xtype) {
36364         if (!this.wrapEl) {
36365             this.wrapEl = this.el.wrap();
36366         }
36367     
36368         this.footer.container = this.wrapEl.createChild();
36369          
36370         this.footer = Roo.factory(this.footer, Roo);
36371         
36372     }
36373     */
36374     
36375      if(typeof config == "string"){
36376         this.title = config;
36377     }else{
36378         Roo.apply(this, config);
36379     }
36380     
36381     if(this.resizeEl){
36382         this.resizeEl = Roo.get(this.resizeEl, true);
36383     }else{
36384         this.resizeEl = this.el;
36385     }
36386     // handle view.xtype
36387     
36388  
36389     
36390     
36391     this.addEvents({
36392         /**
36393          * @event activate
36394          * Fires when this panel is activated. 
36395          * @param {Roo.ContentPanel} this
36396          */
36397         "activate" : true,
36398         /**
36399          * @event deactivate
36400          * Fires when this panel is activated. 
36401          * @param {Roo.ContentPanel} this
36402          */
36403         "deactivate" : true,
36404
36405         /**
36406          * @event resize
36407          * Fires when this panel is resized if fitToFrame is true.
36408          * @param {Roo.ContentPanel} this
36409          * @param {Number} width The width after any component adjustments
36410          * @param {Number} height The height after any component adjustments
36411          */
36412         "resize" : true,
36413         
36414          /**
36415          * @event render
36416          * Fires when this tab is created
36417          * @param {Roo.ContentPanel} this
36418          */
36419         "render" : true
36420         
36421         
36422         
36423     });
36424     
36425
36426     
36427     
36428     if(this.autoScroll){
36429         this.resizeEl.setStyle("overflow", "auto");
36430     } else {
36431         // fix randome scrolling
36432         //this.el.on('scroll', function() {
36433         //    Roo.log('fix random scolling');
36434         //    this.scrollTo('top',0); 
36435         //});
36436     }
36437     content = content || this.content;
36438     if(content){
36439         this.setContent(content);
36440     }
36441     if(config && config.url){
36442         this.setUrl(this.url, this.params, this.loadOnce);
36443     }
36444     
36445     
36446     
36447     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36448     
36449     if (this.view && typeof(this.view.xtype) != 'undefined') {
36450         this.view.el = this.el.appendChild(document.createElement("div"));
36451         this.view = Roo.factory(this.view); 
36452         this.view.render  &&  this.view.render(false, '');  
36453     }
36454     
36455     
36456     this.fireEvent('render', this);
36457 };
36458
36459 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36460     
36461     tabTip : '',
36462     
36463     setRegion : function(region){
36464         this.region = region;
36465         this.setActiveClass(region && !this.background);
36466     },
36467     
36468     
36469     setActiveClass: function(state)
36470     {
36471         if(state){
36472            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36473            this.el.setStyle('position','relative');
36474         }else{
36475            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36476            this.el.setStyle('position', 'absolute');
36477         } 
36478     },
36479     
36480     /**
36481      * Returns the toolbar for this Panel if one was configured. 
36482      * @return {Roo.Toolbar} 
36483      */
36484     getToolbar : function(){
36485         return this.toolbar;
36486     },
36487     
36488     setActiveState : function(active)
36489     {
36490         this.active = active;
36491         this.setActiveClass(active);
36492         if(!active){
36493             this.fireEvent("deactivate", this);
36494         }else{
36495             this.fireEvent("activate", this);
36496         }
36497     },
36498     /**
36499      * Updates this panel's element
36500      * @param {String} content The new content
36501      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36502     */
36503     setContent : function(content, loadScripts){
36504         this.el.update(content, loadScripts);
36505     },
36506
36507     ignoreResize : function(w, h){
36508         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36509             return true;
36510         }else{
36511             this.lastSize = {width: w, height: h};
36512             return false;
36513         }
36514     },
36515     /**
36516      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36517      * @return {Roo.UpdateManager} The UpdateManager
36518      */
36519     getUpdateManager : function(){
36520         return this.el.getUpdateManager();
36521     },
36522      /**
36523      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36524      * @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:
36525 <pre><code>
36526 panel.load({
36527     url: "your-url.php",
36528     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36529     callback: yourFunction,
36530     scope: yourObject, //(optional scope)
36531     discardUrl: false,
36532     nocache: false,
36533     text: "Loading...",
36534     timeout: 30,
36535     scripts: false
36536 });
36537 </code></pre>
36538      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36539      * 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.
36540      * @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}
36541      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36542      * @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.
36543      * @return {Roo.ContentPanel} this
36544      */
36545     load : function(){
36546         var um = this.el.getUpdateManager();
36547         um.update.apply(um, arguments);
36548         return this;
36549     },
36550
36551
36552     /**
36553      * 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.
36554      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36555      * @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)
36556      * @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)
36557      * @return {Roo.UpdateManager} The UpdateManager
36558      */
36559     setUrl : function(url, params, loadOnce){
36560         if(this.refreshDelegate){
36561             this.removeListener("activate", this.refreshDelegate);
36562         }
36563         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36564         this.on("activate", this.refreshDelegate);
36565         return this.el.getUpdateManager();
36566     },
36567     
36568     _handleRefresh : function(url, params, loadOnce){
36569         if(!loadOnce || !this.loaded){
36570             var updater = this.el.getUpdateManager();
36571             updater.update(url, params, this._setLoaded.createDelegate(this));
36572         }
36573     },
36574     
36575     _setLoaded : function(){
36576         this.loaded = true;
36577     }, 
36578     
36579     /**
36580      * Returns this panel's id
36581      * @return {String} 
36582      */
36583     getId : function(){
36584         return this.el.id;
36585     },
36586     
36587     /** 
36588      * Returns this panel's element - used by regiosn to add.
36589      * @return {Roo.Element} 
36590      */
36591     getEl : function(){
36592         return this.wrapEl || this.el;
36593     },
36594     
36595    
36596     
36597     adjustForComponents : function(width, height)
36598     {
36599         //Roo.log('adjustForComponents ');
36600         if(this.resizeEl != this.el){
36601             width -= this.el.getFrameWidth('lr');
36602             height -= this.el.getFrameWidth('tb');
36603         }
36604         if(this.toolbar){
36605             var te = this.toolbar.getEl();
36606             te.setWidth(width);
36607             height -= te.getHeight();
36608         }
36609         if(this.footer){
36610             var te = this.footer.getEl();
36611             te.setWidth(width);
36612             height -= te.getHeight();
36613         }
36614         
36615         
36616         if(this.adjustments){
36617             width += this.adjustments[0];
36618             height += this.adjustments[1];
36619         }
36620         return {"width": width, "height": height};
36621     },
36622     
36623     setSize : function(width, height){
36624         if(this.fitToFrame && !this.ignoreResize(width, height)){
36625             if(this.fitContainer && this.resizeEl != this.el){
36626                 this.el.setSize(width, height);
36627             }
36628             var size = this.adjustForComponents(width, height);
36629             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36630             this.fireEvent('resize', this, size.width, size.height);
36631         }
36632     },
36633     
36634     /**
36635      * Returns this panel's title
36636      * @return {String} 
36637      */
36638     getTitle : function(){
36639         
36640         if (typeof(this.title) != 'object') {
36641             return this.title;
36642         }
36643         
36644         var t = '';
36645         for (var k in this.title) {
36646             if (!this.title.hasOwnProperty(k)) {
36647                 continue;
36648             }
36649             
36650             if (k.indexOf('-') >= 0) {
36651                 var s = k.split('-');
36652                 for (var i = 0; i<s.length; i++) {
36653                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36654                 }
36655             } else {
36656                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36657             }
36658         }
36659         return t;
36660     },
36661     
36662     /**
36663      * Set this panel's title
36664      * @param {String} title
36665      */
36666     setTitle : function(title){
36667         this.title = title;
36668         if(this.region){
36669             this.region.updatePanelTitle(this, title);
36670         }
36671     },
36672     
36673     /**
36674      * Returns true is this panel was configured to be closable
36675      * @return {Boolean} 
36676      */
36677     isClosable : function(){
36678         return this.closable;
36679     },
36680     
36681     beforeSlide : function(){
36682         this.el.clip();
36683         this.resizeEl.clip();
36684     },
36685     
36686     afterSlide : function(){
36687         this.el.unclip();
36688         this.resizeEl.unclip();
36689     },
36690     
36691     /**
36692      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36693      *   Will fail silently if the {@link #setUrl} method has not been called.
36694      *   This does not activate the panel, just updates its content.
36695      */
36696     refresh : function(){
36697         if(this.refreshDelegate){
36698            this.loaded = false;
36699            this.refreshDelegate();
36700         }
36701     },
36702     
36703     /**
36704      * Destroys this panel
36705      */
36706     destroy : function(){
36707         this.el.removeAllListeners();
36708         var tempEl = document.createElement("span");
36709         tempEl.appendChild(this.el.dom);
36710         tempEl.innerHTML = "";
36711         this.el.remove();
36712         this.el = null;
36713     },
36714     
36715     /**
36716      * form - if the content panel contains a form - this is a reference to it.
36717      * @type {Roo.form.Form}
36718      */
36719     form : false,
36720     /**
36721      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36722      *    This contains a reference to it.
36723      * @type {Roo.View}
36724      */
36725     view : false,
36726     
36727       /**
36728      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36729      * <pre><code>
36730
36731 layout.addxtype({
36732        xtype : 'Form',
36733        items: [ .... ]
36734    }
36735 );
36736
36737 </code></pre>
36738      * @param {Object} cfg Xtype definition of item to add.
36739      */
36740     
36741     
36742     getChildContainer: function () {
36743         return this.getEl();
36744     }
36745     
36746     
36747     /*
36748         var  ret = new Roo.factory(cfg);
36749         return ret;
36750         
36751         
36752         // add form..
36753         if (cfg.xtype.match(/^Form$/)) {
36754             
36755             var el;
36756             //if (this.footer) {
36757             //    el = this.footer.container.insertSibling(false, 'before');
36758             //} else {
36759                 el = this.el.createChild();
36760             //}
36761
36762             this.form = new  Roo.form.Form(cfg);
36763             
36764             
36765             if ( this.form.allItems.length) {
36766                 this.form.render(el.dom);
36767             }
36768             return this.form;
36769         }
36770         // should only have one of theses..
36771         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36772             // views.. should not be just added - used named prop 'view''
36773             
36774             cfg.el = this.el.appendChild(document.createElement("div"));
36775             // factory?
36776             
36777             var ret = new Roo.factory(cfg);
36778              
36779              ret.render && ret.render(false, ''); // render blank..
36780             this.view = ret;
36781             return ret;
36782         }
36783         return false;
36784     }
36785     \*/
36786 });
36787  
36788 /**
36789  * @class Roo.bootstrap.panel.Grid
36790  * @extends Roo.bootstrap.panel.Content
36791  * @constructor
36792  * Create a new GridPanel.
36793  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36794  * @param {Object} config A the config object
36795   
36796  */
36797
36798
36799
36800 Roo.bootstrap.panel.Grid = function(config)
36801 {
36802     
36803       
36804     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36805         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36806
36807     config.el = this.wrapper;
36808     //this.el = this.wrapper;
36809     
36810       if (config.container) {
36811         // ctor'ed from a Border/panel.grid
36812         
36813         
36814         this.wrapper.setStyle("overflow", "hidden");
36815         this.wrapper.addClass('roo-grid-container');
36816
36817     }
36818     
36819     
36820     if(config.toolbar){
36821         var tool_el = this.wrapper.createChild();    
36822         this.toolbar = Roo.factory(config.toolbar);
36823         var ti = [];
36824         if (config.toolbar.items) {
36825             ti = config.toolbar.items ;
36826             delete config.toolbar.items ;
36827         }
36828         
36829         var nitems = [];
36830         this.toolbar.render(tool_el);
36831         for(var i =0;i < ti.length;i++) {
36832           //  Roo.log(['add child', items[i]]);
36833             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36834         }
36835         this.toolbar.items = nitems;
36836         
36837         delete config.toolbar;
36838     }
36839     
36840     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36841     config.grid.scrollBody = true;;
36842     config.grid.monitorWindowResize = false; // turn off autosizing
36843     config.grid.autoHeight = false;
36844     config.grid.autoWidth = false;
36845     
36846     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36847     
36848     if (config.background) {
36849         // render grid on panel activation (if panel background)
36850         this.on('activate', function(gp) {
36851             if (!gp.grid.rendered) {
36852                 gp.grid.render(this.wrapper);
36853                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36854             }
36855         });
36856             
36857     } else {
36858         this.grid.render(this.wrapper);
36859         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36860
36861     }
36862     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36863     // ??? needed ??? config.el = this.wrapper;
36864     
36865     
36866     
36867   
36868     // xtype created footer. - not sure if will work as we normally have to render first..
36869     if (this.footer && !this.footer.el && this.footer.xtype) {
36870         
36871         var ctr = this.grid.getView().getFooterPanel(true);
36872         this.footer.dataSource = this.grid.dataSource;
36873         this.footer = Roo.factory(this.footer, Roo);
36874         this.footer.render(ctr);
36875         
36876     }
36877     
36878     
36879     
36880     
36881      
36882 };
36883
36884 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36885     getId : function(){
36886         return this.grid.id;
36887     },
36888     
36889     /**
36890      * Returns the grid for this panel
36891      * @return {Roo.bootstrap.Table} 
36892      */
36893     getGrid : function(){
36894         return this.grid;    
36895     },
36896     
36897     setSize : function(width, height){
36898         if(!this.ignoreResize(width, height)){
36899             var grid = this.grid;
36900             var size = this.adjustForComponents(width, height);
36901             var gridel = grid.getGridEl();
36902             gridel.setSize(size.width, size.height);
36903             /*
36904             var thd = grid.getGridEl().select('thead',true).first();
36905             var tbd = grid.getGridEl().select('tbody', true).first();
36906             if (tbd) {
36907                 tbd.setSize(width, height - thd.getHeight());
36908             }
36909             */
36910             grid.autoSize();
36911         }
36912     },
36913      
36914     
36915     
36916     beforeSlide : function(){
36917         this.grid.getView().scroller.clip();
36918     },
36919     
36920     afterSlide : function(){
36921         this.grid.getView().scroller.unclip();
36922     },
36923     
36924     destroy : function(){
36925         this.grid.destroy();
36926         delete this.grid;
36927         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36928     }
36929 });
36930
36931 /**
36932  * @class Roo.bootstrap.panel.Nest
36933  * @extends Roo.bootstrap.panel.Content
36934  * @constructor
36935  * Create a new Panel, that can contain a layout.Border.
36936  * 
36937  * 
36938  * @param {Roo.BorderLayout} layout The layout for this panel
36939  * @param {String/Object} config A string to set only the title or a config object
36940  */
36941 Roo.bootstrap.panel.Nest = function(config)
36942 {
36943     // construct with only one argument..
36944     /* FIXME - implement nicer consturctors
36945     if (layout.layout) {
36946         config = layout;
36947         layout = config.layout;
36948         delete config.layout;
36949     }
36950     if (layout.xtype && !layout.getEl) {
36951         // then layout needs constructing..
36952         layout = Roo.factory(layout, Roo);
36953     }
36954     */
36955     
36956     config.el =  config.layout.getEl();
36957     
36958     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36959     
36960     config.layout.monitorWindowResize = false; // turn off autosizing
36961     this.layout = config.layout;
36962     this.layout.getEl().addClass("roo-layout-nested-layout");
36963     
36964     
36965     
36966     
36967 };
36968
36969 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36970
36971     setSize : function(width, height){
36972         if(!this.ignoreResize(width, height)){
36973             var size = this.adjustForComponents(width, height);
36974             var el = this.layout.getEl();
36975             if (size.height < 1) {
36976                 el.setWidth(size.width);   
36977             } else {
36978                 el.setSize(size.width, size.height);
36979             }
36980             var touch = el.dom.offsetWidth;
36981             this.layout.layout();
36982             // ie requires a double layout on the first pass
36983             if(Roo.isIE && !this.initialized){
36984                 this.initialized = true;
36985                 this.layout.layout();
36986             }
36987         }
36988     },
36989     
36990     // activate all subpanels if not currently active..
36991     
36992     setActiveState : function(active){
36993         this.active = active;
36994         this.setActiveClass(active);
36995         
36996         if(!active){
36997             this.fireEvent("deactivate", this);
36998             return;
36999         }
37000         
37001         this.fireEvent("activate", this);
37002         // not sure if this should happen before or after..
37003         if (!this.layout) {
37004             return; // should not happen..
37005         }
37006         var reg = false;
37007         for (var r in this.layout.regions) {
37008             reg = this.layout.getRegion(r);
37009             if (reg.getActivePanel()) {
37010                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37011                 reg.setActivePanel(reg.getActivePanel());
37012                 continue;
37013             }
37014             if (!reg.panels.length) {
37015                 continue;
37016             }
37017             reg.showPanel(reg.getPanel(0));
37018         }
37019         
37020         
37021         
37022         
37023     },
37024     
37025     /**
37026      * Returns the nested BorderLayout for this panel
37027      * @return {Roo.BorderLayout} 
37028      */
37029     getLayout : function(){
37030         return this.layout;
37031     },
37032     
37033      /**
37034      * Adds a xtype elements to the layout of the nested panel
37035      * <pre><code>
37036
37037 panel.addxtype({
37038        xtype : 'ContentPanel',
37039        region: 'west',
37040        items: [ .... ]
37041    }
37042 );
37043
37044 panel.addxtype({
37045         xtype : 'NestedLayoutPanel',
37046         region: 'west',
37047         layout: {
37048            center: { },
37049            west: { }   
37050         },
37051         items : [ ... list of content panels or nested layout panels.. ]
37052    }
37053 );
37054 </code></pre>
37055      * @param {Object} cfg Xtype definition of item to add.
37056      */
37057     addxtype : function(cfg) {
37058         return this.layout.addxtype(cfg);
37059     
37060     }
37061 });        /*
37062  * Based on:
37063  * Ext JS Library 1.1.1
37064  * Copyright(c) 2006-2007, Ext JS, LLC.
37065  *
37066  * Originally Released Under LGPL - original licence link has changed is not relivant.
37067  *
37068  * Fork - LGPL
37069  * <script type="text/javascript">
37070  */
37071 /**
37072  * @class Roo.TabPanel
37073  * @extends Roo.util.Observable
37074  * A lightweight tab container.
37075  * <br><br>
37076  * Usage:
37077  * <pre><code>
37078 // basic tabs 1, built from existing content
37079 var tabs = new Roo.TabPanel("tabs1");
37080 tabs.addTab("script", "View Script");
37081 tabs.addTab("markup", "View Markup");
37082 tabs.activate("script");
37083
37084 // more advanced tabs, built from javascript
37085 var jtabs = new Roo.TabPanel("jtabs");
37086 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37087
37088 // set up the UpdateManager
37089 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37090 var updater = tab2.getUpdateManager();
37091 updater.setDefaultUrl("ajax1.htm");
37092 tab2.on('activate', updater.refresh, updater, true);
37093
37094 // Use setUrl for Ajax loading
37095 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37096 tab3.setUrl("ajax2.htm", null, true);
37097
37098 // Disabled tab
37099 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37100 tab4.disable();
37101
37102 jtabs.activate("jtabs-1");
37103  * </code></pre>
37104  * @constructor
37105  * Create a new TabPanel.
37106  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37107  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37108  */
37109 Roo.bootstrap.panel.Tabs = function(config){
37110     /**
37111     * The container element for this TabPanel.
37112     * @type Roo.Element
37113     */
37114     this.el = Roo.get(config.el);
37115     delete config.el;
37116     if(config){
37117         if(typeof config == "boolean"){
37118             this.tabPosition = config ? "bottom" : "top";
37119         }else{
37120             Roo.apply(this, config);
37121         }
37122     }
37123     
37124     if(this.tabPosition == "bottom"){
37125         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37126         this.el.addClass("roo-tabs-bottom");
37127     }
37128     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37129     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37130     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37131     if(Roo.isIE){
37132         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37133     }
37134     if(this.tabPosition != "bottom"){
37135         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37136          * @type Roo.Element
37137          */
37138         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37139         this.el.addClass("roo-tabs-top");
37140     }
37141     this.items = [];
37142
37143     this.bodyEl.setStyle("position", "relative");
37144
37145     this.active = null;
37146     this.activateDelegate = this.activate.createDelegate(this);
37147
37148     this.addEvents({
37149         /**
37150          * @event tabchange
37151          * Fires when the active tab changes
37152          * @param {Roo.TabPanel} this
37153          * @param {Roo.TabPanelItem} activePanel The new active tab
37154          */
37155         "tabchange": true,
37156         /**
37157          * @event beforetabchange
37158          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37159          * @param {Roo.TabPanel} this
37160          * @param {Object} e Set cancel to true on this object to cancel the tab change
37161          * @param {Roo.TabPanelItem} tab The tab being changed to
37162          */
37163         "beforetabchange" : true
37164     });
37165
37166     Roo.EventManager.onWindowResize(this.onResize, this);
37167     this.cpad = this.el.getPadding("lr");
37168     this.hiddenCount = 0;
37169
37170
37171     // toolbar on the tabbar support...
37172     if (this.toolbar) {
37173         alert("no toolbar support yet");
37174         this.toolbar  = false;
37175         /*
37176         var tcfg = this.toolbar;
37177         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37178         this.toolbar = new Roo.Toolbar(tcfg);
37179         if (Roo.isSafari) {
37180             var tbl = tcfg.container.child('table', true);
37181             tbl.setAttribute('width', '100%');
37182         }
37183         */
37184         
37185     }
37186    
37187
37188
37189     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37190 };
37191
37192 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37193     /*
37194      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37195      */
37196     tabPosition : "top",
37197     /*
37198      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37199      */
37200     currentTabWidth : 0,
37201     /*
37202      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37203      */
37204     minTabWidth : 40,
37205     /*
37206      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37207      */
37208     maxTabWidth : 250,
37209     /*
37210      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37211      */
37212     preferredTabWidth : 175,
37213     /*
37214      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37215      */
37216     resizeTabs : false,
37217     /*
37218      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37219      */
37220     monitorResize : true,
37221     /*
37222      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37223      */
37224     toolbar : false,
37225
37226     /**
37227      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37228      * @param {String} id The id of the div to use <b>or create</b>
37229      * @param {String} text The text for the tab
37230      * @param {String} content (optional) Content to put in the TabPanelItem body
37231      * @param {Boolean} closable (optional) True to create a close icon on the tab
37232      * @return {Roo.TabPanelItem} The created TabPanelItem
37233      */
37234     addTab : function(id, text, content, closable, tpl)
37235     {
37236         var item = new Roo.bootstrap.panel.TabItem({
37237             panel: this,
37238             id : id,
37239             text : text,
37240             closable : closable,
37241             tpl : tpl
37242         });
37243         this.addTabItem(item);
37244         if(content){
37245             item.setContent(content);
37246         }
37247         return item;
37248     },
37249
37250     /**
37251      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37252      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37253      * @return {Roo.TabPanelItem}
37254      */
37255     getTab : function(id){
37256         return this.items[id];
37257     },
37258
37259     /**
37260      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37261      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37262      */
37263     hideTab : function(id){
37264         var t = this.items[id];
37265         if(!t.isHidden()){
37266            t.setHidden(true);
37267            this.hiddenCount++;
37268            this.autoSizeTabs();
37269         }
37270     },
37271
37272     /**
37273      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37274      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37275      */
37276     unhideTab : function(id){
37277         var t = this.items[id];
37278         if(t.isHidden()){
37279            t.setHidden(false);
37280            this.hiddenCount--;
37281            this.autoSizeTabs();
37282         }
37283     },
37284
37285     /**
37286      * Adds an existing {@link Roo.TabPanelItem}.
37287      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37288      */
37289     addTabItem : function(item){
37290         this.items[item.id] = item;
37291         this.items.push(item);
37292       //  if(this.resizeTabs){
37293     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37294   //         this.autoSizeTabs();
37295 //        }else{
37296 //            item.autoSize();
37297        // }
37298     },
37299
37300     /**
37301      * Removes a {@link Roo.TabPanelItem}.
37302      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37303      */
37304     removeTab : function(id){
37305         var items = this.items;
37306         var tab = items[id];
37307         if(!tab) { return; }
37308         var index = items.indexOf(tab);
37309         if(this.active == tab && items.length > 1){
37310             var newTab = this.getNextAvailable(index);
37311             if(newTab) {
37312                 newTab.activate();
37313             }
37314         }
37315         this.stripEl.dom.removeChild(tab.pnode.dom);
37316         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37317             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37318         }
37319         items.splice(index, 1);
37320         delete this.items[tab.id];
37321         tab.fireEvent("close", tab);
37322         tab.purgeListeners();
37323         this.autoSizeTabs();
37324     },
37325
37326     getNextAvailable : function(start){
37327         var items = this.items;
37328         var index = start;
37329         // look for a next tab that will slide over to
37330         // replace the one being removed
37331         while(index < items.length){
37332             var item = items[++index];
37333             if(item && !item.isHidden()){
37334                 return item;
37335             }
37336         }
37337         // if one isn't found select the previous tab (on the left)
37338         index = start;
37339         while(index >= 0){
37340             var item = items[--index];
37341             if(item && !item.isHidden()){
37342                 return item;
37343             }
37344         }
37345         return null;
37346     },
37347
37348     /**
37349      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37350      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37351      */
37352     disableTab : function(id){
37353         var tab = this.items[id];
37354         if(tab && this.active != tab){
37355             tab.disable();
37356         }
37357     },
37358
37359     /**
37360      * Enables a {@link Roo.TabPanelItem} that is disabled.
37361      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37362      */
37363     enableTab : function(id){
37364         var tab = this.items[id];
37365         tab.enable();
37366     },
37367
37368     /**
37369      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37370      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37371      * @return {Roo.TabPanelItem} The TabPanelItem.
37372      */
37373     activate : function(id){
37374         var tab = this.items[id];
37375         if(!tab){
37376             return null;
37377         }
37378         if(tab == this.active || tab.disabled){
37379             return tab;
37380         }
37381         var e = {};
37382         this.fireEvent("beforetabchange", this, e, tab);
37383         if(e.cancel !== true && !tab.disabled){
37384             if(this.active){
37385                 this.active.hide();
37386             }
37387             this.active = this.items[id];
37388             this.active.show();
37389             this.fireEvent("tabchange", this, this.active);
37390         }
37391         return tab;
37392     },
37393
37394     /**
37395      * Gets the active {@link Roo.TabPanelItem}.
37396      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37397      */
37398     getActiveTab : function(){
37399         return this.active;
37400     },
37401
37402     /**
37403      * Updates the tab body element to fit the height of the container element
37404      * for overflow scrolling
37405      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37406      */
37407     syncHeight : function(targetHeight){
37408         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37409         var bm = this.bodyEl.getMargins();
37410         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37411         this.bodyEl.setHeight(newHeight);
37412         return newHeight;
37413     },
37414
37415     onResize : function(){
37416         if(this.monitorResize){
37417             this.autoSizeTabs();
37418         }
37419     },
37420
37421     /**
37422      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37423      */
37424     beginUpdate : function(){
37425         this.updating = true;
37426     },
37427
37428     /**
37429      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37430      */
37431     endUpdate : function(){
37432         this.updating = false;
37433         this.autoSizeTabs();
37434     },
37435
37436     /**
37437      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37438      */
37439     autoSizeTabs : function(){
37440         var count = this.items.length;
37441         var vcount = count - this.hiddenCount;
37442         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37443             return;
37444         }
37445         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37446         var availWidth = Math.floor(w / vcount);
37447         var b = this.stripBody;
37448         if(b.getWidth() > w){
37449             var tabs = this.items;
37450             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37451             if(availWidth < this.minTabWidth){
37452                 /*if(!this.sleft){    // incomplete scrolling code
37453                     this.createScrollButtons();
37454                 }
37455                 this.showScroll();
37456                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37457             }
37458         }else{
37459             if(this.currentTabWidth < this.preferredTabWidth){
37460                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37461             }
37462         }
37463     },
37464
37465     /**
37466      * Returns the number of tabs in this TabPanel.
37467      * @return {Number}
37468      */
37469      getCount : function(){
37470          return this.items.length;
37471      },
37472
37473     /**
37474      * Resizes all the tabs to the passed width
37475      * @param {Number} The new width
37476      */
37477     setTabWidth : function(width){
37478         this.currentTabWidth = width;
37479         for(var i = 0, len = this.items.length; i < len; i++) {
37480                 if(!this.items[i].isHidden()) {
37481                 this.items[i].setWidth(width);
37482             }
37483         }
37484     },
37485
37486     /**
37487      * Destroys this TabPanel
37488      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37489      */
37490     destroy : function(removeEl){
37491         Roo.EventManager.removeResizeListener(this.onResize, this);
37492         for(var i = 0, len = this.items.length; i < len; i++){
37493             this.items[i].purgeListeners();
37494         }
37495         if(removeEl === true){
37496             this.el.update("");
37497             this.el.remove();
37498         }
37499     },
37500     
37501     createStrip : function(container)
37502     {
37503         var strip = document.createElement("nav");
37504         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37505         container.appendChild(strip);
37506         return strip;
37507     },
37508     
37509     createStripList : function(strip)
37510     {
37511         // div wrapper for retard IE
37512         // returns the "tr" element.
37513         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37514         //'<div class="x-tabs-strip-wrap">'+
37515           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37516           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37517         return strip.firstChild; //.firstChild.firstChild.firstChild;
37518     },
37519     createBody : function(container)
37520     {
37521         var body = document.createElement("div");
37522         Roo.id(body, "tab-body");
37523         //Roo.fly(body).addClass("x-tabs-body");
37524         Roo.fly(body).addClass("tab-content");
37525         container.appendChild(body);
37526         return body;
37527     },
37528     createItemBody :function(bodyEl, id){
37529         var body = Roo.getDom(id);
37530         if(!body){
37531             body = document.createElement("div");
37532             body.id = id;
37533         }
37534         //Roo.fly(body).addClass("x-tabs-item-body");
37535         Roo.fly(body).addClass("tab-pane");
37536          bodyEl.insertBefore(body, bodyEl.firstChild);
37537         return body;
37538     },
37539     /** @private */
37540     createStripElements :  function(stripEl, text, closable, tpl)
37541     {
37542         var td = document.createElement("li"); // was td..
37543         
37544         
37545         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37546         
37547         
37548         stripEl.appendChild(td);
37549         /*if(closable){
37550             td.className = "x-tabs-closable";
37551             if(!this.closeTpl){
37552                 this.closeTpl = new Roo.Template(
37553                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37554                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37555                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37556                 );
37557             }
37558             var el = this.closeTpl.overwrite(td, {"text": text});
37559             var close = el.getElementsByTagName("div")[0];
37560             var inner = el.getElementsByTagName("em")[0];
37561             return {"el": el, "close": close, "inner": inner};
37562         } else {
37563         */
37564         // not sure what this is..
37565 //            if(!this.tabTpl){
37566                 //this.tabTpl = new Roo.Template(
37567                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37568                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37569                 //);
37570 //                this.tabTpl = new Roo.Template(
37571 //                   '<a href="#">' +
37572 //                   '<span unselectable="on"' +
37573 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37574 //                            ' >{text}</span></a>'
37575 //                );
37576 //                
37577 //            }
37578
37579
37580             var template = tpl || this.tabTpl || false;
37581             
37582             if(!template){
37583                 
37584                 template = new Roo.Template(
37585                    '<a href="#">' +
37586                    '<span unselectable="on"' +
37587                             (this.disableTooltips ? '' : ' title="{text}"') +
37588                             ' >{text}</span></a>'
37589                 );
37590             }
37591             
37592             switch (typeof(template)) {
37593                 case 'object' :
37594                     break;
37595                 case 'string' :
37596                     template = new Roo.Template(template);
37597                     break;
37598                 default :
37599                     break;
37600             }
37601             
37602             var el = template.overwrite(td, {"text": text});
37603             
37604             var inner = el.getElementsByTagName("span")[0];
37605             
37606             return {"el": el, "inner": inner};
37607             
37608     }
37609         
37610     
37611 });
37612
37613 /**
37614  * @class Roo.TabPanelItem
37615  * @extends Roo.util.Observable
37616  * Represents an individual item (tab plus body) in a TabPanel.
37617  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37618  * @param {String} id The id of this TabPanelItem
37619  * @param {String} text The text for the tab of this TabPanelItem
37620  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37621  */
37622 Roo.bootstrap.panel.TabItem = function(config){
37623     /**
37624      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37625      * @type Roo.TabPanel
37626      */
37627     this.tabPanel = config.panel;
37628     /**
37629      * The id for this TabPanelItem
37630      * @type String
37631      */
37632     this.id = config.id;
37633     /** @private */
37634     this.disabled = false;
37635     /** @private */
37636     this.text = config.text;
37637     /** @private */
37638     this.loaded = false;
37639     this.closable = config.closable;
37640
37641     /**
37642      * The body element for this TabPanelItem.
37643      * @type Roo.Element
37644      */
37645     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37646     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37647     this.bodyEl.setStyle("display", "block");
37648     this.bodyEl.setStyle("zoom", "1");
37649     //this.hideAction();
37650
37651     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37652     /** @private */
37653     this.el = Roo.get(els.el);
37654     this.inner = Roo.get(els.inner, true);
37655     this.textEl = Roo.get(this.el.dom.firstChild, true);
37656     this.pnode = Roo.get(els.el.parentNode, true);
37657     this.el.on("mousedown", this.onTabMouseDown, this);
37658     this.el.on("click", this.onTabClick, this);
37659     /** @private */
37660     if(config.closable){
37661         var c = Roo.get(els.close, true);
37662         c.dom.title = this.closeText;
37663         c.addClassOnOver("close-over");
37664         c.on("click", this.closeClick, this);
37665      }
37666
37667     this.addEvents({
37668          /**
37669          * @event activate
37670          * Fires when this tab becomes the active tab.
37671          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37672          * @param {Roo.TabPanelItem} this
37673          */
37674         "activate": true,
37675         /**
37676          * @event beforeclose
37677          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37678          * @param {Roo.TabPanelItem} this
37679          * @param {Object} e Set cancel to true on this object to cancel the close.
37680          */
37681         "beforeclose": true,
37682         /**
37683          * @event close
37684          * Fires when this tab is closed.
37685          * @param {Roo.TabPanelItem} this
37686          */
37687          "close": true,
37688         /**
37689          * @event deactivate
37690          * Fires when this tab is no longer the active tab.
37691          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37692          * @param {Roo.TabPanelItem} this
37693          */
37694          "deactivate" : true
37695     });
37696     this.hidden = false;
37697
37698     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37699 };
37700
37701 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37702            {
37703     purgeListeners : function(){
37704        Roo.util.Observable.prototype.purgeListeners.call(this);
37705        this.el.removeAllListeners();
37706     },
37707     /**
37708      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37709      */
37710     show : function(){
37711         this.pnode.addClass("active");
37712         this.showAction();
37713         if(Roo.isOpera){
37714             this.tabPanel.stripWrap.repaint();
37715         }
37716         this.fireEvent("activate", this.tabPanel, this);
37717     },
37718
37719     /**
37720      * Returns true if this tab is the active tab.
37721      * @return {Boolean}
37722      */
37723     isActive : function(){
37724         return this.tabPanel.getActiveTab() == this;
37725     },
37726
37727     /**
37728      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37729      */
37730     hide : function(){
37731         this.pnode.removeClass("active");
37732         this.hideAction();
37733         this.fireEvent("deactivate", this.tabPanel, this);
37734     },
37735
37736     hideAction : function(){
37737         this.bodyEl.hide();
37738         this.bodyEl.setStyle("position", "absolute");
37739         this.bodyEl.setLeft("-20000px");
37740         this.bodyEl.setTop("-20000px");
37741     },
37742
37743     showAction : function(){
37744         this.bodyEl.setStyle("position", "relative");
37745         this.bodyEl.setTop("");
37746         this.bodyEl.setLeft("");
37747         this.bodyEl.show();
37748     },
37749
37750     /**
37751      * Set the tooltip for the tab.
37752      * @param {String} tooltip The tab's tooltip
37753      */
37754     setTooltip : function(text){
37755         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37756             this.textEl.dom.qtip = text;
37757             this.textEl.dom.removeAttribute('title');
37758         }else{
37759             this.textEl.dom.title = text;
37760         }
37761     },
37762
37763     onTabClick : function(e){
37764         e.preventDefault();
37765         this.tabPanel.activate(this.id);
37766     },
37767
37768     onTabMouseDown : function(e){
37769         e.preventDefault();
37770         this.tabPanel.activate(this.id);
37771     },
37772 /*
37773     getWidth : function(){
37774         return this.inner.getWidth();
37775     },
37776
37777     setWidth : function(width){
37778         var iwidth = width - this.pnode.getPadding("lr");
37779         this.inner.setWidth(iwidth);
37780         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37781         this.pnode.setWidth(width);
37782     },
37783 */
37784     /**
37785      * Show or hide the tab
37786      * @param {Boolean} hidden True to hide or false to show.
37787      */
37788     setHidden : function(hidden){
37789         this.hidden = hidden;
37790         this.pnode.setStyle("display", hidden ? "none" : "");
37791     },
37792
37793     /**
37794      * Returns true if this tab is "hidden"
37795      * @return {Boolean}
37796      */
37797     isHidden : function(){
37798         return this.hidden;
37799     },
37800
37801     /**
37802      * Returns the text for this tab
37803      * @return {String}
37804      */
37805     getText : function(){
37806         return this.text;
37807     },
37808     /*
37809     autoSize : function(){
37810         //this.el.beginMeasure();
37811         this.textEl.setWidth(1);
37812         /*
37813          *  #2804 [new] Tabs in Roojs
37814          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37815          */
37816         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37817         //this.el.endMeasure();
37818     //},
37819
37820     /**
37821      * Sets the text for the tab (Note: this also sets the tooltip text)
37822      * @param {String} text The tab's text and tooltip
37823      */
37824     setText : function(text){
37825         this.text = text;
37826         this.textEl.update(text);
37827         this.setTooltip(text);
37828         //if(!this.tabPanel.resizeTabs){
37829         //    this.autoSize();
37830         //}
37831     },
37832     /**
37833      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37834      */
37835     activate : function(){
37836         this.tabPanel.activate(this.id);
37837     },
37838
37839     /**
37840      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37841      */
37842     disable : function(){
37843         if(this.tabPanel.active != this){
37844             this.disabled = true;
37845             this.pnode.addClass("disabled");
37846         }
37847     },
37848
37849     /**
37850      * Enables this TabPanelItem if it was previously disabled.
37851      */
37852     enable : function(){
37853         this.disabled = false;
37854         this.pnode.removeClass("disabled");
37855     },
37856
37857     /**
37858      * Sets the content for this TabPanelItem.
37859      * @param {String} content The content
37860      * @param {Boolean} loadScripts true to look for and load scripts
37861      */
37862     setContent : function(content, loadScripts){
37863         this.bodyEl.update(content, loadScripts);
37864     },
37865
37866     /**
37867      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37868      * @return {Roo.UpdateManager} The UpdateManager
37869      */
37870     getUpdateManager : function(){
37871         return this.bodyEl.getUpdateManager();
37872     },
37873
37874     /**
37875      * Set a URL to be used to load the content for this TabPanelItem.
37876      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37877      * @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)
37878      * @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)
37879      * @return {Roo.UpdateManager} The UpdateManager
37880      */
37881     setUrl : function(url, params, loadOnce){
37882         if(this.refreshDelegate){
37883             this.un('activate', this.refreshDelegate);
37884         }
37885         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37886         this.on("activate", this.refreshDelegate);
37887         return this.bodyEl.getUpdateManager();
37888     },
37889
37890     /** @private */
37891     _handleRefresh : function(url, params, loadOnce){
37892         if(!loadOnce || !this.loaded){
37893             var updater = this.bodyEl.getUpdateManager();
37894             updater.update(url, params, this._setLoaded.createDelegate(this));
37895         }
37896     },
37897
37898     /**
37899      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37900      *   Will fail silently if the setUrl method has not been called.
37901      *   This does not activate the panel, just updates its content.
37902      */
37903     refresh : function(){
37904         if(this.refreshDelegate){
37905            this.loaded = false;
37906            this.refreshDelegate();
37907         }
37908     },
37909
37910     /** @private */
37911     _setLoaded : function(){
37912         this.loaded = true;
37913     },
37914
37915     /** @private */
37916     closeClick : function(e){
37917         var o = {};
37918         e.stopEvent();
37919         this.fireEvent("beforeclose", this, o);
37920         if(o.cancel !== true){
37921             this.tabPanel.removeTab(this.id);
37922         }
37923     },
37924     /**
37925      * The text displayed in the tooltip for the close icon.
37926      * @type String
37927      */
37928     closeText : "Close this tab"
37929 });
37930
37931 Roo.bootstrap.PhoneInputData = function() {
37932     var d = [
37933       [
37934         "Afghanistan (‫افغانستان‬‎)",
37935         "af",
37936         "93"
37937       ],
37938       [
37939         "Albania (Shqipëri)",
37940         "al",
37941         "355"
37942       ],
37943       [
37944         "Algeria (‫الجزائر‬‎)",
37945         "dz",
37946         "213"
37947       ],
37948       [
37949         "American Samoa",
37950         "as",
37951         "1684"
37952       ],
37953       [
37954         "Andorra",
37955         "ad",
37956         "376"
37957       ],
37958       [
37959         "Angola",
37960         "ao",
37961         "244"
37962       ],
37963       [
37964         "Anguilla",
37965         "ai",
37966         "1264"
37967       ],
37968       [
37969         "Antigua and Barbuda",
37970         "ag",
37971         "1268"
37972       ],
37973       [
37974         "Argentina",
37975         "ar",
37976         "54"
37977       ],
37978       [
37979         "Armenia (Հայաստան)",
37980         "am",
37981         "374"
37982       ],
37983       [
37984         "Aruba",
37985         "aw",
37986         "297"
37987       ],
37988       [
37989         "Australia",
37990         "au",
37991         "61",
37992         0
37993       ],
37994       [
37995         "Austria (Österreich)",
37996         "at",
37997         "43"
37998       ],
37999       [
38000         "Azerbaijan (Azərbaycan)",
38001         "az",
38002         "994"
38003       ],
38004       [
38005         "Bahamas",
38006         "bs",
38007         "1242"
38008       ],
38009       [
38010         "Bahrain (‫البحرين‬‎)",
38011         "bh",
38012         "973"
38013       ],
38014       [
38015         "Bangladesh (বাংলাদেশ)",
38016         "bd",
38017         "880"
38018       ],
38019       [
38020         "Barbados",
38021         "bb",
38022         "1246"
38023       ],
38024       [
38025         "Belarus (Беларусь)",
38026         "by",
38027         "375"
38028       ],
38029       [
38030         "Belgium (België)",
38031         "be",
38032         "32"
38033       ],
38034       [
38035         "Belize",
38036         "bz",
38037         "501"
38038       ],
38039       [
38040         "Benin (Bénin)",
38041         "bj",
38042         "229"
38043       ],
38044       [
38045         "Bermuda",
38046         "bm",
38047         "1441"
38048       ],
38049       [
38050         "Bhutan (འབྲུག)",
38051         "bt",
38052         "975"
38053       ],
38054       [
38055         "Bolivia",
38056         "bo",
38057         "591"
38058       ],
38059       [
38060         "Bosnia and Herzegovina (Босна и Херцеговина)",
38061         "ba",
38062         "387"
38063       ],
38064       [
38065         "Botswana",
38066         "bw",
38067         "267"
38068       ],
38069       [
38070         "Brazil (Brasil)",
38071         "br",
38072         "55"
38073       ],
38074       [
38075         "British Indian Ocean Territory",
38076         "io",
38077         "246"
38078       ],
38079       [
38080         "British Virgin Islands",
38081         "vg",
38082         "1284"
38083       ],
38084       [
38085         "Brunei",
38086         "bn",
38087         "673"
38088       ],
38089       [
38090         "Bulgaria (България)",
38091         "bg",
38092         "359"
38093       ],
38094       [
38095         "Burkina Faso",
38096         "bf",
38097         "226"
38098       ],
38099       [
38100         "Burundi (Uburundi)",
38101         "bi",
38102         "257"
38103       ],
38104       [
38105         "Cambodia (កម្ពុជា)",
38106         "kh",
38107         "855"
38108       ],
38109       [
38110         "Cameroon (Cameroun)",
38111         "cm",
38112         "237"
38113       ],
38114       [
38115         "Canada",
38116         "ca",
38117         "1",
38118         1,
38119         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38120       ],
38121       [
38122         "Cape Verde (Kabu Verdi)",
38123         "cv",
38124         "238"
38125       ],
38126       [
38127         "Caribbean Netherlands",
38128         "bq",
38129         "599",
38130         1
38131       ],
38132       [
38133         "Cayman Islands",
38134         "ky",
38135         "1345"
38136       ],
38137       [
38138         "Central African Republic (République centrafricaine)",
38139         "cf",
38140         "236"
38141       ],
38142       [
38143         "Chad (Tchad)",
38144         "td",
38145         "235"
38146       ],
38147       [
38148         "Chile",
38149         "cl",
38150         "56"
38151       ],
38152       [
38153         "China (中国)",
38154         "cn",
38155         "86"
38156       ],
38157       [
38158         "Christmas Island",
38159         "cx",
38160         "61",
38161         2
38162       ],
38163       [
38164         "Cocos (Keeling) Islands",
38165         "cc",
38166         "61",
38167         1
38168       ],
38169       [
38170         "Colombia",
38171         "co",
38172         "57"
38173       ],
38174       [
38175         "Comoros (‫جزر القمر‬‎)",
38176         "km",
38177         "269"
38178       ],
38179       [
38180         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38181         "cd",
38182         "243"
38183       ],
38184       [
38185         "Congo (Republic) (Congo-Brazzaville)",
38186         "cg",
38187         "242"
38188       ],
38189       [
38190         "Cook Islands",
38191         "ck",
38192         "682"
38193       ],
38194       [
38195         "Costa Rica",
38196         "cr",
38197         "506"
38198       ],
38199       [
38200         "Côte d’Ivoire",
38201         "ci",
38202         "225"
38203       ],
38204       [
38205         "Croatia (Hrvatska)",
38206         "hr",
38207         "385"
38208       ],
38209       [
38210         "Cuba",
38211         "cu",
38212         "53"
38213       ],
38214       [
38215         "Curaçao",
38216         "cw",
38217         "599",
38218         0
38219       ],
38220       [
38221         "Cyprus (Κύπρος)",
38222         "cy",
38223         "357"
38224       ],
38225       [
38226         "Czech Republic (Česká republika)",
38227         "cz",
38228         "420"
38229       ],
38230       [
38231         "Denmark (Danmark)",
38232         "dk",
38233         "45"
38234       ],
38235       [
38236         "Djibouti",
38237         "dj",
38238         "253"
38239       ],
38240       [
38241         "Dominica",
38242         "dm",
38243         "1767"
38244       ],
38245       [
38246         "Dominican Republic (República Dominicana)",
38247         "do",
38248         "1",
38249         2,
38250         ["809", "829", "849"]
38251       ],
38252       [
38253         "Ecuador",
38254         "ec",
38255         "593"
38256       ],
38257       [
38258         "Egypt (‫مصر‬‎)",
38259         "eg",
38260         "20"
38261       ],
38262       [
38263         "El Salvador",
38264         "sv",
38265         "503"
38266       ],
38267       [
38268         "Equatorial Guinea (Guinea Ecuatorial)",
38269         "gq",
38270         "240"
38271       ],
38272       [
38273         "Eritrea",
38274         "er",
38275         "291"
38276       ],
38277       [
38278         "Estonia (Eesti)",
38279         "ee",
38280         "372"
38281       ],
38282       [
38283         "Ethiopia",
38284         "et",
38285         "251"
38286       ],
38287       [
38288         "Falkland Islands (Islas Malvinas)",
38289         "fk",
38290         "500"
38291       ],
38292       [
38293         "Faroe Islands (Føroyar)",
38294         "fo",
38295         "298"
38296       ],
38297       [
38298         "Fiji",
38299         "fj",
38300         "679"
38301       ],
38302       [
38303         "Finland (Suomi)",
38304         "fi",
38305         "358",
38306         0
38307       ],
38308       [
38309         "France",
38310         "fr",
38311         "33"
38312       ],
38313       [
38314         "French Guiana (Guyane française)",
38315         "gf",
38316         "594"
38317       ],
38318       [
38319         "French Polynesia (Polynésie française)",
38320         "pf",
38321         "689"
38322       ],
38323       [
38324         "Gabon",
38325         "ga",
38326         "241"
38327       ],
38328       [
38329         "Gambia",
38330         "gm",
38331         "220"
38332       ],
38333       [
38334         "Georgia (საქართველო)",
38335         "ge",
38336         "995"
38337       ],
38338       [
38339         "Germany (Deutschland)",
38340         "de",
38341         "49"
38342       ],
38343       [
38344         "Ghana (Gaana)",
38345         "gh",
38346         "233"
38347       ],
38348       [
38349         "Gibraltar",
38350         "gi",
38351         "350"
38352       ],
38353       [
38354         "Greece (Ελλάδα)",
38355         "gr",
38356         "30"
38357       ],
38358       [
38359         "Greenland (Kalaallit Nunaat)",
38360         "gl",
38361         "299"
38362       ],
38363       [
38364         "Grenada",
38365         "gd",
38366         "1473"
38367       ],
38368       [
38369         "Guadeloupe",
38370         "gp",
38371         "590",
38372         0
38373       ],
38374       [
38375         "Guam",
38376         "gu",
38377         "1671"
38378       ],
38379       [
38380         "Guatemala",
38381         "gt",
38382         "502"
38383       ],
38384       [
38385         "Guernsey",
38386         "gg",
38387         "44",
38388         1
38389       ],
38390       [
38391         "Guinea (Guinée)",
38392         "gn",
38393         "224"
38394       ],
38395       [
38396         "Guinea-Bissau (Guiné Bissau)",
38397         "gw",
38398         "245"
38399       ],
38400       [
38401         "Guyana",
38402         "gy",
38403         "592"
38404       ],
38405       [
38406         "Haiti",
38407         "ht",
38408         "509"
38409       ],
38410       [
38411         "Honduras",
38412         "hn",
38413         "504"
38414       ],
38415       [
38416         "Hong Kong (香港)",
38417         "hk",
38418         "852"
38419       ],
38420       [
38421         "Hungary (Magyarország)",
38422         "hu",
38423         "36"
38424       ],
38425       [
38426         "Iceland (Ísland)",
38427         "is",
38428         "354"
38429       ],
38430       [
38431         "India (भारत)",
38432         "in",
38433         "91"
38434       ],
38435       [
38436         "Indonesia",
38437         "id",
38438         "62"
38439       ],
38440       [
38441         "Iran (‫ایران‬‎)",
38442         "ir",
38443         "98"
38444       ],
38445       [
38446         "Iraq (‫العراق‬‎)",
38447         "iq",
38448         "964"
38449       ],
38450       [
38451         "Ireland",
38452         "ie",
38453         "353"
38454       ],
38455       [
38456         "Isle of Man",
38457         "im",
38458         "44",
38459         2
38460       ],
38461       [
38462         "Israel (‫ישראל‬‎)",
38463         "il",
38464         "972"
38465       ],
38466       [
38467         "Italy (Italia)",
38468         "it",
38469         "39",
38470         0
38471       ],
38472       [
38473         "Jamaica",
38474         "jm",
38475         "1876"
38476       ],
38477       [
38478         "Japan (日本)",
38479         "jp",
38480         "81"
38481       ],
38482       [
38483         "Jersey",
38484         "je",
38485         "44",
38486         3
38487       ],
38488       [
38489         "Jordan (‫الأردن‬‎)",
38490         "jo",
38491         "962"
38492       ],
38493       [
38494         "Kazakhstan (Казахстан)",
38495         "kz",
38496         "7",
38497         1
38498       ],
38499       [
38500         "Kenya",
38501         "ke",
38502         "254"
38503       ],
38504       [
38505         "Kiribati",
38506         "ki",
38507         "686"
38508       ],
38509       [
38510         "Kosovo",
38511         "xk",
38512         "383"
38513       ],
38514       [
38515         "Kuwait (‫الكويت‬‎)",
38516         "kw",
38517         "965"
38518       ],
38519       [
38520         "Kyrgyzstan (Кыргызстан)",
38521         "kg",
38522         "996"
38523       ],
38524       [
38525         "Laos (ລາວ)",
38526         "la",
38527         "856"
38528       ],
38529       [
38530         "Latvia (Latvija)",
38531         "lv",
38532         "371"
38533       ],
38534       [
38535         "Lebanon (‫لبنان‬‎)",
38536         "lb",
38537         "961"
38538       ],
38539       [
38540         "Lesotho",
38541         "ls",
38542         "266"
38543       ],
38544       [
38545         "Liberia",
38546         "lr",
38547         "231"
38548       ],
38549       [
38550         "Libya (‫ليبيا‬‎)",
38551         "ly",
38552         "218"
38553       ],
38554       [
38555         "Liechtenstein",
38556         "li",
38557         "423"
38558       ],
38559       [
38560         "Lithuania (Lietuva)",
38561         "lt",
38562         "370"
38563       ],
38564       [
38565         "Luxembourg",
38566         "lu",
38567         "352"
38568       ],
38569       [
38570         "Macau (澳門)",
38571         "mo",
38572         "853"
38573       ],
38574       [
38575         "Macedonia (FYROM) (Македонија)",
38576         "mk",
38577         "389"
38578       ],
38579       [
38580         "Madagascar (Madagasikara)",
38581         "mg",
38582         "261"
38583       ],
38584       [
38585         "Malawi",
38586         "mw",
38587         "265"
38588       ],
38589       [
38590         "Malaysia",
38591         "my",
38592         "60"
38593       ],
38594       [
38595         "Maldives",
38596         "mv",
38597         "960"
38598       ],
38599       [
38600         "Mali",
38601         "ml",
38602         "223"
38603       ],
38604       [
38605         "Malta",
38606         "mt",
38607         "356"
38608       ],
38609       [
38610         "Marshall Islands",
38611         "mh",
38612         "692"
38613       ],
38614       [
38615         "Martinique",
38616         "mq",
38617         "596"
38618       ],
38619       [
38620         "Mauritania (‫موريتانيا‬‎)",
38621         "mr",
38622         "222"
38623       ],
38624       [
38625         "Mauritius (Moris)",
38626         "mu",
38627         "230"
38628       ],
38629       [
38630         "Mayotte",
38631         "yt",
38632         "262",
38633         1
38634       ],
38635       [
38636         "Mexico (México)",
38637         "mx",
38638         "52"
38639       ],
38640       [
38641         "Micronesia",
38642         "fm",
38643         "691"
38644       ],
38645       [
38646         "Moldova (Republica Moldova)",
38647         "md",
38648         "373"
38649       ],
38650       [
38651         "Monaco",
38652         "mc",
38653         "377"
38654       ],
38655       [
38656         "Mongolia (Монгол)",
38657         "mn",
38658         "976"
38659       ],
38660       [
38661         "Montenegro (Crna Gora)",
38662         "me",
38663         "382"
38664       ],
38665       [
38666         "Montserrat",
38667         "ms",
38668         "1664"
38669       ],
38670       [
38671         "Morocco (‫المغرب‬‎)",
38672         "ma",
38673         "212",
38674         0
38675       ],
38676       [
38677         "Mozambique (Moçambique)",
38678         "mz",
38679         "258"
38680       ],
38681       [
38682         "Myanmar (Burma) (မြန်မာ)",
38683         "mm",
38684         "95"
38685       ],
38686       [
38687         "Namibia (Namibië)",
38688         "na",
38689         "264"
38690       ],
38691       [
38692         "Nauru",
38693         "nr",
38694         "674"
38695       ],
38696       [
38697         "Nepal (नेपाल)",
38698         "np",
38699         "977"
38700       ],
38701       [
38702         "Netherlands (Nederland)",
38703         "nl",
38704         "31"
38705       ],
38706       [
38707         "New Caledonia (Nouvelle-Calédonie)",
38708         "nc",
38709         "687"
38710       ],
38711       [
38712         "New Zealand",
38713         "nz",
38714         "64"
38715       ],
38716       [
38717         "Nicaragua",
38718         "ni",
38719         "505"
38720       ],
38721       [
38722         "Niger (Nijar)",
38723         "ne",
38724         "227"
38725       ],
38726       [
38727         "Nigeria",
38728         "ng",
38729         "234"
38730       ],
38731       [
38732         "Niue",
38733         "nu",
38734         "683"
38735       ],
38736       [
38737         "Norfolk Island",
38738         "nf",
38739         "672"
38740       ],
38741       [
38742         "North Korea (조선 민주주의 인민 공화국)",
38743         "kp",
38744         "850"
38745       ],
38746       [
38747         "Northern Mariana Islands",
38748         "mp",
38749         "1670"
38750       ],
38751       [
38752         "Norway (Norge)",
38753         "no",
38754         "47",
38755         0
38756       ],
38757       [
38758         "Oman (‫عُمان‬‎)",
38759         "om",
38760         "968"
38761       ],
38762       [
38763         "Pakistan (‫پاکستان‬‎)",
38764         "pk",
38765         "92"
38766       ],
38767       [
38768         "Palau",
38769         "pw",
38770         "680"
38771       ],
38772       [
38773         "Palestine (‫فلسطين‬‎)",
38774         "ps",
38775         "970"
38776       ],
38777       [
38778         "Panama (Panamá)",
38779         "pa",
38780         "507"
38781       ],
38782       [
38783         "Papua New Guinea",
38784         "pg",
38785         "675"
38786       ],
38787       [
38788         "Paraguay",
38789         "py",
38790         "595"
38791       ],
38792       [
38793         "Peru (Perú)",
38794         "pe",
38795         "51"
38796       ],
38797       [
38798         "Philippines",
38799         "ph",
38800         "63"
38801       ],
38802       [
38803         "Poland (Polska)",
38804         "pl",
38805         "48"
38806       ],
38807       [
38808         "Portugal",
38809         "pt",
38810         "351"
38811       ],
38812       [
38813         "Puerto Rico",
38814         "pr",
38815         "1",
38816         3,
38817         ["787", "939"]
38818       ],
38819       [
38820         "Qatar (‫قطر‬‎)",
38821         "qa",
38822         "974"
38823       ],
38824       [
38825         "Réunion (La Réunion)",
38826         "re",
38827         "262",
38828         0
38829       ],
38830       [
38831         "Romania (România)",
38832         "ro",
38833         "40"
38834       ],
38835       [
38836         "Russia (Россия)",
38837         "ru",
38838         "7",
38839         0
38840       ],
38841       [
38842         "Rwanda",
38843         "rw",
38844         "250"
38845       ],
38846       [
38847         "Saint Barthélemy",
38848         "bl",
38849         "590",
38850         1
38851       ],
38852       [
38853         "Saint Helena",
38854         "sh",
38855         "290"
38856       ],
38857       [
38858         "Saint Kitts and Nevis",
38859         "kn",
38860         "1869"
38861       ],
38862       [
38863         "Saint Lucia",
38864         "lc",
38865         "1758"
38866       ],
38867       [
38868         "Saint Martin (Saint-Martin (partie française))",
38869         "mf",
38870         "590",
38871         2
38872       ],
38873       [
38874         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38875         "pm",
38876         "508"
38877       ],
38878       [
38879         "Saint Vincent and the Grenadines",
38880         "vc",
38881         "1784"
38882       ],
38883       [
38884         "Samoa",
38885         "ws",
38886         "685"
38887       ],
38888       [
38889         "San Marino",
38890         "sm",
38891         "378"
38892       ],
38893       [
38894         "São Tomé and Príncipe (São Tomé e Príncipe)",
38895         "st",
38896         "239"
38897       ],
38898       [
38899         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38900         "sa",
38901         "966"
38902       ],
38903       [
38904         "Senegal (Sénégal)",
38905         "sn",
38906         "221"
38907       ],
38908       [
38909         "Serbia (Србија)",
38910         "rs",
38911         "381"
38912       ],
38913       [
38914         "Seychelles",
38915         "sc",
38916         "248"
38917       ],
38918       [
38919         "Sierra Leone",
38920         "sl",
38921         "232"
38922       ],
38923       [
38924         "Singapore",
38925         "sg",
38926         "65"
38927       ],
38928       [
38929         "Sint Maarten",
38930         "sx",
38931         "1721"
38932       ],
38933       [
38934         "Slovakia (Slovensko)",
38935         "sk",
38936         "421"
38937       ],
38938       [
38939         "Slovenia (Slovenija)",
38940         "si",
38941         "386"
38942       ],
38943       [
38944         "Solomon Islands",
38945         "sb",
38946         "677"
38947       ],
38948       [
38949         "Somalia (Soomaaliya)",
38950         "so",
38951         "252"
38952       ],
38953       [
38954         "South Africa",
38955         "za",
38956         "27"
38957       ],
38958       [
38959         "South Korea (대한민국)",
38960         "kr",
38961         "82"
38962       ],
38963       [
38964         "South Sudan (‫جنوب السودان‬‎)",
38965         "ss",
38966         "211"
38967       ],
38968       [
38969         "Spain (España)",
38970         "es",
38971         "34"
38972       ],
38973       [
38974         "Sri Lanka (ශ්‍රී ලංකාව)",
38975         "lk",
38976         "94"
38977       ],
38978       [
38979         "Sudan (‫السودان‬‎)",
38980         "sd",
38981         "249"
38982       ],
38983       [
38984         "Suriname",
38985         "sr",
38986         "597"
38987       ],
38988       [
38989         "Svalbard and Jan Mayen",
38990         "sj",
38991         "47",
38992         1
38993       ],
38994       [
38995         "Swaziland",
38996         "sz",
38997         "268"
38998       ],
38999       [
39000         "Sweden (Sverige)",
39001         "se",
39002         "46"
39003       ],
39004       [
39005         "Switzerland (Schweiz)",
39006         "ch",
39007         "41"
39008       ],
39009       [
39010         "Syria (‫سوريا‬‎)",
39011         "sy",
39012         "963"
39013       ],
39014       [
39015         "Taiwan (台灣)",
39016         "tw",
39017         "886"
39018       ],
39019       [
39020         "Tajikistan",
39021         "tj",
39022         "992"
39023       ],
39024       [
39025         "Tanzania",
39026         "tz",
39027         "255"
39028       ],
39029       [
39030         "Thailand (ไทย)",
39031         "th",
39032         "66"
39033       ],
39034       [
39035         "Timor-Leste",
39036         "tl",
39037         "670"
39038       ],
39039       [
39040         "Togo",
39041         "tg",
39042         "228"
39043       ],
39044       [
39045         "Tokelau",
39046         "tk",
39047         "690"
39048       ],
39049       [
39050         "Tonga",
39051         "to",
39052         "676"
39053       ],
39054       [
39055         "Trinidad and Tobago",
39056         "tt",
39057         "1868"
39058       ],
39059       [
39060         "Tunisia (‫تونس‬‎)",
39061         "tn",
39062         "216"
39063       ],
39064       [
39065         "Turkey (Türkiye)",
39066         "tr",
39067         "90"
39068       ],
39069       [
39070         "Turkmenistan",
39071         "tm",
39072         "993"
39073       ],
39074       [
39075         "Turks and Caicos Islands",
39076         "tc",
39077         "1649"
39078       ],
39079       [
39080         "Tuvalu",
39081         "tv",
39082         "688"
39083       ],
39084       [
39085         "U.S. Virgin Islands",
39086         "vi",
39087         "1340"
39088       ],
39089       [
39090         "Uganda",
39091         "ug",
39092         "256"
39093       ],
39094       [
39095         "Ukraine (Україна)",
39096         "ua",
39097         "380"
39098       ],
39099       [
39100         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39101         "ae",
39102         "971"
39103       ],
39104       [
39105         "United Kingdom",
39106         "gb",
39107         "44",
39108         0
39109       ],
39110       [
39111         "United States",
39112         "us",
39113         "1",
39114         0
39115       ],
39116       [
39117         "Uruguay",
39118         "uy",
39119         "598"
39120       ],
39121       [
39122         "Uzbekistan (Oʻzbekiston)",
39123         "uz",
39124         "998"
39125       ],
39126       [
39127         "Vanuatu",
39128         "vu",
39129         "678"
39130       ],
39131       [
39132         "Vatican City (Città del Vaticano)",
39133         "va",
39134         "39",
39135         1
39136       ],
39137       [
39138         "Venezuela",
39139         "ve",
39140         "58"
39141       ],
39142       [
39143         "Vietnam (Việt Nam)",
39144         "vn",
39145         "84"
39146       ],
39147       [
39148         "Wallis and Futuna (Wallis-et-Futuna)",
39149         "wf",
39150         "681"
39151       ],
39152       [
39153         "Western Sahara (‫الصحراء الغربية‬‎)",
39154         "eh",
39155         "212",
39156         1
39157       ],
39158       [
39159         "Yemen (‫اليمن‬‎)",
39160         "ye",
39161         "967"
39162       ],
39163       [
39164         "Zambia",
39165         "zm",
39166         "260"
39167       ],
39168       [
39169         "Zimbabwe",
39170         "zw",
39171         "263"
39172       ],
39173       [
39174         "Åland Islands",
39175         "ax",
39176         "358",
39177         1
39178       ]
39179   ];
39180   
39181   return d;
39182 }/**
39183 *    This script refer to:
39184 *    Title: International Telephone Input
39185 *    Author: Jack O'Connor
39186 *    Code version:  v12.1.12
39187 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39188 **/
39189
39190 Roo.bootstrap.PhoneInput = function(config) {
39191     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39192 };
39193
39194 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39195         
39196         triggerList : true,
39197         
39198         listWidth: undefined,
39199         
39200         selectedClass: 'active',
39201         
39202         allCountries: false,
39203         
39204         dialCodeMapping: false,
39205         
39206         preferedCountries: false,
39207         
39208         defaultDialCode: '+852',
39209         
39210         getAutoCreate : function()
39211         {
39212             var data = Roo.bootstrap.PhoneInputData;
39213             var align = this.labelAlign || this.parentLabelAlign();
39214             var id = Roo.id();
39215             
39216             for (var i = 0; i < data.length; i++) {
39217               var c = data[i];
39218               this.allCountries[i] = {
39219                 name: c[0],
39220                 iso2: c[1],
39221                 dialCode: c[2],
39222                 priority: c[3] || 0,
39223                 areaCodes: c[4] || null
39224               };
39225               this.dialCodeMapping[c[2]] = {
39226                   name: c[0],
39227                   iso2: c[1],
39228                   priority: c[3] || 0,
39229                   areaCodes: c[4] || null
39230               };
39231             }
39232             
39233             var cfg = {
39234                 cls: 'form-group',
39235                 cn: []
39236             };
39237             
39238             var input =  {
39239                 tag: 'input',
39240                 id : id,
39241                 type : 'number',
39242                 cls : 'form-control tel-input',
39243                 autocomplete: 'new-password'
39244             };
39245             
39246             if (this.name) {
39247                 input.name = this.name;
39248             }
39249             
39250             if (this.disabled) {
39251                 input.disabled = true;
39252             }
39253             
39254             var flag_container = {
39255                 tag: 'div',
39256                 cls: 'flag-box',
39257                 cn: [
39258                     {
39259                         tag: 'div',
39260                         cls: 'flag'
39261                     },
39262                     {
39263                         tag: 'div',
39264                         cls: 'caret'
39265                     }
39266                 ]
39267             };
39268             
39269             var box = {
39270                 tag: 'div',
39271                 cls: this.hasFeedback ? 'has-feedback' : '',
39272                 cn: [
39273                     input,
39274                     {
39275                         tag: 'input',
39276                         cls: 'dial-code-holder',
39277                         disabled: true
39278                     }
39279                 ]
39280             };
39281             
39282             var container = {
39283                 cls: 'roo-select2-container input-group',
39284                 cn: [
39285                     flag_container,
39286                     box
39287                 ]
39288             };
39289             
39290             if (this.fieldLabel.length) {
39291                 var indicator = {
39292                     tag: 'i',
39293                     tooltip: 'This field is required'
39294                 };
39295                 
39296                 var label = {
39297                     tag: 'label',
39298                     'for':  id,
39299                     cls: 'control-label',
39300                     cn: []
39301                 };
39302                 
39303                 var label_text = {
39304                     tag: 'span',
39305                     html: this.fieldLabel
39306                 };
39307                 
39308                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39309                 label.cn = [
39310                     indicator,
39311                     label_text
39312                 ];
39313                 
39314                 if(this.indicatorpos == 'right') {
39315                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39316                     label.cn = [
39317                         label_text,
39318                         indicator
39319                     ];
39320                 }
39321                 
39322                 if(align == 'left') {
39323                     container = {
39324                         tag: 'div',
39325                         cn: [
39326                             container
39327                         ]
39328                     };
39329                     
39330                     if(this.labelWidth > 12){
39331                         label.style = "width: " + this.labelWidth + 'px';
39332                     }
39333                     if(this.labelWidth < 13 && this.labelmd == 0){
39334                         this.labelmd = this.labelWidth;
39335                     }
39336                     if(this.labellg > 0){
39337                         label.cls += ' col-lg-' + this.labellg;
39338                         input.cls += ' col-lg-' + (12 - this.labellg);
39339                     }
39340                     if(this.labelmd > 0){
39341                         label.cls += ' col-md-' + this.labelmd;
39342                         container.cls += ' col-md-' + (12 - this.labelmd);
39343                     }
39344                     if(this.labelsm > 0){
39345                         label.cls += ' col-sm-' + this.labelsm;
39346                         container.cls += ' col-sm-' + (12 - this.labelsm);
39347                     }
39348                     if(this.labelxs > 0){
39349                         label.cls += ' col-xs-' + this.labelxs;
39350                         container.cls += ' col-xs-' + (12 - this.labelxs);
39351                     }
39352                 }
39353             }
39354             
39355             cfg.cn = [
39356                 label,
39357                 container
39358             ];
39359             
39360             var settings = this;
39361             
39362             ['xs','sm','md','lg'].map(function(size){
39363                 if (settings[size]) {
39364                     cfg.cls += ' col-' + size + '-' + settings[size];
39365                 }
39366             });
39367             
39368             this.store = new Roo.data.Store({
39369                 proxy : new Roo.data.MemoryProxy({}),
39370                 reader : new Roo.data.JsonReader({
39371                     fields : [
39372                         {
39373                             'name' : 'name',
39374                             'type' : 'string'
39375                         },
39376                         {
39377                             'name' : 'iso2',
39378                             'type' : 'string'
39379                         },
39380                         {
39381                             'name' : 'dialCode',
39382                             'type' : 'string'
39383                         },
39384                         {
39385                             'name' : 'priority',
39386                             'type' : 'string'
39387                         },
39388                         {
39389                             'name' : 'areaCodes',
39390                             'type' : 'string'
39391                         }
39392                     ]
39393                 })
39394             });
39395             
39396             this.store.proxy.data = {
39397                 success: true,
39398                 data: this.allCountries
39399             };
39400             
39401             return cfg;
39402         },
39403         
39404         initEvents : function()
39405         {
39406             this.createList();
39407             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39408             
39409             this.indicator = this.indicatorEl();
39410             this.flag = this.flagEl();
39411             this.dialCodeHolder = this.dialCodeHolderEl();
39412             
39413             this.trigger = this.el.select('div.flag-box',true).first();
39414             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39415             
39416             var _this = this;
39417             
39418             (function(){
39419                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39420                 _this.list.setWidth(lw);
39421             }).defer(100);
39422             
39423             this.list.on('mouseover', this.onViewOver, this);
39424             this.list.on('mousemove', this.onViewMove, this);
39425             //this.list.on('scroll', this.onViewScroll, this);
39426             
39427             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39428
39429             this.view = new Roo.View(this.list, this.tpl, {
39430                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39431             });
39432             
39433             this.view.on('click', this.onViewClick, this);
39434             this.setValue(this.defaultDialCode);
39435         },
39436         
39437         onTriggerClick : function(e)
39438         {
39439             Roo.log('trigger click');
39440             if(this.disabled || !this.triggerList){
39441                 return;
39442             }
39443             
39444             if(this.isExpanded()){
39445                 this.collapse();
39446                 this.hasFocus = false;
39447             }else {
39448                 this.store.load({});
39449                 this.hasFocus = true;
39450                 this.expand();
39451             }
39452         },
39453         
39454         isExpanded : function()
39455         {
39456             return this.list.isVisible();
39457         },
39458         
39459         collapse : function()
39460         {
39461             if(!this.isExpanded()){
39462                 return;
39463             }
39464             this.list.hide();
39465             Roo.get(document).un('mousedown', this.collapseIf, this);
39466             Roo.get(document).un('mousewheel', this.collapseIf, this);
39467             this.fireEvent('collapse', this);
39468             this.validate();
39469         },
39470         
39471         expand : function()
39472         {
39473             Roo.log('expand');
39474
39475             if(this.isExpanded() || !this.hasFocus){
39476                 return;
39477             }
39478             
39479             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39480             this.list.setWidth(lw);
39481             
39482             this.list.show();
39483             this.restrictHeight();
39484             
39485             Roo.get(document).on('mousedown', this.collapseIf, this);
39486             Roo.get(document).on('mousewheel', this.collapseIf, this);
39487             
39488             this.fireEvent('expand', this);
39489         },
39490         
39491         restrictHeight : function()
39492         {
39493             this.list.alignTo(this.inputEl(), this.listAlign);
39494             this.list.alignTo(this.inputEl(), this.listAlign);
39495         },
39496         
39497         onViewOver : function(e, t)
39498         {
39499             if(this.inKeyMode){
39500                 return;
39501             }
39502             var item = this.view.findItemFromChild(t);
39503             
39504             if(item){
39505                 var index = this.view.indexOf(item);
39506                 this.select(index, false);
39507             }
39508         },
39509
39510         // private
39511         onViewClick : function(view, doFocus, el, e)
39512         {
39513             var index = this.view.getSelectedIndexes()[0];
39514             
39515             var r = this.store.getAt(index);
39516             
39517             if(r){
39518                 this.onSelect(r, index);
39519             }
39520             if(doFocus !== false && !this.blockFocus){
39521                 this.inputEl().focus();
39522             }
39523         },
39524         
39525         onViewMove : function(e, t)
39526         {
39527             this.inKeyMode = false;
39528         },
39529         
39530         select : function(index, scrollIntoView)
39531         {
39532             this.selectedIndex = index;
39533             this.view.select(index);
39534             if(scrollIntoView !== false){
39535                 var el = this.view.getNode(index);
39536                 if(el){
39537                     this.list.scrollChildIntoView(el, false);
39538                 }
39539             }
39540         },
39541         
39542         createList : function()
39543         {
39544             this.list = Roo.get(document.body).createChild({
39545                 tag: 'ul',
39546                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39547                 style: 'display:none'
39548             });
39549             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39550         },
39551         
39552         collapseIf : function(e)
39553         {
39554             var in_combo  = e.within(this.el);
39555             var in_list =  e.within(this.list);
39556             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39557             
39558             if (in_combo || in_list || is_list) {
39559                 return;
39560             }
39561             this.collapse();
39562         },
39563         
39564         onSelect : function(record, index)
39565         {
39566             if(this.fireEvent('beforeselect', this, record, index) !== false){
39567                 
39568                 this.setFlagClass(record.data.iso2);
39569                 this.setDialCode(record.data.dialCode);
39570                 this.hasFocus = false;
39571                 this.collapse();
39572                 this.fireEvent('select', this, record, index);
39573             }
39574         },
39575         
39576         flagEl : function()
39577         {
39578             var flag = this.el.select('div.flag',true).first();
39579             if(!flag){
39580                 return false;
39581             }
39582             return flag;
39583         },
39584         
39585         dialCodeHolderEl : function()
39586         {
39587             var d = this.el.select('input.dial-code-holder',true).first();
39588             if(!d){
39589                 return false;
39590             }
39591             return d;
39592         },
39593         
39594         setDialCode : function(v)
39595         {
39596             this.dialCodeHolder.dom.value = '+'+v;
39597         },
39598         
39599         setFlagClass : function(n)
39600         {
39601             this.flag.dom.className = 'flag '+n;
39602         },
39603         
39604         getValue : function()
39605         {
39606             var v = this.inputEl().getValue();
39607             if(this.dialCodeHolder) {
39608                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39609             }
39610             return v;
39611         },
39612         
39613         setValue : function(v)
39614         {
39615             var d = this.getDialCode(v);
39616             this.value = v;
39617             
39618             if(!d || d.length == 0) {
39619                 if(this.rendered){
39620                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39621                 }
39622                 return;
39623             }
39624             
39625             this.setFlagClass(this.dialCodeMapping[d].iso2);
39626             this.setDialCode(d);
39627             this.inputEl().dom.value = v.replace('+'+d,'');
39628         },
39629         
39630         getDialCode : function(v = '')
39631         {
39632             if (v.length == 0) {
39633                 return this.dialCodeHolder.dom.value;
39634             }
39635             
39636             var dialCode = "";
39637             // only interested in international numbers (starting with a plus)
39638             if (v.charAt(0) != "+") {
39639                 return false;
39640             }
39641             var numericChars = "";
39642             for (var i = 1; i < v.length; i++) {
39643               var c = v.charAt(i);
39644               if (!isNaN(c)) {
39645                 numericChars += c;
39646                 if (this.dialCodeMapping[numericChars]) {
39647                   dialCode = v.substr(1, i);
39648                 }
39649                 if (numericChars.length == 4) {
39650                   break;
39651                 }
39652               }
39653             }
39654             return dialCode;
39655         },
39656         
39657         validate : function()
39658         {
39659             //
39660             return false;
39661         }
39662 });