css-bootstrap/roojs-bootstrap-debug.css.map
[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().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  * {bool} open is the menu open
4569  * @constructor
4570  * Create a new Navbar Button
4571  * @param {Object} config The config object
4572  */
4573 Roo.bootstrap.NavSidebarItem = function(config){
4574     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4575     this.addEvents({
4576         // raw events
4577         /**
4578          * @event click
4579          * The raw click event for the entire grid.
4580          * @param {Roo.EventObject} e
4581          */
4582         "click" : true,
4583          /**
4584             * @event changed
4585             * Fires when the active item active state changes
4586             * @param {Roo.bootstrap.NavSidebarItem} this
4587             * @param {boolean} state the new state
4588              
4589          */
4590         'changed': true
4591     });
4592    
4593 };
4594
4595 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4596     
4597     badgeWeight : 'default',
4598     
4599     open: false,
4600     
4601     getAutoCreate : function(){
4602         
4603         
4604         var a = {
4605                 tag: 'a',
4606                 href : this.href || '#',
4607                 cls: '',
4608                 html : '',
4609                 cn : []
4610         };
4611         var cfg = {
4612             tag: 'li',
4613             cls: '',
4614             cn: [ a ]
4615         };
4616         var span = {
4617             tag: 'span',
4618             html : this.html || ''
4619         };
4620         
4621         
4622         if (this.active) {
4623             cfg.cls += ' active';
4624         }
4625         
4626         if (this.disabled) {
4627             cfg.cls += ' disabled';
4628         }
4629         if (this.open) {
4630             cfg.cls += ' open x-open';
4631         }
4632         // left icon..
4633         if (this.glyphicon || this.icon) {
4634             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4635             a.cn.push({ tag : 'i', cls : c }) ;
4636         }
4637         // html..
4638         a.cn.push(span);
4639         // then badge..
4640         if (this.badge !== '') {
4641             
4642             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4643         }
4644         // fi
4645         if (this.menu) {
4646             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4647             a.cls += 'dropdown-toggle treeview' ;
4648         }
4649         
4650         return cfg;
4651          
4652            
4653     },
4654     
4655     initEvents : function()
4656     { 
4657         if (typeof (this.menu) != 'undefined') {
4658             this.menu.parentType = this.xtype;
4659             this.menu.triggerEl = this.el;
4660             this.menu = this.addxtype(Roo.apply({}, this.menu));
4661         }
4662         
4663         this.el.on('click', this.onClick, this);
4664        
4665     
4666         if(this.badge !== ''){
4667  
4668             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4669         }
4670         
4671     },
4672     
4673     onClick : function(e)
4674     {
4675         if(this.disabled){
4676             e.preventDefault();
4677             return;
4678         }
4679         
4680         if(this.preventDefault){
4681             e.preventDefault();
4682         }
4683         
4684         this.fireEvent('click', this);
4685     },
4686     
4687     disable : function()
4688     {
4689         this.setDisabled(true);
4690     },
4691     
4692     enable : function()
4693     {
4694         this.setDisabled(false);
4695     },
4696     
4697     setDisabled : function(state)
4698     {
4699         if(this.disabled == state){
4700             return;
4701         }
4702         
4703         this.disabled = state;
4704         
4705         if (state) {
4706             this.el.addClass('disabled');
4707             return;
4708         }
4709         
4710         this.el.removeClass('disabled');
4711         
4712         return;
4713     },
4714     
4715     setActive : function(state)
4716     {
4717         if(this.active == state){
4718             return;
4719         }
4720         
4721         this.active = state;
4722         
4723         if (state) {
4724             this.el.addClass('active');
4725             return;
4726         }
4727         
4728         this.el.removeClass('active');
4729         
4730         return;
4731     },
4732     
4733     isActive: function () 
4734     {
4735         return this.active;
4736     },
4737     
4738     setBadge : function(str)
4739     {
4740         if(!this.badgeEl){
4741             return;
4742         }
4743         
4744         this.badgeEl.dom.innerHTML = str;
4745     }
4746     
4747    
4748      
4749  
4750 });
4751  
4752
4753  /*
4754  * - LGPL
4755  *
4756  * row
4757  * 
4758  */
4759
4760 /**
4761  * @class Roo.bootstrap.Row
4762  * @extends Roo.bootstrap.Component
4763  * Bootstrap Row class (contains columns...)
4764  * 
4765  * @constructor
4766  * Create a new Row
4767  * @param {Object} config The config object
4768  */
4769
4770 Roo.bootstrap.Row = function(config){
4771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4772 };
4773
4774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4775     
4776     getAutoCreate : function(){
4777        return {
4778             cls: 'row clearfix'
4779        };
4780     }
4781     
4782     
4783 });
4784
4785  
4786
4787  /*
4788  * - LGPL
4789  *
4790  * element
4791  * 
4792  */
4793
4794 /**
4795  * @class Roo.bootstrap.Element
4796  * @extends Roo.bootstrap.Component
4797  * Bootstrap Element class
4798  * @cfg {String} html contents of the element
4799  * @cfg {String} tag tag of the element
4800  * @cfg {String} cls class of the element
4801  * @cfg {Boolean} preventDefault (true|false) default false
4802  * @cfg {Boolean} clickable (true|false) default false
4803  * 
4804  * @constructor
4805  * Create a new Element
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Element = function(config){
4810     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4811     
4812     this.addEvents({
4813         // raw events
4814         /**
4815          * @event click
4816          * When a element is chick
4817          * @param {Roo.bootstrap.Element} this
4818          * @param {Roo.EventObject} e
4819          */
4820         "click" : true
4821     });
4822 };
4823
4824 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4825     
4826     tag: 'div',
4827     cls: '',
4828     html: '',
4829     preventDefault: false, 
4830     clickable: false,
4831     
4832     getAutoCreate : function(){
4833         
4834         var cfg = {
4835             tag: this.tag,
4836             cls: this.cls,
4837             html: this.html
4838         };
4839         
4840         return cfg;
4841     },
4842     
4843     initEvents: function() 
4844     {
4845         Roo.bootstrap.Element.superclass.initEvents.call(this);
4846         
4847         if(this.clickable){
4848             this.el.on('click', this.onClick, this);
4849         }
4850         
4851     },
4852     
4853     onClick : function(e)
4854     {
4855         if(this.preventDefault){
4856             e.preventDefault();
4857         }
4858         
4859         this.fireEvent('click', this, e);
4860     },
4861     
4862     getValue : function()
4863     {
4864         return this.el.dom.innerHTML;
4865     },
4866     
4867     setValue : function(value)
4868     {
4869         this.el.dom.innerHTML = value;
4870     }
4871    
4872 });
4873
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * pagination
4880  * 
4881  */
4882
4883 /**
4884  * @class Roo.bootstrap.Pagination
4885  * @extends Roo.bootstrap.Component
4886  * Bootstrap Pagination class
4887  * @cfg {String} size xs | sm | md | lg
4888  * @cfg {Boolean} inverse false | true
4889  * 
4890  * @constructor
4891  * Create a new Pagination
4892  * @param {Object} config The config object
4893  */
4894
4895 Roo.bootstrap.Pagination = function(config){
4896     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4897 };
4898
4899 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4900     
4901     cls: false,
4902     size: false,
4903     inverse: false,
4904     
4905     getAutoCreate : function(){
4906         var cfg = {
4907             tag: 'ul',
4908                 cls: 'pagination'
4909         };
4910         if (this.inverse) {
4911             cfg.cls += ' inverse';
4912         }
4913         if (this.html) {
4914             cfg.html=this.html;
4915         }
4916         if (this.cls) {
4917             cfg.cls += " " + this.cls;
4918         }
4919         return cfg;
4920     }
4921    
4922 });
4923
4924  
4925
4926  /*
4927  * - LGPL
4928  *
4929  * Pagination item
4930  * 
4931  */
4932
4933
4934 /**
4935  * @class Roo.bootstrap.PaginationItem
4936  * @extends Roo.bootstrap.Component
4937  * Bootstrap PaginationItem class
4938  * @cfg {String} html text
4939  * @cfg {String} href the link
4940  * @cfg {Boolean} preventDefault (true | false) default true
4941  * @cfg {Boolean} active (true | false) default false
4942  * @cfg {Boolean} disabled default false
4943  * 
4944  * 
4945  * @constructor
4946  * Create a new PaginationItem
4947  * @param {Object} config The config object
4948  */
4949
4950
4951 Roo.bootstrap.PaginationItem = function(config){
4952     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4953     this.addEvents({
4954         // raw events
4955         /**
4956          * @event click
4957          * The raw click event for the entire grid.
4958          * @param {Roo.EventObject} e
4959          */
4960         "click" : true
4961     });
4962 };
4963
4964 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4965     
4966     href : false,
4967     html : false,
4968     preventDefault: true,
4969     active : false,
4970     cls : false,
4971     disabled: false,
4972     
4973     getAutoCreate : function(){
4974         var cfg= {
4975             tag: 'li',
4976             cn: [
4977                 {
4978                     tag : 'a',
4979                     href : this.href ? this.href : '#',
4980                     html : this.html ? this.html : ''
4981                 }
4982             ]
4983         };
4984         
4985         if(this.cls){
4986             cfg.cls = this.cls;
4987         }
4988         
4989         if(this.disabled){
4990             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4991         }
4992         
4993         if(this.active){
4994             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4995         }
4996         
4997         return cfg;
4998     },
4999     
5000     initEvents: function() {
5001         
5002         this.el.on('click', this.onClick, this);
5003         
5004     },
5005     onClick : function(e)
5006     {
5007         Roo.log('PaginationItem on click ');
5008         if(this.preventDefault){
5009             e.preventDefault();
5010         }
5011         
5012         if(this.disabled){
5013             return;
5014         }
5015         
5016         this.fireEvent('click', this, e);
5017     }
5018    
5019 });
5020
5021  
5022
5023  /*
5024  * - LGPL
5025  *
5026  * slider
5027  * 
5028  */
5029
5030
5031 /**
5032  * @class Roo.bootstrap.Slider
5033  * @extends Roo.bootstrap.Component
5034  * Bootstrap Slider class
5035  *    
5036  * @constructor
5037  * Create a new Slider
5038  * @param {Object} config The config object
5039  */
5040
5041 Roo.bootstrap.Slider = function(config){
5042     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5043 };
5044
5045 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5046     
5047     getAutoCreate : function(){
5048         
5049         var cfg = {
5050             tag: 'div',
5051             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5052             cn: [
5053                 {
5054                     tag: 'a',
5055                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5056                 }
5057             ]
5058         };
5059         
5060         return cfg;
5061     }
5062    
5063 });
5064
5065  /*
5066  * Based on:
5067  * Ext JS Library 1.1.1
5068  * Copyright(c) 2006-2007, Ext JS, LLC.
5069  *
5070  * Originally Released Under LGPL - original licence link has changed is not relivant.
5071  *
5072  * Fork - LGPL
5073  * <script type="text/javascript">
5074  */
5075  
5076
5077 /**
5078  * @class Roo.grid.ColumnModel
5079  * @extends Roo.util.Observable
5080  * This is the default implementation of a ColumnModel used by the Grid. It defines
5081  * the columns in the grid.
5082  * <br>Usage:<br>
5083  <pre><code>
5084  var colModel = new Roo.grid.ColumnModel([
5085         {header: "Ticker", width: 60, sortable: true, locked: true},
5086         {header: "Company Name", width: 150, sortable: true},
5087         {header: "Market Cap.", width: 100, sortable: true},
5088         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5089         {header: "Employees", width: 100, sortable: true, resizable: false}
5090  ]);
5091  </code></pre>
5092  * <p>
5093  
5094  * The config options listed for this class are options which may appear in each
5095  * individual column definition.
5096  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5097  * @constructor
5098  * @param {Object} config An Array of column config objects. See this class's
5099  * config objects for details.
5100 */
5101 Roo.grid.ColumnModel = function(config){
5102         /**
5103      * The config passed into the constructor
5104      */
5105     this.config = config;
5106     this.lookup = {};
5107
5108     // if no id, create one
5109     // if the column does not have a dataIndex mapping,
5110     // map it to the order it is in the config
5111     for(var i = 0, len = config.length; i < len; i++){
5112         var c = config[i];
5113         if(typeof c.dataIndex == "undefined"){
5114             c.dataIndex = i;
5115         }
5116         if(typeof c.renderer == "string"){
5117             c.renderer = Roo.util.Format[c.renderer];
5118         }
5119         if(typeof c.id == "undefined"){
5120             c.id = Roo.id();
5121         }
5122         if(c.editor && c.editor.xtype){
5123             c.editor  = Roo.factory(c.editor, Roo.grid);
5124         }
5125         if(c.editor && c.editor.isFormField){
5126             c.editor = new Roo.grid.GridEditor(c.editor);
5127         }
5128         this.lookup[c.id] = c;
5129     }
5130
5131     /**
5132      * The width of columns which have no width specified (defaults to 100)
5133      * @type Number
5134      */
5135     this.defaultWidth = 100;
5136
5137     /**
5138      * Default sortable of columns which have no sortable specified (defaults to false)
5139      * @type Boolean
5140      */
5141     this.defaultSortable = false;
5142
5143     this.addEvents({
5144         /**
5145              * @event widthchange
5146              * Fires when the width of a column changes.
5147              * @param {ColumnModel} this
5148              * @param {Number} columnIndex The column index
5149              * @param {Number} newWidth The new width
5150              */
5151             "widthchange": true,
5152         /**
5153              * @event headerchange
5154              * Fires when the text of a header changes.
5155              * @param {ColumnModel} this
5156              * @param {Number} columnIndex The column index
5157              * @param {Number} newText The new header text
5158              */
5159             "headerchange": true,
5160         /**
5161              * @event hiddenchange
5162              * Fires when a column is hidden or "unhidden".
5163              * @param {ColumnModel} this
5164              * @param {Number} columnIndex The column index
5165              * @param {Boolean} hidden true if hidden, false otherwise
5166              */
5167             "hiddenchange": true,
5168             /**
5169          * @event columnmoved
5170          * Fires when a column is moved.
5171          * @param {ColumnModel} this
5172          * @param {Number} oldIndex
5173          * @param {Number} newIndex
5174          */
5175         "columnmoved" : true,
5176         /**
5177          * @event columlockchange
5178          * Fires when a column's locked state is changed
5179          * @param {ColumnModel} this
5180          * @param {Number} colIndex
5181          * @param {Boolean} locked true if locked
5182          */
5183         "columnlockchange" : true
5184     });
5185     Roo.grid.ColumnModel.superclass.constructor.call(this);
5186 };
5187 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5188     /**
5189      * @cfg {String} header The header text to display in the Grid view.
5190      */
5191     /**
5192      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5193      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5194      * specified, the column's index is used as an index into the Record's data Array.
5195      */
5196     /**
5197      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5198      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5199      */
5200     /**
5201      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5202      * Defaults to the value of the {@link #defaultSortable} property.
5203      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5204      */
5205     /**
5206      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5207      */
5208     /**
5209      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5210      */
5211     /**
5212      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5213      */
5214     /**
5215      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5216      */
5217     /**
5218      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5219      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5220      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5221      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5222      */
5223        /**
5224      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5225      */
5226     /**
5227      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5228      */
5229     /**
5230      * @cfg {String} cursor (Optional)
5231      */
5232     /**
5233      * @cfg {String} tooltip (Optional)
5234      */
5235     /**
5236      * @cfg {Number} xs (Optional)
5237      */
5238     /**
5239      * @cfg {Number} sm (Optional)
5240      */
5241     /**
5242      * @cfg {Number} md (Optional)
5243      */
5244     /**
5245      * @cfg {Number} lg (Optional)
5246      */
5247     /**
5248      * Returns the id of the column at the specified index.
5249      * @param {Number} index The column index
5250      * @return {String} the id
5251      */
5252     getColumnId : function(index){
5253         return this.config[index].id;
5254     },
5255
5256     /**
5257      * Returns the column for a specified id.
5258      * @param {String} id The column id
5259      * @return {Object} the column
5260      */
5261     getColumnById : function(id){
5262         return this.lookup[id];
5263     },
5264
5265     
5266     /**
5267      * Returns the column for a specified dataIndex.
5268      * @param {String} dataIndex The column dataIndex
5269      * @return {Object|Boolean} the column or false if not found
5270      */
5271     getColumnByDataIndex: function(dataIndex){
5272         var index = this.findColumnIndex(dataIndex);
5273         return index > -1 ? this.config[index] : false;
5274     },
5275     
5276     /**
5277      * Returns the index for a specified column id.
5278      * @param {String} id The column id
5279      * @return {Number} the index, or -1 if not found
5280      */
5281     getIndexById : function(id){
5282         for(var i = 0, len = this.config.length; i < len; i++){
5283             if(this.config[i].id == id){
5284                 return i;
5285             }
5286         }
5287         return -1;
5288     },
5289     
5290     /**
5291      * Returns the index for a specified column dataIndex.
5292      * @param {String} dataIndex The column dataIndex
5293      * @return {Number} the index, or -1 if not found
5294      */
5295     
5296     findColumnIndex : function(dataIndex){
5297         for(var i = 0, len = this.config.length; i < len; i++){
5298             if(this.config[i].dataIndex == dataIndex){
5299                 return i;
5300             }
5301         }
5302         return -1;
5303     },
5304     
5305     
5306     moveColumn : function(oldIndex, newIndex){
5307         var c = this.config[oldIndex];
5308         this.config.splice(oldIndex, 1);
5309         this.config.splice(newIndex, 0, c);
5310         this.dataMap = null;
5311         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5312     },
5313
5314     isLocked : function(colIndex){
5315         return this.config[colIndex].locked === true;
5316     },
5317
5318     setLocked : function(colIndex, value, suppressEvent){
5319         if(this.isLocked(colIndex) == value){
5320             return;
5321         }
5322         this.config[colIndex].locked = value;
5323         if(!suppressEvent){
5324             this.fireEvent("columnlockchange", this, colIndex, value);
5325         }
5326     },
5327
5328     getTotalLockedWidth : function(){
5329         var totalWidth = 0;
5330         for(var i = 0; i < this.config.length; i++){
5331             if(this.isLocked(i) && !this.isHidden(i)){
5332                 this.totalWidth += this.getColumnWidth(i);
5333             }
5334         }
5335         return totalWidth;
5336     },
5337
5338     getLockedCount : function(){
5339         for(var i = 0, len = this.config.length; i < len; i++){
5340             if(!this.isLocked(i)){
5341                 return i;
5342             }
5343         }
5344         
5345         return this.config.length;
5346     },
5347
5348     /**
5349      * Returns the number of columns.
5350      * @return {Number}
5351      */
5352     getColumnCount : function(visibleOnly){
5353         if(visibleOnly === true){
5354             var c = 0;
5355             for(var i = 0, len = this.config.length; i < len; i++){
5356                 if(!this.isHidden(i)){
5357                     c++;
5358                 }
5359             }
5360             return c;
5361         }
5362         return this.config.length;
5363     },
5364
5365     /**
5366      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5367      * @param {Function} fn
5368      * @param {Object} scope (optional)
5369      * @return {Array} result
5370      */
5371     getColumnsBy : function(fn, scope){
5372         var r = [];
5373         for(var i = 0, len = this.config.length; i < len; i++){
5374             var c = this.config[i];
5375             if(fn.call(scope||this, c, i) === true){
5376                 r[r.length] = c;
5377             }
5378         }
5379         return r;
5380     },
5381
5382     /**
5383      * Returns true if the specified column is sortable.
5384      * @param {Number} col The column index
5385      * @return {Boolean}
5386      */
5387     isSortable : function(col){
5388         if(typeof this.config[col].sortable == "undefined"){
5389             return this.defaultSortable;
5390         }
5391         return this.config[col].sortable;
5392     },
5393
5394     /**
5395      * Returns the rendering (formatting) function defined for the column.
5396      * @param {Number} col The column index.
5397      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5398      */
5399     getRenderer : function(col){
5400         if(!this.config[col].renderer){
5401             return Roo.grid.ColumnModel.defaultRenderer;
5402         }
5403         return this.config[col].renderer;
5404     },
5405
5406     /**
5407      * Sets the rendering (formatting) function for a column.
5408      * @param {Number} col The column index
5409      * @param {Function} fn The function to use to process the cell's raw data
5410      * to return HTML markup for the grid view. The render function is called with
5411      * the following parameters:<ul>
5412      * <li>Data value.</li>
5413      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5414      * <li>css A CSS style string to apply to the table cell.</li>
5415      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5416      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5417      * <li>Row index</li>
5418      * <li>Column index</li>
5419      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5420      */
5421     setRenderer : function(col, fn){
5422         this.config[col].renderer = fn;
5423     },
5424
5425     /**
5426      * Returns the width for the specified column.
5427      * @param {Number} col The column index
5428      * @return {Number}
5429      */
5430     getColumnWidth : function(col){
5431         return this.config[col].width * 1 || this.defaultWidth;
5432     },
5433
5434     /**
5435      * Sets the width for a column.
5436      * @param {Number} col The column index
5437      * @param {Number} width The new width
5438      */
5439     setColumnWidth : function(col, width, suppressEvent){
5440         this.config[col].width = width;
5441         this.totalWidth = null;
5442         if(!suppressEvent){
5443              this.fireEvent("widthchange", this, col, width);
5444         }
5445     },
5446
5447     /**
5448      * Returns the total width of all columns.
5449      * @param {Boolean} includeHidden True to include hidden column widths
5450      * @return {Number}
5451      */
5452     getTotalWidth : function(includeHidden){
5453         if(!this.totalWidth){
5454             this.totalWidth = 0;
5455             for(var i = 0, len = this.config.length; i < len; i++){
5456                 if(includeHidden || !this.isHidden(i)){
5457                     this.totalWidth += this.getColumnWidth(i);
5458                 }
5459             }
5460         }
5461         return this.totalWidth;
5462     },
5463
5464     /**
5465      * Returns the header for the specified column.
5466      * @param {Number} col The column index
5467      * @return {String}
5468      */
5469     getColumnHeader : function(col){
5470         return this.config[col].header;
5471     },
5472
5473     /**
5474      * Sets the header for a column.
5475      * @param {Number} col The column index
5476      * @param {String} header The new header
5477      */
5478     setColumnHeader : function(col, header){
5479         this.config[col].header = header;
5480         this.fireEvent("headerchange", this, col, header);
5481     },
5482
5483     /**
5484      * Returns the tooltip for the specified column.
5485      * @param {Number} col The column index
5486      * @return {String}
5487      */
5488     getColumnTooltip : function(col){
5489             return this.config[col].tooltip;
5490     },
5491     /**
5492      * Sets the tooltip for a column.
5493      * @param {Number} col The column index
5494      * @param {String} tooltip The new tooltip
5495      */
5496     setColumnTooltip : function(col, tooltip){
5497             this.config[col].tooltip = tooltip;
5498     },
5499
5500     /**
5501      * Returns the dataIndex for the specified column.
5502      * @param {Number} col The column index
5503      * @return {Number}
5504      */
5505     getDataIndex : function(col){
5506         return this.config[col].dataIndex;
5507     },
5508
5509     /**
5510      * Sets the dataIndex for a column.
5511      * @param {Number} col The column index
5512      * @param {Number} dataIndex The new dataIndex
5513      */
5514     setDataIndex : function(col, dataIndex){
5515         this.config[col].dataIndex = dataIndex;
5516     },
5517
5518     
5519     
5520     /**
5521      * Returns true if the cell is editable.
5522      * @param {Number} colIndex The column index
5523      * @param {Number} rowIndex The row index - this is nto actually used..?
5524      * @return {Boolean}
5525      */
5526     isCellEditable : function(colIndex, rowIndex){
5527         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5528     },
5529
5530     /**
5531      * Returns the editor defined for the cell/column.
5532      * return false or null to disable editing.
5533      * @param {Number} colIndex The column index
5534      * @param {Number} rowIndex The row index
5535      * @return {Object}
5536      */
5537     getCellEditor : function(colIndex, rowIndex){
5538         return this.config[colIndex].editor;
5539     },
5540
5541     /**
5542      * Sets if a column is editable.
5543      * @param {Number} col The column index
5544      * @param {Boolean} editable True if the column is editable
5545      */
5546     setEditable : function(col, editable){
5547         this.config[col].editable = editable;
5548     },
5549
5550
5551     /**
5552      * Returns true if the column is hidden.
5553      * @param {Number} colIndex The column index
5554      * @return {Boolean}
5555      */
5556     isHidden : function(colIndex){
5557         return this.config[colIndex].hidden;
5558     },
5559
5560
5561     /**
5562      * Returns true if the column width cannot be changed
5563      */
5564     isFixed : function(colIndex){
5565         return this.config[colIndex].fixed;
5566     },
5567
5568     /**
5569      * Returns true if the column can be resized
5570      * @return {Boolean}
5571      */
5572     isResizable : function(colIndex){
5573         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5574     },
5575     /**
5576      * Sets if a column is hidden.
5577      * @param {Number} colIndex The column index
5578      * @param {Boolean} hidden True if the column is hidden
5579      */
5580     setHidden : function(colIndex, hidden){
5581         this.config[colIndex].hidden = hidden;
5582         this.totalWidth = null;
5583         this.fireEvent("hiddenchange", this, colIndex, hidden);
5584     },
5585
5586     /**
5587      * Sets the editor for a column.
5588      * @param {Number} col The column index
5589      * @param {Object} editor The editor object
5590      */
5591     setEditor : function(col, editor){
5592         this.config[col].editor = editor;
5593     }
5594 });
5595
5596 Roo.grid.ColumnModel.defaultRenderer = function(value)
5597 {
5598     if(typeof value == "object") {
5599         return value;
5600     }
5601         if(typeof value == "string" && value.length < 1){
5602             return "&#160;";
5603         }
5604     
5605         return String.format("{0}", value);
5606 };
5607
5608 // Alias for backwards compatibility
5609 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5610 /*
5611  * Based on:
5612  * Ext JS Library 1.1.1
5613  * Copyright(c) 2006-2007, Ext JS, LLC.
5614  *
5615  * Originally Released Under LGPL - original licence link has changed is not relivant.
5616  *
5617  * Fork - LGPL
5618  * <script type="text/javascript">
5619  */
5620  
5621 /**
5622  * @class Roo.LoadMask
5623  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5624  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5625  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5626  * element's UpdateManager load indicator and will be destroyed after the initial load.
5627  * @constructor
5628  * Create a new LoadMask
5629  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5630  * @param {Object} config The config object
5631  */
5632 Roo.LoadMask = function(el, config){
5633     this.el = Roo.get(el);
5634     Roo.apply(this, config);
5635     if(this.store){
5636         this.store.on('beforeload', this.onBeforeLoad, this);
5637         this.store.on('load', this.onLoad, this);
5638         this.store.on('loadexception', this.onLoadException, this);
5639         this.removeMask = false;
5640     }else{
5641         var um = this.el.getUpdateManager();
5642         um.showLoadIndicator = false; // disable the default indicator
5643         um.on('beforeupdate', this.onBeforeLoad, this);
5644         um.on('update', this.onLoad, this);
5645         um.on('failure', this.onLoad, this);
5646         this.removeMask = true;
5647     }
5648 };
5649
5650 Roo.LoadMask.prototype = {
5651     /**
5652      * @cfg {Boolean} removeMask
5653      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5654      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5655      */
5656     /**
5657      * @cfg {String} msg
5658      * The text to display in a centered loading message box (defaults to 'Loading...')
5659      */
5660     msg : 'Loading...',
5661     /**
5662      * @cfg {String} msgCls
5663      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5664      */
5665     msgCls : 'x-mask-loading',
5666
5667     /**
5668      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5669      * @type Boolean
5670      */
5671     disabled: false,
5672
5673     /**
5674      * Disables the mask to prevent it from being displayed
5675      */
5676     disable : function(){
5677        this.disabled = true;
5678     },
5679
5680     /**
5681      * Enables the mask so that it can be displayed
5682      */
5683     enable : function(){
5684         this.disabled = false;
5685     },
5686     
5687     onLoadException : function()
5688     {
5689         Roo.log(arguments);
5690         
5691         if (typeof(arguments[3]) != 'undefined') {
5692             Roo.MessageBox.alert("Error loading",arguments[3]);
5693         } 
5694         /*
5695         try {
5696             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5697                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5698             }   
5699         } catch(e) {
5700             
5701         }
5702         */
5703     
5704         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5705     },
5706     // private
5707     onLoad : function()
5708     {
5709         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5710     },
5711
5712     // private
5713     onBeforeLoad : function(){
5714         if(!this.disabled){
5715             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5716         }
5717     },
5718
5719     // private
5720     destroy : function(){
5721         if(this.store){
5722             this.store.un('beforeload', this.onBeforeLoad, this);
5723             this.store.un('load', this.onLoad, this);
5724             this.store.un('loadexception', this.onLoadException, this);
5725         }else{
5726             var um = this.el.getUpdateManager();
5727             um.un('beforeupdate', this.onBeforeLoad, this);
5728             um.un('update', this.onLoad, this);
5729             um.un('failure', this.onLoad, this);
5730         }
5731     }
5732 };/*
5733  * - LGPL
5734  *
5735  * table
5736  * 
5737  */
5738
5739 /**
5740  * @class Roo.bootstrap.Table
5741  * @extends Roo.bootstrap.Component
5742  * Bootstrap Table class
5743  * @cfg {String} cls table class
5744  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5745  * @cfg {String} bgcolor Specifies the background color for a table
5746  * @cfg {Number} border Specifies whether the table cells should have borders or not
5747  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5748  * @cfg {Number} cellspacing Specifies the space between cells
5749  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5750  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5751  * @cfg {String} sortable Specifies that the table should be sortable
5752  * @cfg {String} summary Specifies a summary of the content of a table
5753  * @cfg {Number} width Specifies the width of a table
5754  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5755  * 
5756  * @cfg {boolean} striped Should the rows be alternative striped
5757  * @cfg {boolean} bordered Add borders to the table
5758  * @cfg {boolean} hover Add hover highlighting
5759  * @cfg {boolean} condensed Format condensed
5760  * @cfg {boolean} responsive Format condensed
5761  * @cfg {Boolean} loadMask (true|false) default false
5762  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5763  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5764  * @cfg {Boolean} rowSelection (true|false) default false
5765  * @cfg {Boolean} cellSelection (true|false) default false
5766  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5767  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5768  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5769  
5770  * 
5771  * @constructor
5772  * Create a new Table
5773  * @param {Object} config The config object
5774  */
5775
5776 Roo.bootstrap.Table = function(config){
5777     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5778     
5779   
5780     
5781     // BC...
5782     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5783     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5784     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5785     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5786     
5787     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5788     if (this.sm) {
5789         this.sm.grid = this;
5790         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5791         this.sm = this.selModel;
5792         this.sm.xmodule = this.xmodule || false;
5793     }
5794     
5795     if (this.cm && typeof(this.cm.config) == 'undefined') {
5796         this.colModel = new Roo.grid.ColumnModel(this.cm);
5797         this.cm = this.colModel;
5798         this.cm.xmodule = this.xmodule || false;
5799     }
5800     if (this.store) {
5801         this.store= Roo.factory(this.store, Roo.data);
5802         this.ds = this.store;
5803         this.ds.xmodule = this.xmodule || false;
5804          
5805     }
5806     if (this.footer && this.store) {
5807         this.footer.dataSource = this.ds;
5808         this.footer = Roo.factory(this.footer);
5809     }
5810     
5811     /** @private */
5812     this.addEvents({
5813         /**
5814          * @event cellclick
5815          * Fires when a cell is clicked
5816          * @param {Roo.bootstrap.Table} this
5817          * @param {Roo.Element} el
5818          * @param {Number} rowIndex
5819          * @param {Number} columnIndex
5820          * @param {Roo.EventObject} e
5821          */
5822         "cellclick" : true,
5823         /**
5824          * @event celldblclick
5825          * Fires when a cell is double clicked
5826          * @param {Roo.bootstrap.Table} this
5827          * @param {Roo.Element} el
5828          * @param {Number} rowIndex
5829          * @param {Number} columnIndex
5830          * @param {Roo.EventObject} e
5831          */
5832         "celldblclick" : true,
5833         /**
5834          * @event rowclick
5835          * Fires when a row is clicked
5836          * @param {Roo.bootstrap.Table} this
5837          * @param {Roo.Element} el
5838          * @param {Number} rowIndex
5839          * @param {Roo.EventObject} e
5840          */
5841         "rowclick" : true,
5842         /**
5843          * @event rowdblclick
5844          * Fires when a row is double clicked
5845          * @param {Roo.bootstrap.Table} this
5846          * @param {Roo.Element} el
5847          * @param {Number} rowIndex
5848          * @param {Roo.EventObject} e
5849          */
5850         "rowdblclick" : true,
5851         /**
5852          * @event mouseover
5853          * Fires when a mouseover occur
5854          * @param {Roo.bootstrap.Table} this
5855          * @param {Roo.Element} el
5856          * @param {Number} rowIndex
5857          * @param {Number} columnIndex
5858          * @param {Roo.EventObject} e
5859          */
5860         "mouseover" : true,
5861         /**
5862          * @event mouseout
5863          * Fires when a mouseout occur
5864          * @param {Roo.bootstrap.Table} this
5865          * @param {Roo.Element} el
5866          * @param {Number} rowIndex
5867          * @param {Number} columnIndex
5868          * @param {Roo.EventObject} e
5869          */
5870         "mouseout" : true,
5871         /**
5872          * @event rowclass
5873          * Fires when a row is rendered, so you can change add a style to it.
5874          * @param {Roo.bootstrap.Table} this
5875          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5876          */
5877         'rowclass' : true,
5878           /**
5879          * @event rowsrendered
5880          * Fires when all the  rows have been rendered
5881          * @param {Roo.bootstrap.Table} this
5882          */
5883         'rowsrendered' : true,
5884         /**
5885          * @event contextmenu
5886          * The raw contextmenu event for the entire grid.
5887          * @param {Roo.EventObject} e
5888          */
5889         "contextmenu" : true,
5890         /**
5891          * @event rowcontextmenu
5892          * Fires when a row is right clicked
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Number} rowIndex
5895          * @param {Roo.EventObject} e
5896          */
5897         "rowcontextmenu" : true,
5898         /**
5899          * @event cellcontextmenu
5900          * Fires when a cell is right clicked
5901          * @param {Roo.bootstrap.Table} this
5902          * @param {Number} rowIndex
5903          * @param {Number} cellIndex
5904          * @param {Roo.EventObject} e
5905          */
5906          "cellcontextmenu" : true,
5907          /**
5908          * @event headercontextmenu
5909          * Fires when a header is right clicked
5910          * @param {Roo.bootstrap.Table} this
5911          * @param {Number} columnIndex
5912          * @param {Roo.EventObject} e
5913          */
5914         "headercontextmenu" : true
5915     });
5916 };
5917
5918 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5919     
5920     cls: false,
5921     align: false,
5922     bgcolor: false,
5923     border: false,
5924     cellpadding: false,
5925     cellspacing: false,
5926     frame: false,
5927     rules: false,
5928     sortable: false,
5929     summary: false,
5930     width: false,
5931     striped : false,
5932     scrollBody : false,
5933     bordered: false,
5934     hover:  false,
5935     condensed : false,
5936     responsive : false,
5937     sm : false,
5938     cm : false,
5939     store : false,
5940     loadMask : false,
5941     footerShow : true,
5942     headerShow : true,
5943   
5944     rowSelection : false,
5945     cellSelection : false,
5946     layout : false,
5947     
5948     // Roo.Element - the tbody
5949     mainBody: false,
5950     // Roo.Element - thead element
5951     mainHead: false,
5952     
5953     container: false, // used by gridpanel...
5954     
5955     lazyLoad : false,
5956     
5957     getAutoCreate : function()
5958     {
5959         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5960         
5961         cfg = {
5962             tag: 'table',
5963             cls : 'table',
5964             cn : []
5965         };
5966         if (this.scrollBody) {
5967             cfg.cls += ' table-body-fixed';
5968         }    
5969         if (this.striped) {
5970             cfg.cls += ' table-striped';
5971         }
5972         
5973         if (this.hover) {
5974             cfg.cls += ' table-hover';
5975         }
5976         if (this.bordered) {
5977             cfg.cls += ' table-bordered';
5978         }
5979         if (this.condensed) {
5980             cfg.cls += ' table-condensed';
5981         }
5982         if (this.responsive) {
5983             cfg.cls += ' table-responsive';
5984         }
5985         
5986         if (this.cls) {
5987             cfg.cls+=  ' ' +this.cls;
5988         }
5989         
5990         // this lot should be simplifed...
5991         
5992         if (this.align) {
5993             cfg.align=this.align;
5994         }
5995         if (this.bgcolor) {
5996             cfg.bgcolor=this.bgcolor;
5997         }
5998         if (this.border) {
5999             cfg.border=this.border;
6000         }
6001         if (this.cellpadding) {
6002             cfg.cellpadding=this.cellpadding;
6003         }
6004         if (this.cellspacing) {
6005             cfg.cellspacing=this.cellspacing;
6006         }
6007         if (this.frame) {
6008             cfg.frame=this.frame;
6009         }
6010         if (this.rules) {
6011             cfg.rules=this.rules;
6012         }
6013         if (this.sortable) {
6014             cfg.sortable=this.sortable;
6015         }
6016         if (this.summary) {
6017             cfg.summary=this.summary;
6018         }
6019         if (this.width) {
6020             cfg.width=this.width;
6021         }
6022         if (this.layout) {
6023             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6024         }
6025         
6026         if(this.store || this.cm){
6027             if(this.headerShow){
6028                 cfg.cn.push(this.renderHeader());
6029             }
6030             
6031             cfg.cn.push(this.renderBody());
6032             
6033             if(this.footerShow){
6034                 cfg.cn.push(this.renderFooter());
6035             }
6036             // where does this come from?
6037             //cfg.cls+=  ' TableGrid';
6038         }
6039         
6040         return { cn : [ cfg ] };
6041     },
6042     
6043     initEvents : function()
6044     {   
6045         if(!this.store || !this.cm){
6046             return;
6047         }
6048         if (this.selModel) {
6049             this.selModel.initEvents();
6050         }
6051         
6052         
6053         //Roo.log('initEvents with ds!!!!');
6054         
6055         this.mainBody = this.el.select('tbody', true).first();
6056         this.mainHead = this.el.select('thead', true).first();
6057         
6058         
6059         
6060         
6061         var _this = this;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.on('click', _this.sort, _this);
6065         });
6066         
6067         this.mainBody.on("click", this.onClick, this);
6068         this.mainBody.on("dblclick", this.onDblClick, this);
6069         
6070         // why is this done????? = it breaks dialogs??
6071         //this.parent().el.setStyle('position', 'relative');
6072         
6073         
6074         if (this.footer) {
6075             this.footer.parentId = this.id;
6076             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6077             
6078             if(this.lazyLoad){
6079                 this.el.select('tfoot tr td').first().addClass('hide');
6080             }
6081         } 
6082         
6083         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6084         
6085         this.store.on('load', this.onLoad, this);
6086         this.store.on('beforeload', this.onBeforeLoad, this);
6087         this.store.on('update', this.onUpdate, this);
6088         this.store.on('add', this.onAdd, this);
6089         this.store.on("clear", this.clear, this);
6090         
6091         this.el.on("contextmenu", this.onContextMenu, this);
6092         
6093         this.mainBody.on('scroll', this.onBodyScroll, this);
6094         
6095         
6096     },
6097     
6098     onContextMenu : function(e, t)
6099     {
6100         this.processEvent("contextmenu", e);
6101     },
6102     
6103     processEvent : function(name, e)
6104     {
6105         if (name != 'touchstart' ) {
6106             this.fireEvent(name, e);    
6107         }
6108         
6109         var t = e.getTarget();
6110         
6111         var cell = Roo.get(t);
6112         
6113         if(!cell){
6114             return;
6115         }
6116         
6117         if(cell.findParent('tfoot', false, true)){
6118             return;
6119         }
6120         
6121         if(cell.findParent('thead', false, true)){
6122             
6123             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6124                 cell = Roo.get(t).findParent('th', false, true);
6125                 if (!cell) {
6126                     Roo.log("failed to find th in thead?");
6127                     Roo.log(e.getTarget());
6128                     return;
6129                 }
6130             }
6131             
6132             var cellIndex = cell.dom.cellIndex;
6133             
6134             var ename = name == 'touchstart' ? 'click' : name;
6135             this.fireEvent("header" + ename, this, cellIndex, e);
6136             
6137             return;
6138         }
6139         
6140         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6141             cell = Roo.get(t).findParent('td', false, true);
6142             if (!cell) {
6143                 Roo.log("failed to find th in tbody?");
6144                 Roo.log(e.getTarget());
6145                 return;
6146             }
6147         }
6148         
6149         var row = cell.findParent('tr', false, true);
6150         var cellIndex = cell.dom.cellIndex;
6151         var rowIndex = row.dom.rowIndex - 1;
6152         
6153         if(row !== false){
6154             
6155             this.fireEvent("row" + name, this, rowIndex, e);
6156             
6157             if(cell !== false){
6158             
6159                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6160             }
6161         }
6162         
6163     },
6164     
6165     onMouseover : function(e, el)
6166     {
6167         var cell = Roo.get(el);
6168         
6169         if(!cell){
6170             return;
6171         }
6172         
6173         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174             cell = cell.findParent('td', false, true);
6175         }
6176         
6177         var row = cell.findParent('tr', false, true);
6178         var cellIndex = cell.dom.cellIndex;
6179         var rowIndex = row.dom.rowIndex - 1; // start from 0
6180         
6181         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6182         
6183     },
6184     
6185     onMouseout : function(e, el)
6186     {
6187         var cell = Roo.get(el);
6188         
6189         if(!cell){
6190             return;
6191         }
6192         
6193         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6194             cell = cell.findParent('td', false, true);
6195         }
6196         
6197         var row = cell.findParent('tr', false, true);
6198         var cellIndex = cell.dom.cellIndex;
6199         var rowIndex = row.dom.rowIndex - 1; // start from 0
6200         
6201         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6202         
6203     },
6204     
6205     onClick : function(e, el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell || (!this.cellSelection && !this.rowSelection)){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         if(!cell || typeof(cell) == 'undefined'){
6218             return;
6219         }
6220         
6221         var row = cell.findParent('tr', false, true);
6222         
6223         if(!row || typeof(row) == 'undefined'){
6224             return;
6225         }
6226         
6227         var cellIndex = cell.dom.cellIndex;
6228         var rowIndex = this.getRowIndex(row);
6229         
6230         // why??? - should these not be based on SelectionModel?
6231         if(this.cellSelection){
6232             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6233         }
6234         
6235         if(this.rowSelection){
6236             this.fireEvent('rowclick', this, row, rowIndex, e);
6237         }
6238         
6239         
6240     },
6241         
6242     onDblClick : function(e,el)
6243     {
6244         var cell = Roo.get(el);
6245         
6246         if(!cell || (!this.cellSelection && !this.rowSelection)){
6247             return;
6248         }
6249         
6250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6251             cell = cell.findParent('td', false, true);
6252         }
6253         
6254         if(!cell || typeof(cell) == 'undefined'){
6255             return;
6256         }
6257         
6258         var row = cell.findParent('tr', false, true);
6259         
6260         if(!row || typeof(row) == 'undefined'){
6261             return;
6262         }
6263         
6264         var cellIndex = cell.dom.cellIndex;
6265         var rowIndex = this.getRowIndex(row);
6266         
6267         if(this.cellSelection){
6268             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6269         }
6270         
6271         if(this.rowSelection){
6272             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6273         }
6274     },
6275     
6276     sort : function(e,el)
6277     {
6278         var col = Roo.get(el);
6279         
6280         if(!col.hasClass('sortable')){
6281             return;
6282         }
6283         
6284         var sort = col.attr('sort');
6285         var dir = 'ASC';
6286         
6287         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6288             dir = 'DESC';
6289         }
6290         
6291         this.store.sortInfo = {field : sort, direction : dir};
6292         
6293         if (this.footer) {
6294             Roo.log("calling footer first");
6295             this.footer.onClick('first');
6296         } else {
6297         
6298             this.store.load({ params : { start : 0 } });
6299         }
6300     },
6301     
6302     renderHeader : function()
6303     {
6304         var header = {
6305             tag: 'thead',
6306             cn : []
6307         };
6308         
6309         var cm = this.cm;
6310         this.totalWidth = 0;
6311         
6312         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6313             
6314             var config = cm.config[i];
6315             
6316             var c = {
6317                 tag: 'th',
6318                 style : '',
6319                 html: cm.getColumnHeader(i)
6320             };
6321             
6322             var hh = '';
6323             
6324             if(typeof(config.sortable) != 'undefined' && config.sortable){
6325                 c.cls = 'sortable';
6326                 c.html = '<i class="glyphicon"></i>' + c.html;
6327             }
6328             
6329             if(typeof(config.lgHeader) != 'undefined'){
6330                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6331             }
6332             
6333             if(typeof(config.mdHeader) != 'undefined'){
6334                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6335             }
6336             
6337             if(typeof(config.smHeader) != 'undefined'){
6338                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6339             }
6340             
6341             if(typeof(config.xsHeader) != 'undefined'){
6342                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6343             }
6344             
6345             if(hh.length){
6346                 c.html = hh;
6347             }
6348             
6349             if(typeof(config.tooltip) != 'undefined'){
6350                 c.tooltip = config.tooltip;
6351             }
6352             
6353             if(typeof(config.colspan) != 'undefined'){
6354                 c.colspan = config.colspan;
6355             }
6356             
6357             if(typeof(config.hidden) != 'undefined' && config.hidden){
6358                 c.style += ' display:none;';
6359             }
6360             
6361             if(typeof(config.dataIndex) != 'undefined'){
6362                 c.sort = config.dataIndex;
6363             }
6364             
6365            
6366             
6367             if(typeof(config.align) != 'undefined' && config.align.length){
6368                 c.style += ' text-align:' + config.align + ';';
6369             }
6370             
6371             if(typeof(config.width) != 'undefined'){
6372                 c.style += ' width:' + config.width + 'px;';
6373                 this.totalWidth += config.width;
6374             } else {
6375                 this.totalWidth += 100; // assume minimum of 100 per column?
6376             }
6377             
6378             if(typeof(config.cls) != 'undefined'){
6379                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6380             }
6381             
6382             ['xs','sm','md','lg'].map(function(size){
6383                 
6384                 if(typeof(config[size]) == 'undefined'){
6385                     return;
6386                 }
6387                 
6388                 if (!config[size]) { // 0 = hidden
6389                     c.cls += ' hidden-' + size;
6390                     return;
6391                 }
6392                 
6393                 c.cls += ' col-' + size + '-' + config[size];
6394
6395             });
6396             
6397             header.cn.push(c)
6398         }
6399         
6400         return header;
6401     },
6402     
6403     renderBody : function()
6404     {
6405         var body = {
6406             tag: 'tbody',
6407             cn : [
6408                 {
6409                     tag: 'tr',
6410                     cn : [
6411                         {
6412                             tag : 'td',
6413                             colspan :  this.cm.getColumnCount()
6414                         }
6415                     ]
6416                 }
6417             ]
6418         };
6419         
6420         return body;
6421     },
6422     
6423     renderFooter : function()
6424     {
6425         var footer = {
6426             tag: 'tfoot',
6427             cn : [
6428                 {
6429                     tag: 'tr',
6430                     cn : [
6431                         {
6432                             tag : 'td',
6433                             colspan :  this.cm.getColumnCount()
6434                         }
6435                     ]
6436                 }
6437             ]
6438         };
6439         
6440         return footer;
6441     },
6442     
6443     
6444     
6445     onLoad : function()
6446     {
6447 //        Roo.log('ds onload');
6448         this.clear();
6449         
6450         var _this = this;
6451         var cm = this.cm;
6452         var ds = this.store;
6453         
6454         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6455             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6456             if (_this.store.sortInfo) {
6457                     
6458                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6459                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6460                 }
6461                 
6462                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6463                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6464                 }
6465             }
6466         });
6467         
6468         var tbody =  this.mainBody;
6469               
6470         if(ds.getCount() > 0){
6471             ds.data.each(function(d,rowIndex){
6472                 var row =  this.renderRow(cm, ds, rowIndex);
6473                 
6474                 tbody.createChild(row);
6475                 
6476                 var _this = this;
6477                 
6478                 if(row.cellObjects.length){
6479                     Roo.each(row.cellObjects, function(r){
6480                         _this.renderCellObject(r);
6481                     })
6482                 }
6483                 
6484             }, this);
6485         }
6486         
6487         Roo.each(this.el.select('tbody td', true).elements, function(e){
6488             e.on('mouseover', _this.onMouseover, _this);
6489         });
6490         
6491         Roo.each(this.el.select('tbody td', true).elements, function(e){
6492             e.on('mouseout', _this.onMouseout, _this);
6493         });
6494         this.fireEvent('rowsrendered', this);
6495         //if(this.loadMask){
6496         //    this.maskEl.hide();
6497         //}
6498         
6499         this.autoSize();
6500     },
6501     
6502     
6503     onUpdate : function(ds,record)
6504     {
6505         this.refreshRow(record);
6506         this.autoSize();
6507     },
6508     
6509     onRemove : function(ds, record, index, isUpdate){
6510         if(isUpdate !== true){
6511             this.fireEvent("beforerowremoved", this, index, record);
6512         }
6513         var bt = this.mainBody.dom;
6514         
6515         var rows = this.el.select('tbody > tr', true).elements;
6516         
6517         if(typeof(rows[index]) != 'undefined'){
6518             bt.removeChild(rows[index].dom);
6519         }
6520         
6521 //        if(bt.rows[index]){
6522 //            bt.removeChild(bt.rows[index]);
6523 //        }
6524         
6525         if(isUpdate !== true){
6526             //this.stripeRows(index);
6527             //this.syncRowHeights(index, index);
6528             //this.layout();
6529             this.fireEvent("rowremoved", this, index, record);
6530         }
6531     },
6532     
6533     onAdd : function(ds, records, rowIndex)
6534     {
6535         //Roo.log('on Add called');
6536         // - note this does not handle multiple adding very well..
6537         var bt = this.mainBody.dom;
6538         for (var i =0 ; i < records.length;i++) {
6539             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6540             //Roo.log(records[i]);
6541             //Roo.log(this.store.getAt(rowIndex+i));
6542             this.insertRow(this.store, rowIndex + i, false);
6543             return;
6544         }
6545         
6546     },
6547     
6548     
6549     refreshRow : function(record){
6550         var ds = this.store, index;
6551         if(typeof record == 'number'){
6552             index = record;
6553             record = ds.getAt(index);
6554         }else{
6555             index = ds.indexOf(record);
6556         }
6557         this.insertRow(ds, index, true);
6558         this.autoSize();
6559         this.onRemove(ds, record, index+1, true);
6560         this.autoSize();
6561         //this.syncRowHeights(index, index);
6562         //this.layout();
6563         this.fireEvent("rowupdated", this, index, record);
6564     },
6565     
6566     insertRow : function(dm, rowIndex, isUpdate){
6567         
6568         if(!isUpdate){
6569             this.fireEvent("beforerowsinserted", this, rowIndex);
6570         }
6571             //var s = this.getScrollState();
6572         var row = this.renderRow(this.cm, this.store, rowIndex);
6573         // insert before rowIndex..
6574         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6575         
6576         var _this = this;
6577                 
6578         if(row.cellObjects.length){
6579             Roo.each(row.cellObjects, function(r){
6580                 _this.renderCellObject(r);
6581             })
6582         }
6583             
6584         if(!isUpdate){
6585             this.fireEvent("rowsinserted", this, rowIndex);
6586             //this.syncRowHeights(firstRow, lastRow);
6587             //this.stripeRows(firstRow);
6588             //this.layout();
6589         }
6590         
6591     },
6592     
6593     
6594     getRowDom : function(rowIndex)
6595     {
6596         var rows = this.el.select('tbody > tr', true).elements;
6597         
6598         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6599         
6600     },
6601     // returns the object tree for a tr..
6602   
6603     
6604     renderRow : function(cm, ds, rowIndex) 
6605     {
6606         
6607         var d = ds.getAt(rowIndex);
6608         
6609         var row = {
6610             tag : 'tr',
6611             cn : []
6612         };
6613             
6614         var cellObjects = [];
6615         
6616         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6617             var config = cm.config[i];
6618             
6619             var renderer = cm.getRenderer(i);
6620             var value = '';
6621             var id = false;
6622             
6623             if(typeof(renderer) !== 'undefined'){
6624                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6625             }
6626             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6627             // and are rendered into the cells after the row is rendered - using the id for the element.
6628             
6629             if(typeof(value) === 'object'){
6630                 id = Roo.id();
6631                 cellObjects.push({
6632                     container : id,
6633                     cfg : value 
6634                 })
6635             }
6636             
6637             var rowcfg = {
6638                 record: d,
6639                 rowIndex : rowIndex,
6640                 colIndex : i,
6641                 rowClass : ''
6642             };
6643
6644             this.fireEvent('rowclass', this, rowcfg);
6645             
6646             var td = {
6647                 tag: 'td',
6648                 cls : rowcfg.rowClass,
6649                 style: '',
6650                 html: (typeof(value) === 'object') ? '' : value
6651             };
6652             
6653             if (id) {
6654                 td.id = id;
6655             }
6656             
6657             if(typeof(config.colspan) != 'undefined'){
6658                 td.colspan = config.colspan;
6659             }
6660             
6661             if(typeof(config.hidden) != 'undefined' && config.hidden){
6662                 td.style += ' display:none;';
6663             }
6664             
6665             if(typeof(config.align) != 'undefined' && config.align.length){
6666                 td.style += ' text-align:' + config.align + ';';
6667             }
6668             
6669             if(typeof(config.width) != 'undefined'){
6670                 td.style += ' width:' +  config.width + 'px;';
6671             }
6672             
6673             if(typeof(config.cursor) != 'undefined'){
6674                 td.style += ' cursor:' +  config.cursor + ';';
6675             }
6676             
6677             if(typeof(config.cls) != 'undefined'){
6678                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6679             }
6680             
6681             ['xs','sm','md','lg'].map(function(size){
6682                 
6683                 if(typeof(config[size]) == 'undefined'){
6684                     return;
6685                 }
6686                 
6687                 if (!config[size]) { // 0 = hidden
6688                     td.cls += ' hidden-' + size;
6689                     return;
6690                 }
6691                 
6692                 td.cls += ' col-' + size + '-' + config[size];
6693
6694             });
6695              
6696             row.cn.push(td);
6697            
6698         }
6699         
6700         row.cellObjects = cellObjects;
6701         
6702         return row;
6703           
6704     },
6705     
6706     
6707     
6708     onBeforeLoad : function()
6709     {
6710         //Roo.log('ds onBeforeLoad');
6711         
6712         //this.clear();
6713         
6714         //if(this.loadMask){
6715         //    this.maskEl.show();
6716         //}
6717     },
6718      /**
6719      * Remove all rows
6720      */
6721     clear : function()
6722     {
6723         this.el.select('tbody', true).first().dom.innerHTML = '';
6724     },
6725     /**
6726      * Show or hide a row.
6727      * @param {Number} rowIndex to show or hide
6728      * @param {Boolean} state hide
6729      */
6730     setRowVisibility : function(rowIndex, state)
6731     {
6732         var bt = this.mainBody.dom;
6733         
6734         var rows = this.el.select('tbody > tr', true).elements;
6735         
6736         if(typeof(rows[rowIndex]) == 'undefined'){
6737             return;
6738         }
6739         rows[rowIndex].dom.style.display = state ? '' : 'none';
6740     },
6741     
6742     
6743     getSelectionModel : function(){
6744         if(!this.selModel){
6745             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6746         }
6747         return this.selModel;
6748     },
6749     /*
6750      * Render the Roo.bootstrap object from renderder
6751      */
6752     renderCellObject : function(r)
6753     {
6754         var _this = this;
6755         
6756         var t = r.cfg.render(r.container);
6757         
6758         if(r.cfg.cn){
6759             Roo.each(r.cfg.cn, function(c){
6760                 var child = {
6761                     container: t.getChildContainer(),
6762                     cfg: c
6763                 };
6764                 _this.renderCellObject(child);
6765             })
6766         }
6767     },
6768     
6769     getRowIndex : function(row)
6770     {
6771         var rowIndex = -1;
6772         
6773         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6774             if(el != row){
6775                 return;
6776             }
6777             
6778             rowIndex = index;
6779         });
6780         
6781         return rowIndex;
6782     },
6783      /**
6784      * Returns the grid's underlying element = used by panel.Grid
6785      * @return {Element} The element
6786      */
6787     getGridEl : function(){
6788         return this.el;
6789     },
6790      /**
6791      * Forces a resize - used by panel.Grid
6792      * @return {Element} The element
6793      */
6794     autoSize : function()
6795     {
6796         //var ctr = Roo.get(this.container.dom.parentElement);
6797         var ctr = Roo.get(this.el.dom);
6798         
6799         var thd = this.getGridEl().select('thead',true).first();
6800         var tbd = this.getGridEl().select('tbody', true).first();
6801         var tfd = this.getGridEl().select('tfoot', true).first();
6802         
6803         var cw = ctr.getWidth();
6804         
6805         if (tbd) {
6806             
6807             tbd.setSize(ctr.getWidth(),
6808                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6809             );
6810             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6811             cw -= barsize;
6812         }
6813         cw = Math.max(cw, this.totalWidth);
6814         this.getGridEl().select('tr',true).setWidth(cw);
6815         // resize 'expandable coloumn?
6816         
6817         return; // we doe not have a view in this design..
6818         
6819     },
6820     onBodyScroll: function()
6821     {
6822         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6823         this.mainHead.setStyle({
6824             'position' : 'relative',
6825             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6826         });
6827         
6828         if(this.lazyLoad){
6829             
6830             var scrollHeight = this.mainBody.dom.scrollHeight;
6831             
6832             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6833             
6834             var height = this.mainBody.getHeight();
6835             
6836             if(scrollHeight - height == scrollTop) {
6837                 
6838                 var total = this.ds.getTotalCount();
6839                 
6840                 if(this.footer.cursor + this.footer.pageSize < total){
6841                     
6842                     this.footer.ds.load({
6843                         params : {
6844                             start : this.footer.cursor + this.footer.pageSize,
6845                             limit : this.footer.pageSize
6846                         },
6847                         add : true
6848                     });
6849                 }
6850             }
6851             
6852         }
6853     }
6854 });
6855
6856  
6857
6858  /*
6859  * - LGPL
6860  *
6861  * table cell
6862  * 
6863  */
6864
6865 /**
6866  * @class Roo.bootstrap.TableCell
6867  * @extends Roo.bootstrap.Component
6868  * Bootstrap TableCell class
6869  * @cfg {String} html cell contain text
6870  * @cfg {String} cls cell class
6871  * @cfg {String} tag cell tag (td|th) default td
6872  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6873  * @cfg {String} align Aligns the content in a cell
6874  * @cfg {String} axis Categorizes cells
6875  * @cfg {String} bgcolor Specifies the background color of a cell
6876  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877  * @cfg {Number} colspan Specifies the number of columns a cell should span
6878  * @cfg {String} headers Specifies one or more header cells a cell is related to
6879  * @cfg {Number} height Sets the height of a cell
6880  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6881  * @cfg {Number} rowspan Sets the number of rows a cell should span
6882  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6883  * @cfg {String} valign Vertical aligns the content in a cell
6884  * @cfg {Number} width Specifies the width of a cell
6885  * 
6886  * @constructor
6887  * Create a new TableCell
6888  * @param {Object} config The config object
6889  */
6890
6891 Roo.bootstrap.TableCell = function(config){
6892     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6893 };
6894
6895 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6896     
6897     html: false,
6898     cls: false,
6899     tag: false,
6900     abbr: false,
6901     align: false,
6902     axis: false,
6903     bgcolor: false,
6904     charoff: false,
6905     colspan: false,
6906     headers: false,
6907     height: false,
6908     nowrap: false,
6909     rowspan: false,
6910     scope: false,
6911     valign: false,
6912     width: false,
6913     
6914     
6915     getAutoCreate : function(){
6916         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6917         
6918         cfg = {
6919             tag: 'td'
6920         };
6921         
6922         if(this.tag){
6923             cfg.tag = this.tag;
6924         }
6925         
6926         if (this.html) {
6927             cfg.html=this.html
6928         }
6929         if (this.cls) {
6930             cfg.cls=this.cls
6931         }
6932         if (this.abbr) {
6933             cfg.abbr=this.abbr
6934         }
6935         if (this.align) {
6936             cfg.align=this.align
6937         }
6938         if (this.axis) {
6939             cfg.axis=this.axis
6940         }
6941         if (this.bgcolor) {
6942             cfg.bgcolor=this.bgcolor
6943         }
6944         if (this.charoff) {
6945             cfg.charoff=this.charoff
6946         }
6947         if (this.colspan) {
6948             cfg.colspan=this.colspan
6949         }
6950         if (this.headers) {
6951             cfg.headers=this.headers
6952         }
6953         if (this.height) {
6954             cfg.height=this.height
6955         }
6956         if (this.nowrap) {
6957             cfg.nowrap=this.nowrap
6958         }
6959         if (this.rowspan) {
6960             cfg.rowspan=this.rowspan
6961         }
6962         if (this.scope) {
6963             cfg.scope=this.scope
6964         }
6965         if (this.valign) {
6966             cfg.valign=this.valign
6967         }
6968         if (this.width) {
6969             cfg.width=this.width
6970         }
6971         
6972         
6973         return cfg;
6974     }
6975    
6976 });
6977
6978  
6979
6980  /*
6981  * - LGPL
6982  *
6983  * table row
6984  * 
6985  */
6986
6987 /**
6988  * @class Roo.bootstrap.TableRow
6989  * @extends Roo.bootstrap.Component
6990  * Bootstrap TableRow class
6991  * @cfg {String} cls row class
6992  * @cfg {String} align Aligns the content in a table row
6993  * @cfg {String} bgcolor Specifies a background color for a table row
6994  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6995  * @cfg {String} valign Vertical aligns the content in a table row
6996  * 
6997  * @constructor
6998  * Create a new TableRow
6999  * @param {Object} config The config object
7000  */
7001
7002 Roo.bootstrap.TableRow = function(config){
7003     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7004 };
7005
7006 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7007     
7008     cls: false,
7009     align: false,
7010     bgcolor: false,
7011     charoff: false,
7012     valign: false,
7013     
7014     getAutoCreate : function(){
7015         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7016         
7017         cfg = {
7018             tag: 'tr'
7019         };
7020             
7021         if(this.cls){
7022             cfg.cls = this.cls;
7023         }
7024         if(this.align){
7025             cfg.align = this.align;
7026         }
7027         if(this.bgcolor){
7028             cfg.bgcolor = this.bgcolor;
7029         }
7030         if(this.charoff){
7031             cfg.charoff = this.charoff;
7032         }
7033         if(this.valign){
7034             cfg.valign = this.valign;
7035         }
7036         
7037         return cfg;
7038     }
7039    
7040 });
7041
7042  
7043
7044  /*
7045  * - LGPL
7046  *
7047  * table body
7048  * 
7049  */
7050
7051 /**
7052  * @class Roo.bootstrap.TableBody
7053  * @extends Roo.bootstrap.Component
7054  * Bootstrap TableBody class
7055  * @cfg {String} cls element class
7056  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7057  * @cfg {String} align Aligns the content inside the element
7058  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7059  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7060  * 
7061  * @constructor
7062  * Create a new TableBody
7063  * @param {Object} config The config object
7064  */
7065
7066 Roo.bootstrap.TableBody = function(config){
7067     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7068 };
7069
7070 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7071     
7072     cls: false,
7073     tag: false,
7074     align: false,
7075     charoff: false,
7076     valign: false,
7077     
7078     getAutoCreate : function(){
7079         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7080         
7081         cfg = {
7082             tag: 'tbody'
7083         };
7084             
7085         if (this.cls) {
7086             cfg.cls=this.cls
7087         }
7088         if(this.tag){
7089             cfg.tag = this.tag;
7090         }
7091         
7092         if(this.align){
7093             cfg.align = this.align;
7094         }
7095         if(this.charoff){
7096             cfg.charoff = this.charoff;
7097         }
7098         if(this.valign){
7099             cfg.valign = this.valign;
7100         }
7101         
7102         return cfg;
7103     }
7104     
7105     
7106 //    initEvents : function()
7107 //    {
7108 //        
7109 //        if(!this.store){
7110 //            return;
7111 //        }
7112 //        
7113 //        this.store = Roo.factory(this.store, Roo.data);
7114 //        this.store.on('load', this.onLoad, this);
7115 //        
7116 //        this.store.load();
7117 //        
7118 //    },
7119 //    
7120 //    onLoad: function () 
7121 //    {   
7122 //        this.fireEvent('load', this);
7123 //    }
7124 //    
7125 //   
7126 });
7127
7128  
7129
7130  /*
7131  * Based on:
7132  * Ext JS Library 1.1.1
7133  * Copyright(c) 2006-2007, Ext JS, LLC.
7134  *
7135  * Originally Released Under LGPL - original licence link has changed is not relivant.
7136  *
7137  * Fork - LGPL
7138  * <script type="text/javascript">
7139  */
7140
7141 // as we use this in bootstrap.
7142 Roo.namespace('Roo.form');
7143  /**
7144  * @class Roo.form.Action
7145  * Internal Class used to handle form actions
7146  * @constructor
7147  * @param {Roo.form.BasicForm} el The form element or its id
7148  * @param {Object} config Configuration options
7149  */
7150
7151  
7152  
7153 // define the action interface
7154 Roo.form.Action = function(form, options){
7155     this.form = form;
7156     this.options = options || {};
7157 };
7158 /**
7159  * Client Validation Failed
7160  * @const 
7161  */
7162 Roo.form.Action.CLIENT_INVALID = 'client';
7163 /**
7164  * Server Validation Failed
7165  * @const 
7166  */
7167 Roo.form.Action.SERVER_INVALID = 'server';
7168  /**
7169  * Connect to Server Failed
7170  * @const 
7171  */
7172 Roo.form.Action.CONNECT_FAILURE = 'connect';
7173 /**
7174  * Reading Data from Server Failed
7175  * @const 
7176  */
7177 Roo.form.Action.LOAD_FAILURE = 'load';
7178
7179 Roo.form.Action.prototype = {
7180     type : 'default',
7181     failureType : undefined,
7182     response : undefined,
7183     result : undefined,
7184
7185     // interface method
7186     run : function(options){
7187
7188     },
7189
7190     // interface method
7191     success : function(response){
7192
7193     },
7194
7195     // interface method
7196     handleResponse : function(response){
7197
7198     },
7199
7200     // default connection failure
7201     failure : function(response){
7202         
7203         this.response = response;
7204         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7205         this.form.afterAction(this, false);
7206     },
7207
7208     processResponse : function(response){
7209         this.response = response;
7210         if(!response.responseText){
7211             return true;
7212         }
7213         this.result = this.handleResponse(response);
7214         return this.result;
7215     },
7216
7217     // utility functions used internally
7218     getUrl : function(appendParams){
7219         var url = this.options.url || this.form.url || this.form.el.dom.action;
7220         if(appendParams){
7221             var p = this.getParams();
7222             if(p){
7223                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7224             }
7225         }
7226         return url;
7227     },
7228
7229     getMethod : function(){
7230         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7231     },
7232
7233     getParams : function(){
7234         var bp = this.form.baseParams;
7235         var p = this.options.params;
7236         if(p){
7237             if(typeof p == "object"){
7238                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7239             }else if(typeof p == 'string' && bp){
7240                 p += '&' + Roo.urlEncode(bp);
7241             }
7242         }else if(bp){
7243             p = Roo.urlEncode(bp);
7244         }
7245         return p;
7246     },
7247
7248     createCallback : function(){
7249         return {
7250             success: this.success,
7251             failure: this.failure,
7252             scope: this,
7253             timeout: (this.form.timeout*1000),
7254             upload: this.form.fileUpload ? this.success : undefined
7255         };
7256     }
7257 };
7258
7259 Roo.form.Action.Submit = function(form, options){
7260     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7261 };
7262
7263 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7264     type : 'submit',
7265
7266     haveProgress : false,
7267     uploadComplete : false,
7268     
7269     // uploadProgress indicator.
7270     uploadProgress : function()
7271     {
7272         if (!this.form.progressUrl) {
7273             return;
7274         }
7275         
7276         if (!this.haveProgress) {
7277             Roo.MessageBox.progress("Uploading", "Uploading");
7278         }
7279         if (this.uploadComplete) {
7280            Roo.MessageBox.hide();
7281            return;
7282         }
7283         
7284         this.haveProgress = true;
7285    
7286         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7287         
7288         var c = new Roo.data.Connection();
7289         c.request({
7290             url : this.form.progressUrl,
7291             params: {
7292                 id : uid
7293             },
7294             method: 'GET',
7295             success : function(req){
7296                //console.log(data);
7297                 var rdata = false;
7298                 var edata;
7299                 try  {
7300                    rdata = Roo.decode(req.responseText)
7301                 } catch (e) {
7302                     Roo.log("Invalid data from server..");
7303                     Roo.log(edata);
7304                     return;
7305                 }
7306                 if (!rdata || !rdata.success) {
7307                     Roo.log(rdata);
7308                     Roo.MessageBox.alert(Roo.encode(rdata));
7309                     return;
7310                 }
7311                 var data = rdata.data;
7312                 
7313                 if (this.uploadComplete) {
7314                    Roo.MessageBox.hide();
7315                    return;
7316                 }
7317                    
7318                 if (data){
7319                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7320                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7321                     );
7322                 }
7323                 this.uploadProgress.defer(2000,this);
7324             },
7325        
7326             failure: function(data) {
7327                 Roo.log('progress url failed ');
7328                 Roo.log(data);
7329             },
7330             scope : this
7331         });
7332            
7333     },
7334     
7335     
7336     run : function()
7337     {
7338         // run get Values on the form, so it syncs any secondary forms.
7339         this.form.getValues();
7340         
7341         var o = this.options;
7342         var method = this.getMethod();
7343         var isPost = method == 'POST';
7344         if(o.clientValidation === false || this.form.isValid()){
7345             
7346             if (this.form.progressUrl) {
7347                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7348                     (new Date() * 1) + '' + Math.random());
7349                     
7350             } 
7351             
7352             
7353             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7354                 form:this.form.el.dom,
7355                 url:this.getUrl(!isPost),
7356                 method: method,
7357                 params:isPost ? this.getParams() : null,
7358                 isUpload: this.form.fileUpload
7359             }));
7360             
7361             this.uploadProgress();
7362
7363         }else if (o.clientValidation !== false){ // client validation failed
7364             this.failureType = Roo.form.Action.CLIENT_INVALID;
7365             this.form.afterAction(this, false);
7366         }
7367     },
7368
7369     success : function(response)
7370     {
7371         this.uploadComplete= true;
7372         if (this.haveProgress) {
7373             Roo.MessageBox.hide();
7374         }
7375         
7376         
7377         var result = this.processResponse(response);
7378         if(result === true || result.success){
7379             this.form.afterAction(this, true);
7380             return;
7381         }
7382         if(result.errors){
7383             this.form.markInvalid(result.errors);
7384             this.failureType = Roo.form.Action.SERVER_INVALID;
7385         }
7386         this.form.afterAction(this, false);
7387     },
7388     failure : function(response)
7389     {
7390         this.uploadComplete= true;
7391         if (this.haveProgress) {
7392             Roo.MessageBox.hide();
7393         }
7394         
7395         this.response = response;
7396         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7397         this.form.afterAction(this, false);
7398     },
7399     
7400     handleResponse : function(response){
7401         if(this.form.errorReader){
7402             var rs = this.form.errorReader.read(response);
7403             var errors = [];
7404             if(rs.records){
7405                 for(var i = 0, len = rs.records.length; i < len; i++) {
7406                     var r = rs.records[i];
7407                     errors[i] = r.data;
7408                 }
7409             }
7410             if(errors.length < 1){
7411                 errors = null;
7412             }
7413             return {
7414                 success : rs.success,
7415                 errors : errors
7416             };
7417         }
7418         var ret = false;
7419         try {
7420             ret = Roo.decode(response.responseText);
7421         } catch (e) {
7422             ret = {
7423                 success: false,
7424                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7425                 errors : []
7426             };
7427         }
7428         return ret;
7429         
7430     }
7431 });
7432
7433
7434 Roo.form.Action.Load = function(form, options){
7435     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7436     this.reader = this.form.reader;
7437 };
7438
7439 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7440     type : 'load',
7441
7442     run : function(){
7443         
7444         Roo.Ajax.request(Roo.apply(
7445                 this.createCallback(), {
7446                     method:this.getMethod(),
7447                     url:this.getUrl(false),
7448                     params:this.getParams()
7449         }));
7450     },
7451
7452     success : function(response){
7453         
7454         var result = this.processResponse(response);
7455         if(result === true || !result.success || !result.data){
7456             this.failureType = Roo.form.Action.LOAD_FAILURE;
7457             this.form.afterAction(this, false);
7458             return;
7459         }
7460         this.form.clearInvalid();
7461         this.form.setValues(result.data);
7462         this.form.afterAction(this, true);
7463     },
7464
7465     handleResponse : function(response){
7466         if(this.form.reader){
7467             var rs = this.form.reader.read(response);
7468             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7469             return {
7470                 success : rs.success,
7471                 data : data
7472             };
7473         }
7474         return Roo.decode(response.responseText);
7475     }
7476 });
7477
7478 Roo.form.Action.ACTION_TYPES = {
7479     'load' : Roo.form.Action.Load,
7480     'submit' : Roo.form.Action.Submit
7481 };/*
7482  * - LGPL
7483  *
7484  * form
7485  *
7486  */
7487
7488 /**
7489  * @class Roo.bootstrap.Form
7490  * @extends Roo.bootstrap.Component
7491  * Bootstrap Form class
7492  * @cfg {String} method  GET | POST (default POST)
7493  * @cfg {String} labelAlign top | left (default top)
7494  * @cfg {String} align left  | right - for navbars
7495  * @cfg {Boolean} loadMask load mask when submit (default true)
7496
7497  *
7498  * @constructor
7499  * Create a new Form
7500  * @param {Object} config The config object
7501  */
7502
7503
7504 Roo.bootstrap.Form = function(config){
7505     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7506     
7507     Roo.bootstrap.Form.popover.apply();
7508     
7509     this.addEvents({
7510         /**
7511          * @event clientvalidation
7512          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7513          * @param {Form} this
7514          * @param {Boolean} valid true if the form has passed client-side validation
7515          */
7516         clientvalidation: true,
7517         /**
7518          * @event beforeaction
7519          * Fires before any action is performed. Return false to cancel the action.
7520          * @param {Form} this
7521          * @param {Action} action The action to be performed
7522          */
7523         beforeaction: true,
7524         /**
7525          * @event actionfailed
7526          * Fires when an action fails.
7527          * @param {Form} this
7528          * @param {Action} action The action that failed
7529          */
7530         actionfailed : true,
7531         /**
7532          * @event actioncomplete
7533          * Fires when an action is completed.
7534          * @param {Form} this
7535          * @param {Action} action The action that completed
7536          */
7537         actioncomplete : true
7538     });
7539
7540 };
7541
7542 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7543
7544      /**
7545      * @cfg {String} method
7546      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7547      */
7548     method : 'POST',
7549     /**
7550      * @cfg {String} url
7551      * The URL to use for form actions if one isn't supplied in the action options.
7552      */
7553     /**
7554      * @cfg {Boolean} fileUpload
7555      * Set to true if this form is a file upload.
7556      */
7557
7558     /**
7559      * @cfg {Object} baseParams
7560      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7561      */
7562
7563     /**
7564      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7565      */
7566     timeout: 30,
7567     /**
7568      * @cfg {Sting} align (left|right) for navbar forms
7569      */
7570     align : 'left',
7571
7572     // private
7573     activeAction : null,
7574
7575     /**
7576      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7577      * element by passing it or its id or mask the form itself by passing in true.
7578      * @type Mixed
7579      */
7580     waitMsgTarget : false,
7581
7582     loadMask : true,
7583     
7584     /**
7585      * @cfg {Boolean} errorMask (true|false) default false
7586      */
7587     errorMask : false,
7588     
7589     /**
7590      * @cfg {Number} maskOffset Default 100
7591      */
7592     maskOffset : 100,
7593
7594     getAutoCreate : function(){
7595
7596         var cfg = {
7597             tag: 'form',
7598             method : this.method || 'POST',
7599             id : this.id || Roo.id(),
7600             cls : ''
7601         };
7602         if (this.parent().xtype.match(/^Nav/)) {
7603             cfg.cls = 'navbar-form navbar-' + this.align;
7604
7605         }
7606
7607         if (this.labelAlign == 'left' ) {
7608             cfg.cls += ' form-horizontal';
7609         }
7610
7611
7612         return cfg;
7613     },
7614     initEvents : function()
7615     {
7616         this.el.on('submit', this.onSubmit, this);
7617         // this was added as random key presses on the form where triggering form submit.
7618         this.el.on('keypress', function(e) {
7619             if (e.getCharCode() != 13) {
7620                 return true;
7621             }
7622             // we might need to allow it for textareas.. and some other items.
7623             // check e.getTarget().
7624
7625             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7626                 return true;
7627             }
7628
7629             Roo.log("keypress blocked");
7630
7631             e.preventDefault();
7632             return false;
7633         });
7634         
7635     },
7636     // private
7637     onSubmit : function(e){
7638         e.stopEvent();
7639     },
7640
7641      /**
7642      * Returns true if client-side validation on the form is successful.
7643      * @return Boolean
7644      */
7645     isValid : function(){
7646         var items = this.getItems();
7647         var valid = true;
7648         var target = false;
7649         
7650         items.each(function(f){
7651             
7652             if(f.validate()){
7653                 return;
7654             }
7655             valid = false;
7656
7657             if(!target && f.el.isVisible(true)){
7658                 target = f;
7659             }
7660            
7661         });
7662         
7663         if(this.errorMask && !valid){
7664             Roo.bootstrap.Form.popover.mask(this, target);
7665         }
7666         
7667         return valid;
7668     },
7669     
7670     /**
7671      * Returns true if any fields in this form have changed since their original load.
7672      * @return Boolean
7673      */
7674     isDirty : function(){
7675         var dirty = false;
7676         var items = this.getItems();
7677         items.each(function(f){
7678            if(f.isDirty()){
7679                dirty = true;
7680                return false;
7681            }
7682            return true;
7683         });
7684         return dirty;
7685     },
7686      /**
7687      * Performs a predefined action (submit or load) or custom actions you define on this form.
7688      * @param {String} actionName The name of the action type
7689      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7690      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7691      * accept other config options):
7692      * <pre>
7693 Property          Type             Description
7694 ----------------  ---------------  ----------------------------------------------------------------------------------
7695 url               String           The url for the action (defaults to the form's url)
7696 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7697 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7698 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7699                                    validate the form on the client (defaults to false)
7700      * </pre>
7701      * @return {BasicForm} this
7702      */
7703     doAction : function(action, options){
7704         if(typeof action == 'string'){
7705             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7706         }
7707         if(this.fireEvent('beforeaction', this, action) !== false){
7708             this.beforeAction(action);
7709             action.run.defer(100, action);
7710         }
7711         return this;
7712     },
7713
7714     // private
7715     beforeAction : function(action){
7716         var o = action.options;
7717
7718         if(this.loadMask){
7719             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7720         }
7721         // not really supported yet.. ??
7722
7723         //if(this.waitMsgTarget === true){
7724         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7725         //}else if(this.waitMsgTarget){
7726         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7727         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7728         //}else {
7729         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7730        // }
7731
7732     },
7733
7734     // private
7735     afterAction : function(action, success){
7736         this.activeAction = null;
7737         var o = action.options;
7738
7739         //if(this.waitMsgTarget === true){
7740             this.el.unmask();
7741         //}else if(this.waitMsgTarget){
7742         //    this.waitMsgTarget.unmask();
7743         //}else{
7744         //    Roo.MessageBox.updateProgress(1);
7745         //    Roo.MessageBox.hide();
7746        // }
7747         //
7748         if(success){
7749             if(o.reset){
7750                 this.reset();
7751             }
7752             Roo.callback(o.success, o.scope, [this, action]);
7753             this.fireEvent('actioncomplete', this, action);
7754
7755         }else{
7756
7757             // failure condition..
7758             // we have a scenario where updates need confirming.
7759             // eg. if a locking scenario exists..
7760             // we look for { errors : { needs_confirm : true }} in the response.
7761             if (
7762                 (typeof(action.result) != 'undefined')  &&
7763                 (typeof(action.result.errors) != 'undefined')  &&
7764                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7765            ){
7766                 var _t = this;
7767                 Roo.log("not supported yet");
7768                  /*
7769
7770                 Roo.MessageBox.confirm(
7771                     "Change requires confirmation",
7772                     action.result.errorMsg,
7773                     function(r) {
7774                         if (r != 'yes') {
7775                             return;
7776                         }
7777                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7778                     }
7779
7780                 );
7781                 */
7782
7783
7784                 return;
7785             }
7786
7787             Roo.callback(o.failure, o.scope, [this, action]);
7788             // show an error message if no failed handler is set..
7789             if (!this.hasListener('actionfailed')) {
7790                 Roo.log("need to add dialog support");
7791                 /*
7792                 Roo.MessageBox.alert("Error",
7793                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7794                         action.result.errorMsg :
7795                         "Saving Failed, please check your entries or try again"
7796                 );
7797                 */
7798             }
7799
7800             this.fireEvent('actionfailed', this, action);
7801         }
7802
7803     },
7804     /**
7805      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7806      * @param {String} id The value to search for
7807      * @return Field
7808      */
7809     findField : function(id){
7810         var items = this.getItems();
7811         var field = items.get(id);
7812         if(!field){
7813              items.each(function(f){
7814                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7815                     field = f;
7816                     return false;
7817                 }
7818                 return true;
7819             });
7820         }
7821         return field || null;
7822     },
7823      /**
7824      * Mark fields in this form invalid in bulk.
7825      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7826      * @return {BasicForm} this
7827      */
7828     markInvalid : function(errors){
7829         if(errors instanceof Array){
7830             for(var i = 0, len = errors.length; i < len; i++){
7831                 var fieldError = errors[i];
7832                 var f = this.findField(fieldError.id);
7833                 if(f){
7834                     f.markInvalid(fieldError.msg);
7835                 }
7836             }
7837         }else{
7838             var field, id;
7839             for(id in errors){
7840                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7841                     field.markInvalid(errors[id]);
7842                 }
7843             }
7844         }
7845         //Roo.each(this.childForms || [], function (f) {
7846         //    f.markInvalid(errors);
7847         //});
7848
7849         return this;
7850     },
7851
7852     /**
7853      * Set values for fields in this form in bulk.
7854      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7855      * @return {BasicForm} this
7856      */
7857     setValues : function(values){
7858         if(values instanceof Array){ // array of objects
7859             for(var i = 0, len = values.length; i < len; i++){
7860                 var v = values[i];
7861                 var f = this.findField(v.id);
7862                 if(f){
7863                     f.setValue(v.value);
7864                     if(this.trackResetOnLoad){
7865                         f.originalValue = f.getValue();
7866                     }
7867                 }
7868             }
7869         }else{ // object hash
7870             var field, id;
7871             for(id in values){
7872                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7873
7874                     if (field.setFromData &&
7875                         field.valueField &&
7876                         field.displayField &&
7877                         // combos' with local stores can
7878                         // be queried via setValue()
7879                         // to set their value..
7880                         (field.store && !field.store.isLocal)
7881                         ) {
7882                         // it's a combo
7883                         var sd = { };
7884                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7885                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7886                         field.setFromData(sd);
7887
7888                     } else {
7889                         field.setValue(values[id]);
7890                     }
7891
7892
7893                     if(this.trackResetOnLoad){
7894                         field.originalValue = field.getValue();
7895                     }
7896                 }
7897             }
7898         }
7899
7900         //Roo.each(this.childForms || [], function (f) {
7901         //    f.setValues(values);
7902         //});
7903
7904         return this;
7905     },
7906
7907     /**
7908      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7909      * they are returned as an array.
7910      * @param {Boolean} asString
7911      * @return {Object}
7912      */
7913     getValues : function(asString){
7914         //if (this.childForms) {
7915             // copy values from the child forms
7916         //    Roo.each(this.childForms, function (f) {
7917         //        this.setValues(f.getValues());
7918         //    }, this);
7919         //}
7920
7921
7922
7923         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7924         if(asString === true){
7925             return fs;
7926         }
7927         return Roo.urlDecode(fs);
7928     },
7929
7930     /**
7931      * Returns the fields in this form as an object with key/value pairs.
7932      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7933      * @return {Object}
7934      */
7935     getFieldValues : function(with_hidden)
7936     {
7937         var items = this.getItems();
7938         var ret = {};
7939         items.each(function(f){
7940             if (!f.getName()) {
7941                 return;
7942             }
7943             var v = f.getValue();
7944             if (f.inputType =='radio') {
7945                 if (typeof(ret[f.getName()]) == 'undefined') {
7946                     ret[f.getName()] = ''; // empty..
7947                 }
7948
7949                 if (!f.el.dom.checked) {
7950                     return;
7951
7952                 }
7953                 v = f.el.dom.value;
7954
7955             }
7956
7957             // not sure if this supported any more..
7958             if ((typeof(v) == 'object') && f.getRawValue) {
7959                 v = f.getRawValue() ; // dates..
7960             }
7961             // combo boxes where name != hiddenName...
7962             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7963                 ret[f.name] = f.getRawValue();
7964             }
7965             ret[f.getName()] = v;
7966         });
7967
7968         return ret;
7969     },
7970
7971     /**
7972      * Clears all invalid messages in this form.
7973      * @return {BasicForm} this
7974      */
7975     clearInvalid : function(){
7976         var items = this.getItems();
7977
7978         items.each(function(f){
7979            f.clearInvalid();
7980         });
7981
7982
7983
7984         return this;
7985     },
7986
7987     /**
7988      * Resets this form.
7989      * @return {BasicForm} this
7990      */
7991     reset : function(){
7992         var items = this.getItems();
7993         items.each(function(f){
7994             f.reset();
7995         });
7996
7997         Roo.each(this.childForms || [], function (f) {
7998             f.reset();
7999         });
8000
8001
8002         return this;
8003     },
8004     getItems : function()
8005     {
8006         var r=new Roo.util.MixedCollection(false, function(o){
8007             return o.id || (o.id = Roo.id());
8008         });
8009         var iter = function(el) {
8010             if (el.inputEl) {
8011                 r.add(el);
8012             }
8013             if (!el.items) {
8014                 return;
8015             }
8016             Roo.each(el.items,function(e) {
8017                 iter(e);
8018             });
8019
8020
8021         };
8022
8023         iter(this);
8024         return r;
8025
8026
8027
8028
8029     }
8030
8031 });
8032
8033 Roo.apply(Roo.bootstrap.Form, {
8034     
8035     popover : {
8036         
8037         padding : 5,
8038         
8039         isApplied : false,
8040         
8041         isMasked : false,
8042         
8043         form : false,
8044         
8045         target : false,
8046         
8047         toolTip : false,
8048         
8049         intervalID : false,
8050         
8051         maskEl : false,
8052         
8053         apply : function()
8054         {
8055             if(this.isApplied){
8056                 return;
8057             }
8058             
8059             this.maskEl = {
8060                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8061                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8062                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8063                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8064             };
8065             
8066             this.maskEl.top.enableDisplayMode("block");
8067             this.maskEl.left.enableDisplayMode("block");
8068             this.maskEl.bottom.enableDisplayMode("block");
8069             this.maskEl.right.enableDisplayMode("block");
8070             
8071             this.toolTip = new Roo.bootstrap.Tooltip({
8072                 cls : 'roo-form-error-popover',
8073                 alignment : {
8074                     'left' : ['r-l', [-2,0], 'right'],
8075                     'right' : ['l-r', [2,0], 'left'],
8076                     'bottom' : ['tl-bl', [0,2], 'top'],
8077                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8078                 }
8079             });
8080             
8081             this.toolTip.render(Roo.get(document.body));
8082
8083             this.toolTip.el.enableDisplayMode("block");
8084             
8085             Roo.get(document.body).on('click', function(){
8086                 this.unmask();
8087             }, this);
8088             
8089             Roo.get(document.body).on('touchstart', function(){
8090                 this.unmask();
8091             }, this);
8092             
8093             this.isApplied = true
8094         },
8095         
8096         mask : function(form, target)
8097         {
8098             this.form = form;
8099             
8100             this.target = target;
8101             
8102             if(!this.form.errorMask || !target.el){
8103                 return;
8104             }
8105             
8106             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8107             
8108             var ot = this.target.el.calcOffsetsTo(scrollable);
8109             
8110             var scrollTo = ot[1] - this.form.maskOffset;
8111             
8112             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8113             
8114             scrollable.scrollTo('top', scrollTo);
8115             
8116             var box = this.target.el.getBox();
8117             Roo.log(box);
8118             var zIndex = Roo.bootstrap.Modal.zIndex++;
8119
8120             
8121             this.maskEl.top.setStyle('position', 'absolute');
8122             this.maskEl.top.setStyle('z-index', zIndex);
8123             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8124             this.maskEl.top.setLeft(0);
8125             this.maskEl.top.setTop(0);
8126             this.maskEl.top.show();
8127             
8128             this.maskEl.left.setStyle('position', 'absolute');
8129             this.maskEl.left.setStyle('z-index', zIndex);
8130             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8131             this.maskEl.left.setLeft(0);
8132             this.maskEl.left.setTop(box.y - this.padding);
8133             this.maskEl.left.show();
8134
8135             this.maskEl.bottom.setStyle('position', 'absolute');
8136             this.maskEl.bottom.setStyle('z-index', zIndex);
8137             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8138             this.maskEl.bottom.setLeft(0);
8139             this.maskEl.bottom.setTop(box.bottom + this.padding);
8140             this.maskEl.bottom.show();
8141
8142             this.maskEl.right.setStyle('position', 'absolute');
8143             this.maskEl.right.setStyle('z-index', zIndex);
8144             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8145             this.maskEl.right.setLeft(box.right + this.padding);
8146             this.maskEl.right.setTop(box.y - this.padding);
8147             this.maskEl.right.show();
8148
8149             this.toolTip.bindEl = this.target.el;
8150
8151             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8152
8153             var tip = this.target.blankText;
8154
8155             if(this.target.getValue() !== '' ) {
8156                 
8157                 if (this.target.invalidText.length) {
8158                     tip = this.target.invalidText;
8159                 } else if (this.target.regexText.length){
8160                     tip = this.target.regexText;
8161                 }
8162             }
8163
8164             this.toolTip.show(tip);
8165
8166             this.intervalID = window.setInterval(function() {
8167                 Roo.bootstrap.Form.popover.unmask();
8168             }, 10000);
8169
8170             window.onwheel = function(){ return false;};
8171             
8172             (function(){ this.isMasked = true; }).defer(500, this);
8173             
8174         },
8175         
8176         unmask : function()
8177         {
8178             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8179                 return;
8180             }
8181             
8182             this.maskEl.top.setStyle('position', 'absolute');
8183             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8184             this.maskEl.top.hide();
8185
8186             this.maskEl.left.setStyle('position', 'absolute');
8187             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8188             this.maskEl.left.hide();
8189
8190             this.maskEl.bottom.setStyle('position', 'absolute');
8191             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8192             this.maskEl.bottom.hide();
8193
8194             this.maskEl.right.setStyle('position', 'absolute');
8195             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8196             this.maskEl.right.hide();
8197             
8198             this.toolTip.hide();
8199             
8200             this.toolTip.el.hide();
8201             
8202             window.onwheel = function(){ return true;};
8203             
8204             if(this.intervalID){
8205                 window.clearInterval(this.intervalID);
8206                 this.intervalID = false;
8207             }
8208             
8209             this.isMasked = false;
8210             
8211         }
8212         
8213     }
8214     
8215 });
8216
8217 /*
8218  * Based on:
8219  * Ext JS Library 1.1.1
8220  * Copyright(c) 2006-2007, Ext JS, LLC.
8221  *
8222  * Originally Released Under LGPL - original licence link has changed is not relivant.
8223  *
8224  * Fork - LGPL
8225  * <script type="text/javascript">
8226  */
8227 /**
8228  * @class Roo.form.VTypes
8229  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8230  * @singleton
8231  */
8232 Roo.form.VTypes = function(){
8233     // closure these in so they are only created once.
8234     var alpha = /^[a-zA-Z_]+$/;
8235     var alphanum = /^[a-zA-Z0-9_]+$/;
8236     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8237     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8238
8239     // All these messages and functions are configurable
8240     return {
8241         /**
8242          * The function used to validate email addresses
8243          * @param {String} value The email address
8244          */
8245         'email' : function(v){
8246             return email.test(v);
8247         },
8248         /**
8249          * The error text to display when the email validation function returns false
8250          * @type String
8251          */
8252         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8253         /**
8254          * The keystroke filter mask to be applied on email input
8255          * @type RegExp
8256          */
8257         'emailMask' : /[a-z0-9_\.\-@]/i,
8258
8259         /**
8260          * The function used to validate URLs
8261          * @param {String} value The URL
8262          */
8263         'url' : function(v){
8264             return url.test(v);
8265         },
8266         /**
8267          * The error text to display when the url validation function returns false
8268          * @type String
8269          */
8270         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8271         
8272         /**
8273          * The function used to validate alpha values
8274          * @param {String} value The value
8275          */
8276         'alpha' : function(v){
8277             return alpha.test(v);
8278         },
8279         /**
8280          * The error text to display when the alpha validation function returns false
8281          * @type String
8282          */
8283         'alphaText' : 'This field should only contain letters and _',
8284         /**
8285          * The keystroke filter mask to be applied on alpha input
8286          * @type RegExp
8287          */
8288         'alphaMask' : /[a-z_]/i,
8289
8290         /**
8291          * The function used to validate alphanumeric values
8292          * @param {String} value The value
8293          */
8294         'alphanum' : function(v){
8295             return alphanum.test(v);
8296         },
8297         /**
8298          * The error text to display when the alphanumeric validation function returns false
8299          * @type String
8300          */
8301         'alphanumText' : 'This field should only contain letters, numbers and _',
8302         /**
8303          * The keystroke filter mask to be applied on alphanumeric input
8304          * @type RegExp
8305          */
8306         'alphanumMask' : /[a-z0-9_]/i
8307     };
8308 }();/*
8309  * - LGPL
8310  *
8311  * Input
8312  * 
8313  */
8314
8315 /**
8316  * @class Roo.bootstrap.Input
8317  * @extends Roo.bootstrap.Component
8318  * Bootstrap Input class
8319  * @cfg {Boolean} disabled is it disabled
8320  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8321  * @cfg {String} name name of the input
8322  * @cfg {string} fieldLabel - the label associated
8323  * @cfg {string} placeholder - placeholder to put in text.
8324  * @cfg {string}  before - input group add on before
8325  * @cfg {string} after - input group add on after
8326  * @cfg {string} size - (lg|sm) or leave empty..
8327  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8328  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8329  * @cfg {Number} md colspan out of 12 for computer-sized screens
8330  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8331  * @cfg {string} value default value of the input
8332  * @cfg {Number} labelWidth set the width of label 
8333  * @cfg {Number} labellg set the width of label (1-12)
8334  * @cfg {Number} labelmd set the width of label (1-12)
8335  * @cfg {Number} labelsm set the width of label (1-12)
8336  * @cfg {Number} labelxs set the width of label (1-12)
8337  * @cfg {String} labelAlign (top|left)
8338  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8339  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8340  * @cfg {String} indicatorpos (left|right) default left
8341
8342  * @cfg {String} align (left|center|right) Default left
8343  * @cfg {Boolean} forceFeedback (true|false) Default false
8344  * 
8345  * 
8346  * 
8347  * 
8348  * @constructor
8349  * Create a new Input
8350  * @param {Object} config The config object
8351  */
8352
8353 Roo.bootstrap.Input = function(config){
8354     
8355     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8356     
8357     this.addEvents({
8358         /**
8359          * @event focus
8360          * Fires when this field receives input focus.
8361          * @param {Roo.form.Field} this
8362          */
8363         focus : true,
8364         /**
8365          * @event blur
8366          * Fires when this field loses input focus.
8367          * @param {Roo.form.Field} this
8368          */
8369         blur : true,
8370         /**
8371          * @event specialkey
8372          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8373          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8374          * @param {Roo.form.Field} this
8375          * @param {Roo.EventObject} e The event object
8376          */
8377         specialkey : true,
8378         /**
8379          * @event change
8380          * Fires just before the field blurs if the field value has changed.
8381          * @param {Roo.form.Field} this
8382          * @param {Mixed} newValue The new value
8383          * @param {Mixed} oldValue The original value
8384          */
8385         change : true,
8386         /**
8387          * @event invalid
8388          * Fires after the field has been marked as invalid.
8389          * @param {Roo.form.Field} this
8390          * @param {String} msg The validation message
8391          */
8392         invalid : true,
8393         /**
8394          * @event valid
8395          * Fires after the field has been validated with no errors.
8396          * @param {Roo.form.Field} this
8397          */
8398         valid : true,
8399          /**
8400          * @event keyup
8401          * Fires after the key up
8402          * @param {Roo.form.Field} this
8403          * @param {Roo.EventObject}  e The event Object
8404          */
8405         keyup : true
8406     });
8407 };
8408
8409 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8410      /**
8411      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8412       automatic validation (defaults to "keyup").
8413      */
8414     validationEvent : "keyup",
8415      /**
8416      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8417      */
8418     validateOnBlur : true,
8419     /**
8420      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8421      */
8422     validationDelay : 250,
8423      /**
8424      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8425      */
8426     focusClass : "x-form-focus",  // not needed???
8427     
8428        
8429     /**
8430      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8431      */
8432     invalidClass : "has-warning",
8433     
8434     /**
8435      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8436      */
8437     validClass : "has-success",
8438     
8439     /**
8440      * @cfg {Boolean} hasFeedback (true|false) default true
8441      */
8442     hasFeedback : true,
8443     
8444     /**
8445      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8446      */
8447     invalidFeedbackClass : "glyphicon-warning-sign",
8448     
8449     /**
8450      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8451      */
8452     validFeedbackClass : "glyphicon-ok",
8453     
8454     /**
8455      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8456      */
8457     selectOnFocus : false,
8458     
8459      /**
8460      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8461      */
8462     maskRe : null,
8463        /**
8464      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8465      */
8466     vtype : null,
8467     
8468       /**
8469      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8470      */
8471     disableKeyFilter : false,
8472     
8473        /**
8474      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8475      */
8476     disabled : false,
8477      /**
8478      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8479      */
8480     allowBlank : true,
8481     /**
8482      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8483      */
8484     blankText : "Please complete this mandatory field",
8485     
8486      /**
8487      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8488      */
8489     minLength : 0,
8490     /**
8491      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8492      */
8493     maxLength : Number.MAX_VALUE,
8494     /**
8495      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8496      */
8497     minLengthText : "The minimum length for this field is {0}",
8498     /**
8499      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8500      */
8501     maxLengthText : "The maximum length for this field is {0}",
8502   
8503     
8504     /**
8505      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8506      * If available, this function will be called only after the basic validators all return true, and will be passed the
8507      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8508      */
8509     validator : null,
8510     /**
8511      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8512      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8513      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8514      */
8515     regex : null,
8516     /**
8517      * @cfg {String} regexText -- Depricated - use Invalid Text
8518      */
8519     regexText : "",
8520     
8521     /**
8522      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8523      */
8524     invalidText : "",
8525     
8526     
8527     
8528     autocomplete: false,
8529     
8530     
8531     fieldLabel : '',
8532     inputType : 'text',
8533     
8534     name : false,
8535     placeholder: false,
8536     before : false,
8537     after : false,
8538     size : false,
8539     hasFocus : false,
8540     preventMark: false,
8541     isFormField : true,
8542     value : '',
8543     labelWidth : 2,
8544     labelAlign : false,
8545     readOnly : false,
8546     align : false,
8547     formatedValue : false,
8548     forceFeedback : false,
8549     
8550     indicatorpos : 'left',
8551     
8552     labellg : 0,
8553     labelmd : 0,
8554     labelsm : 0,
8555     labelxs : 0,
8556     
8557     parentLabelAlign : function()
8558     {
8559         var parent = this;
8560         while (parent.parent()) {
8561             parent = parent.parent();
8562             if (typeof(parent.labelAlign) !='undefined') {
8563                 return parent.labelAlign;
8564             }
8565         }
8566         return 'left';
8567         
8568     },
8569     
8570     getAutoCreate : function()
8571     {
8572         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8573         
8574         var id = Roo.id();
8575         
8576         var cfg = {};
8577         
8578         if(this.inputType != 'hidden'){
8579             cfg.cls = 'form-group' //input-group
8580         }
8581         
8582         var input =  {
8583             tag: 'input',
8584             id : id,
8585             type : this.inputType,
8586             value : this.value,
8587             cls : 'form-control',
8588             placeholder : this.placeholder || '',
8589             autocomplete : this.autocomplete || 'new-password'
8590         };
8591         
8592         if(this.align){
8593             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8594         }
8595         
8596         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8597             input.maxLength = this.maxLength;
8598         }
8599         
8600         if (this.disabled) {
8601             input.disabled=true;
8602         }
8603         
8604         if (this.readOnly) {
8605             input.readonly=true;
8606         }
8607         
8608         if (this.name) {
8609             input.name = this.name;
8610         }
8611         
8612         if (this.size) {
8613             input.cls += ' input-' + this.size;
8614         }
8615         
8616         var settings=this;
8617         ['xs','sm','md','lg'].map(function(size){
8618             if (settings[size]) {
8619                 cfg.cls += ' col-' + size + '-' + settings[size];
8620             }
8621         });
8622         
8623         var inputblock = input;
8624         
8625         var feedback = {
8626             tag: 'span',
8627             cls: 'glyphicon form-control-feedback'
8628         };
8629             
8630         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8631             
8632             inputblock = {
8633                 cls : 'has-feedback',
8634                 cn :  [
8635                     input,
8636                     feedback
8637                 ] 
8638             };  
8639         }
8640         
8641         if (this.before || this.after) {
8642             
8643             inputblock = {
8644                 cls : 'input-group',
8645                 cn :  [] 
8646             };
8647             
8648             if (this.before && typeof(this.before) == 'string') {
8649                 
8650                 inputblock.cn.push({
8651                     tag :'span',
8652                     cls : 'roo-input-before input-group-addon',
8653                     html : this.before
8654                 });
8655             }
8656             if (this.before && typeof(this.before) == 'object') {
8657                 this.before = Roo.factory(this.before);
8658                 
8659                 inputblock.cn.push({
8660                     tag :'span',
8661                     cls : 'roo-input-before input-group-' +
8662                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8663                 });
8664             }
8665             
8666             inputblock.cn.push(input);
8667             
8668             if (this.after && typeof(this.after) == 'string') {
8669                 inputblock.cn.push({
8670                     tag :'span',
8671                     cls : 'roo-input-after input-group-addon',
8672                     html : this.after
8673                 });
8674             }
8675             if (this.after && typeof(this.after) == 'object') {
8676                 this.after = Roo.factory(this.after);
8677                 
8678                 inputblock.cn.push({
8679                     tag :'span',
8680                     cls : 'roo-input-after input-group-' +
8681                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8682                 });
8683             }
8684             
8685             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8686                 inputblock.cls += ' has-feedback';
8687                 inputblock.cn.push(feedback);
8688             }
8689         };
8690         
8691         if (align ==='left' && this.fieldLabel.length) {
8692             
8693             cfg.cls += ' roo-form-group-label-left';
8694             
8695             cfg.cn = [
8696                 {
8697                     tag : 'i',
8698                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8699                     tooltip : 'This field is required'
8700                 },
8701                 {
8702                     tag: 'label',
8703                     'for' :  id,
8704                     cls : 'control-label',
8705                     html : this.fieldLabel
8706
8707                 },
8708                 {
8709                     cls : "", 
8710                     cn: [
8711                         inputblock
8712                     ]
8713                 }
8714             ];
8715             
8716             var labelCfg = cfg.cn[1];
8717             var contentCfg = cfg.cn[2];
8718             
8719             if(this.indicatorpos == 'right'){
8720                 cfg.cn = [
8721                     {
8722                         tag: 'label',
8723                         'for' :  id,
8724                         cls : 'control-label',
8725                         html : this.fieldLabel
8726
8727                     },
8728                     {
8729                         tag : 'i',
8730                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8731                         tooltip : 'This field is required'
8732                     },
8733                     {
8734                         cls : "",
8735                         cn: [
8736                             inputblock
8737                         ]
8738                     }
8739
8740                 ];
8741                 
8742                 labelCfg = cfg.cn[0];
8743                 contentCfg = cfg.cn[2];
8744             
8745             }
8746             
8747             if(this.labelWidth > 12){
8748                 labelCfg.style = "width: " + this.labelWidth + 'px';
8749             }
8750             
8751             if(this.labelWidth < 13 && this.labelmd == 0){
8752                 this.labelmd = this.labelWidth;
8753             }
8754             
8755             if(this.labellg > 0){
8756                 labelCfg.cls += ' col-lg-' + this.labellg;
8757                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8758             }
8759             
8760             if(this.labelmd > 0){
8761                 labelCfg.cls += ' col-md-' + this.labelmd;
8762                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8763             }
8764             
8765             if(this.labelsm > 0){
8766                 labelCfg.cls += ' col-sm-' + this.labelsm;
8767                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8768             }
8769             
8770             if(this.labelxs > 0){
8771                 labelCfg.cls += ' col-xs-' + this.labelxs;
8772                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8773             }
8774             
8775             
8776         } else if ( this.fieldLabel.length) {
8777                 
8778             cfg.cn = [
8779                 {
8780                     tag : 'i',
8781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8782                     tooltip : 'This field is required'
8783                 },
8784                 {
8785                     tag: 'label',
8786                    //cls : 'input-group-addon',
8787                     html : this.fieldLabel
8788
8789                 },
8790
8791                inputblock
8792
8793            ];
8794            
8795            if(this.indicatorpos == 'right'){
8796                 
8797                 cfg.cn = [
8798                     {
8799                         tag: 'label',
8800                        //cls : 'input-group-addon',
8801                         html : this.fieldLabel
8802
8803                     },
8804                     {
8805                         tag : 'i',
8806                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8807                         tooltip : 'This field is required'
8808                     },
8809
8810                    inputblock
8811
8812                ];
8813
8814             }
8815
8816         } else {
8817             
8818             cfg.cn = [
8819
8820                     inputblock
8821
8822             ];
8823                 
8824                 
8825         };
8826         
8827         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8828            cfg.cls += ' navbar-form';
8829         }
8830         
8831         if (this.parentType === 'NavGroup') {
8832            cfg.cls += ' navbar-form';
8833            cfg.tag = 'li';
8834         }
8835         
8836         return cfg;
8837         
8838     },
8839     /**
8840      * return the real input element.
8841      */
8842     inputEl: function ()
8843     {
8844         return this.el.select('input.form-control',true).first();
8845     },
8846     
8847     tooltipEl : function()
8848     {
8849         return this.inputEl();
8850     },
8851     
8852     indicatorEl : function()
8853     {
8854         var indicator = this.el.select('i.roo-required-indicator',true).first();
8855         
8856         if(!indicator){
8857             return false;
8858         }
8859         
8860         return indicator;
8861         
8862     },
8863     
8864     setDisabled : function(v)
8865     {
8866         var i  = this.inputEl().dom;
8867         if (!v) {
8868             i.removeAttribute('disabled');
8869             return;
8870             
8871         }
8872         i.setAttribute('disabled','true');
8873     },
8874     initEvents : function()
8875     {
8876           
8877         this.inputEl().on("keydown" , this.fireKey,  this);
8878         this.inputEl().on("focus", this.onFocus,  this);
8879         this.inputEl().on("blur", this.onBlur,  this);
8880         
8881         this.inputEl().relayEvent('keyup', this);
8882         
8883         this.indicator = this.indicatorEl();
8884         
8885         if(this.indicator){
8886             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8887             this.indicator.hide();
8888         }
8889  
8890         // reference to original value for reset
8891         this.originalValue = this.getValue();
8892         //Roo.form.TextField.superclass.initEvents.call(this);
8893         if(this.validationEvent == 'keyup'){
8894             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8895             this.inputEl().on('keyup', this.filterValidation, this);
8896         }
8897         else if(this.validationEvent !== false){
8898             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8899         }
8900         
8901         if(this.selectOnFocus){
8902             this.on("focus", this.preFocus, this);
8903             
8904         }
8905         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8906             this.inputEl().on("keypress", this.filterKeys, this);
8907         } else {
8908             this.inputEl().relayEvent('keypress', this);
8909         }
8910        /* if(this.grow){
8911             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8912             this.el.on("click", this.autoSize,  this);
8913         }
8914         */
8915         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8916             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8917         }
8918         
8919         if (typeof(this.before) == 'object') {
8920             this.before.render(this.el.select('.roo-input-before',true).first());
8921         }
8922         if (typeof(this.after) == 'object') {
8923             this.after.render(this.el.select('.roo-input-after',true).first());
8924         }
8925         
8926         
8927     },
8928     filterValidation : function(e){
8929         if(!e.isNavKeyPress()){
8930             this.validationTask.delay(this.validationDelay);
8931         }
8932     },
8933      /**
8934      * Validates the field value
8935      * @return {Boolean} True if the value is valid, else false
8936      */
8937     validate : function(){
8938         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8939         if(this.disabled || this.validateValue(this.getRawValue())){
8940             this.markValid();
8941             return true;
8942         }
8943         
8944         this.markInvalid();
8945         return false;
8946     },
8947     
8948     
8949     /**
8950      * Validates a value according to the field's validation rules and marks the field as invalid
8951      * if the validation fails
8952      * @param {Mixed} value The value to validate
8953      * @return {Boolean} True if the value is valid, else false
8954      */
8955     validateValue : function(value){
8956         if(value.length < 1)  { // if it's blank
8957             if(this.allowBlank){
8958                 return true;
8959             }            
8960             return this.inputEl().hasClass('hide') ? true : false;
8961         }
8962         
8963         if(value.length < this.minLength){
8964             return false;
8965         }
8966         if(value.length > this.maxLength){
8967             return false;
8968         }
8969         if(this.vtype){
8970             var vt = Roo.form.VTypes;
8971             if(!vt[this.vtype](value, this)){
8972                 return false;
8973             }
8974         }
8975         if(typeof this.validator == "function"){
8976             var msg = this.validator(value);
8977             if(msg !== true){
8978                 return false;
8979             }
8980             if (typeof(msg) == 'string') {
8981                 this.invalidText = msg;
8982             }
8983         }
8984         
8985         if(this.regex && !this.regex.test(value)){
8986             return false;
8987         }
8988         
8989         return true;
8990     },
8991
8992     
8993     
8994      // private
8995     fireKey : function(e){
8996         //Roo.log('field ' + e.getKey());
8997         if(e.isNavKeyPress()){
8998             this.fireEvent("specialkey", this, e);
8999         }
9000     },
9001     focus : function (selectText){
9002         if(this.rendered){
9003             this.inputEl().focus();
9004             if(selectText === true){
9005                 this.inputEl().dom.select();
9006             }
9007         }
9008         return this;
9009     } ,
9010     
9011     onFocus : function(){
9012         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9013            // this.el.addClass(this.focusClass);
9014         }
9015         if(!this.hasFocus){
9016             this.hasFocus = true;
9017             this.startValue = this.getValue();
9018             this.fireEvent("focus", this);
9019         }
9020     },
9021     
9022     beforeBlur : Roo.emptyFn,
9023
9024     
9025     // private
9026     onBlur : function(){
9027         this.beforeBlur();
9028         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9029             //this.el.removeClass(this.focusClass);
9030         }
9031         this.hasFocus = false;
9032         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9033             this.validate();
9034         }
9035         var v = this.getValue();
9036         if(String(v) !== String(this.startValue)){
9037             this.fireEvent('change', this, v, this.startValue);
9038         }
9039         this.fireEvent("blur", this);
9040     },
9041     
9042     /**
9043      * Resets the current field value to the originally loaded value and clears any validation messages
9044      */
9045     reset : function(){
9046         this.setValue(this.originalValue);
9047         this.validate();
9048     },
9049      /**
9050      * Returns the name of the field
9051      * @return {Mixed} name The name field
9052      */
9053     getName: function(){
9054         return this.name;
9055     },
9056      /**
9057      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9058      * @return {Mixed} value The field value
9059      */
9060     getValue : function(){
9061         
9062         var v = this.inputEl().getValue();
9063         
9064         return v;
9065     },
9066     /**
9067      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9068      * @return {Mixed} value The field value
9069      */
9070     getRawValue : function(){
9071         var v = this.inputEl().getValue();
9072         
9073         return v;
9074     },
9075     
9076     /**
9077      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9078      * @param {Mixed} value The value to set
9079      */
9080     setRawValue : function(v){
9081         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9082     },
9083     
9084     selectText : function(start, end){
9085         var v = this.getRawValue();
9086         if(v.length > 0){
9087             start = start === undefined ? 0 : start;
9088             end = end === undefined ? v.length : end;
9089             var d = this.inputEl().dom;
9090             if(d.setSelectionRange){
9091                 d.setSelectionRange(start, end);
9092             }else if(d.createTextRange){
9093                 var range = d.createTextRange();
9094                 range.moveStart("character", start);
9095                 range.moveEnd("character", v.length-end);
9096                 range.select();
9097             }
9098         }
9099     },
9100     
9101     /**
9102      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9103      * @param {Mixed} value The value to set
9104      */
9105     setValue : function(v){
9106         this.value = v;
9107         if(this.rendered){
9108             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9109             this.validate();
9110         }
9111     },
9112     
9113     /*
9114     processValue : function(value){
9115         if(this.stripCharsRe){
9116             var newValue = value.replace(this.stripCharsRe, '');
9117             if(newValue !== value){
9118                 this.setRawValue(newValue);
9119                 return newValue;
9120             }
9121         }
9122         return value;
9123     },
9124   */
9125     preFocus : function(){
9126         
9127         if(this.selectOnFocus){
9128             this.inputEl().dom.select();
9129         }
9130     },
9131     filterKeys : function(e){
9132         var k = e.getKey();
9133         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9134             return;
9135         }
9136         var c = e.getCharCode(), cc = String.fromCharCode(c);
9137         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9138             return;
9139         }
9140         if(!this.maskRe.test(cc)){
9141             e.stopEvent();
9142         }
9143     },
9144      /**
9145      * Clear any invalid styles/messages for this field
9146      */
9147     clearInvalid : function(){
9148         
9149         if(!this.el || this.preventMark){ // not rendered
9150             return;
9151         }
9152         
9153      
9154         this.el.removeClass(this.invalidClass);
9155         
9156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9157             
9158             var feedback = this.el.select('.form-control-feedback', true).first();
9159             
9160             if(feedback){
9161                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9162             }
9163             
9164         }
9165         
9166         this.fireEvent('valid', this);
9167     },
9168     
9169      /**
9170      * Mark this field as valid
9171      */
9172     markValid : function()
9173     {
9174         if(!this.el  || this.preventMark){ // not rendered...
9175             return;
9176         }
9177         
9178         this.el.removeClass([this.invalidClass, this.validClass]);
9179         
9180         var feedback = this.el.select('.form-control-feedback', true).first();
9181             
9182         if(feedback){
9183             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9184         }
9185
9186         if(this.disabled){
9187             return;
9188         }
9189         
9190         if(this.allowBlank && !this.getRawValue().length){
9191             return;
9192         }
9193         
9194         if(this.indicator){
9195             this.indicator.hide();
9196         }
9197         
9198         this.el.addClass(this.validClass);
9199         
9200         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9201             
9202             var feedback = this.el.select('.form-control-feedback', true).first();
9203             
9204             if(feedback){
9205                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9206                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9207             }
9208             
9209         }
9210         
9211         this.fireEvent('valid', this);
9212     },
9213     
9214      /**
9215      * Mark this field as invalid
9216      * @param {String} msg The validation message
9217      */
9218     markInvalid : function(msg)
9219     {
9220         if(!this.el  || this.preventMark){ // not rendered
9221             return;
9222         }
9223         
9224         this.el.removeClass([this.invalidClass, this.validClass]);
9225         
9226         var feedback = this.el.select('.form-control-feedback', true).first();
9227             
9228         if(feedback){
9229             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9230         }
9231
9232         if(this.disabled){
9233             return;
9234         }
9235         
9236         if(this.allowBlank && !this.getRawValue().length){
9237             return;
9238         }
9239         
9240         if(this.indicator){
9241             this.indicator.show();
9242         }
9243         
9244         this.el.addClass(this.invalidClass);
9245         
9246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9247             
9248             var feedback = this.el.select('.form-control-feedback', true).first();
9249             
9250             if(feedback){
9251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252                 
9253                 if(this.getValue().length || this.forceFeedback){
9254                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9255                 }
9256                 
9257             }
9258             
9259         }
9260         
9261         this.fireEvent('invalid', this, msg);
9262     },
9263     // private
9264     SafariOnKeyDown : function(event)
9265     {
9266         // this is a workaround for a password hang bug on chrome/ webkit.
9267         if (this.inputEl().dom.type != 'password') {
9268             return;
9269         }
9270         
9271         var isSelectAll = false;
9272         
9273         if(this.inputEl().dom.selectionEnd > 0){
9274             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9275         }
9276         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9277             event.preventDefault();
9278             this.setValue('');
9279             return;
9280         }
9281         
9282         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9283             
9284             event.preventDefault();
9285             // this is very hacky as keydown always get's upper case.
9286             //
9287             var cc = String.fromCharCode(event.getCharCode());
9288             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9289             
9290         }
9291     },
9292     adjustWidth : function(tag, w){
9293         tag = tag.toLowerCase();
9294         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9295             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9296                 if(tag == 'input'){
9297                     return w + 2;
9298                 }
9299                 if(tag == 'textarea'){
9300                     return w-2;
9301                 }
9302             }else if(Roo.isOpera){
9303                 if(tag == 'input'){
9304                     return w + 2;
9305                 }
9306                 if(tag == 'textarea'){
9307                     return w-2;
9308                 }
9309             }
9310         }
9311         return w;
9312     }
9313     
9314 });
9315
9316  
9317 /*
9318  * - LGPL
9319  *
9320  * Input
9321  * 
9322  */
9323
9324 /**
9325  * @class Roo.bootstrap.TextArea
9326  * @extends Roo.bootstrap.Input
9327  * Bootstrap TextArea class
9328  * @cfg {Number} cols Specifies the visible width of a text area
9329  * @cfg {Number} rows Specifies the visible number of lines in a text area
9330  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9331  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9332  * @cfg {string} html text
9333  * 
9334  * @constructor
9335  * Create a new TextArea
9336  * @param {Object} config The config object
9337  */
9338
9339 Roo.bootstrap.TextArea = function(config){
9340     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9341    
9342 };
9343
9344 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9345      
9346     cols : false,
9347     rows : 5,
9348     readOnly : false,
9349     warp : 'soft',
9350     resize : false,
9351     value: false,
9352     html: false,
9353     
9354     getAutoCreate : function(){
9355         
9356         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9357         
9358         var id = Roo.id();
9359         
9360         var cfg = {};
9361         
9362         var input =  {
9363             tag: 'textarea',
9364             id : id,
9365             warp : this.warp,
9366             rows : this.rows,
9367             value : this.value || '',
9368             html: this.html || '',
9369             cls : 'form-control',
9370             placeholder : this.placeholder || '' 
9371             
9372         };
9373         
9374         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9375             input.maxLength = this.maxLength;
9376         }
9377         
9378         if(this.resize){
9379             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9380         }
9381         
9382         if(this.cols){
9383             input.cols = this.cols;
9384         }
9385         
9386         if (this.readOnly) {
9387             input.readonly = true;
9388         }
9389         
9390         if (this.name) {
9391             input.name = this.name;
9392         }
9393         
9394         if (this.size) {
9395             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9396         }
9397         
9398         var settings=this;
9399         ['xs','sm','md','lg'].map(function(size){
9400             if (settings[size]) {
9401                 cfg.cls += ' col-' + size + '-' + settings[size];
9402             }
9403         });
9404         
9405         var inputblock = input;
9406         
9407         if(this.hasFeedback && !this.allowBlank){
9408             
9409             var feedback = {
9410                 tag: 'span',
9411                 cls: 'glyphicon form-control-feedback'
9412             };
9413
9414             inputblock = {
9415                 cls : 'has-feedback',
9416                 cn :  [
9417                     input,
9418                     feedback
9419                 ] 
9420             };  
9421         }
9422         
9423         
9424         if (this.before || this.after) {
9425             
9426             inputblock = {
9427                 cls : 'input-group',
9428                 cn :  [] 
9429             };
9430             if (this.before) {
9431                 inputblock.cn.push({
9432                     tag :'span',
9433                     cls : 'input-group-addon',
9434                     html : this.before
9435                 });
9436             }
9437             
9438             inputblock.cn.push(input);
9439             
9440             if(this.hasFeedback && !this.allowBlank){
9441                 inputblock.cls += ' has-feedback';
9442                 inputblock.cn.push(feedback);
9443             }
9444             
9445             if (this.after) {
9446                 inputblock.cn.push({
9447                     tag :'span',
9448                     cls : 'input-group-addon',
9449                     html : this.after
9450                 });
9451             }
9452             
9453         }
9454         
9455         if (align ==='left' && this.fieldLabel.length) {
9456             cfg.cn = [
9457                 {
9458                     tag: 'label',
9459                     'for' :  id,
9460                     cls : 'control-label',
9461                     html : this.fieldLabel
9462                 },
9463                 {
9464                     cls : "",
9465                     cn: [
9466                         inputblock
9467                     ]
9468                 }
9469
9470             ];
9471             
9472             if(this.labelWidth > 12){
9473                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9474             }
9475
9476             if(this.labelWidth < 13 && this.labelmd == 0){
9477                 this.labelmd = this.labelWidth;
9478             }
9479
9480             if(this.labellg > 0){
9481                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9482                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9483             }
9484
9485             if(this.labelmd > 0){
9486                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9487                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9488             }
9489
9490             if(this.labelsm > 0){
9491                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9492                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9493             }
9494
9495             if(this.labelxs > 0){
9496                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9497                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9498             }
9499             
9500         } else if ( this.fieldLabel.length) {
9501             cfg.cn = [
9502
9503                {
9504                    tag: 'label',
9505                    //cls : 'input-group-addon',
9506                    html : this.fieldLabel
9507
9508                },
9509
9510                inputblock
9511
9512            ];
9513
9514         } else {
9515
9516             cfg.cn = [
9517
9518                 inputblock
9519
9520             ];
9521                 
9522         }
9523         
9524         if (this.disabled) {
9525             input.disabled=true;
9526         }
9527         
9528         return cfg;
9529         
9530     },
9531     /**
9532      * return the real textarea element.
9533      */
9534     inputEl: function ()
9535     {
9536         return this.el.select('textarea.form-control',true).first();
9537     },
9538     
9539     /**
9540      * Clear any invalid styles/messages for this field
9541      */
9542     clearInvalid : function()
9543     {
9544         
9545         if(!this.el || this.preventMark){ // not rendered
9546             return;
9547         }
9548         
9549         var label = this.el.select('label', true).first();
9550         var icon = this.el.select('i.fa-star', true).first();
9551         
9552         if(label && icon){
9553             icon.remove();
9554         }
9555         
9556         this.el.removeClass(this.invalidClass);
9557         
9558         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9559             
9560             var feedback = this.el.select('.form-control-feedback', true).first();
9561             
9562             if(feedback){
9563                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9564             }
9565             
9566         }
9567         
9568         this.fireEvent('valid', this);
9569     },
9570     
9571      /**
9572      * Mark this field as valid
9573      */
9574     markValid : function()
9575     {
9576         if(!this.el  || this.preventMark){ // not rendered
9577             return;
9578         }
9579         
9580         this.el.removeClass([this.invalidClass, this.validClass]);
9581         
9582         var feedback = this.el.select('.form-control-feedback', true).first();
9583             
9584         if(feedback){
9585             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9586         }
9587
9588         if(this.disabled || this.allowBlank){
9589             return;
9590         }
9591         
9592         var label = this.el.select('label', true).first();
9593         var icon = this.el.select('i.fa-star', true).first();
9594         
9595         if(label && icon){
9596             icon.remove();
9597         }
9598         
9599         this.el.addClass(this.validClass);
9600         
9601         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9602             
9603             var feedback = this.el.select('.form-control-feedback', true).first();
9604             
9605             if(feedback){
9606                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9607                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9608             }
9609             
9610         }
9611         
9612         this.fireEvent('valid', this);
9613     },
9614     
9615      /**
9616      * Mark this field as invalid
9617      * @param {String} msg The validation message
9618      */
9619     markInvalid : function(msg)
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(!this.getValue().length && label && !icon){
9641             this.el.createChild({
9642                 tag : 'i',
9643                 cls : 'text-danger fa fa-lg fa-star',
9644                 tooltip : 'This field is required',
9645                 style : 'margin-right:5px;'
9646             }, label, true);
9647         }
9648
9649         this.el.addClass(this.invalidClass);
9650         
9651         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9652             
9653             var feedback = this.el.select('.form-control-feedback', true).first();
9654             
9655             if(feedback){
9656                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657                 
9658                 if(this.getValue().length || this.forceFeedback){
9659                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9660                 }
9661                 
9662             }
9663             
9664         }
9665         
9666         this.fireEvent('invalid', this, msg);
9667     }
9668 });
9669
9670  
9671 /*
9672  * - LGPL
9673  *
9674  * trigger field - base class for combo..
9675  * 
9676  */
9677  
9678 /**
9679  * @class Roo.bootstrap.TriggerField
9680  * @extends Roo.bootstrap.Input
9681  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9682  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9683  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9684  * for which you can provide a custom implementation.  For example:
9685  * <pre><code>
9686 var trigger = new Roo.bootstrap.TriggerField();
9687 trigger.onTriggerClick = myTriggerFn;
9688 trigger.applyTo('my-field');
9689 </code></pre>
9690  *
9691  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9692  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9693  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9694  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9695  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9696
9697  * @constructor
9698  * Create a new TriggerField.
9699  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9700  * to the base TextField)
9701  */
9702 Roo.bootstrap.TriggerField = function(config){
9703     this.mimicing = false;
9704     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9705 };
9706
9707 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9708     /**
9709      * @cfg {String} triggerClass A CSS class to apply to the trigger
9710      */
9711      /**
9712      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9713      */
9714     hideTrigger:false,
9715
9716     /**
9717      * @cfg {Boolean} removable (true|false) special filter default false
9718      */
9719     removable : false,
9720     
9721     /** @cfg {Boolean} grow @hide */
9722     /** @cfg {Number} growMin @hide */
9723     /** @cfg {Number} growMax @hide */
9724
9725     /**
9726      * @hide 
9727      * @method
9728      */
9729     autoSize: Roo.emptyFn,
9730     // private
9731     monitorTab : true,
9732     // private
9733     deferHeight : true,
9734
9735     
9736     actionMode : 'wrap',
9737     
9738     caret : false,
9739     
9740     
9741     getAutoCreate : function(){
9742        
9743         var align = this.labelAlign || this.parentLabelAlign();
9744         
9745         var id = Roo.id();
9746         
9747         var cfg = {
9748             cls: 'form-group' //input-group
9749         };
9750         
9751         
9752         var input =  {
9753             tag: 'input',
9754             id : id,
9755             type : this.inputType,
9756             cls : 'form-control',
9757             autocomplete: 'new-password',
9758             placeholder : this.placeholder || '' 
9759             
9760         };
9761         if (this.name) {
9762             input.name = this.name;
9763         }
9764         if (this.size) {
9765             input.cls += ' input-' + this.size;
9766         }
9767         
9768         if (this.disabled) {
9769             input.disabled=true;
9770         }
9771         
9772         var inputblock = input;
9773         
9774         if(this.hasFeedback && !this.allowBlank){
9775             
9776             var feedback = {
9777                 tag: 'span',
9778                 cls: 'glyphicon form-control-feedback'
9779             };
9780             
9781             if(this.removable && !this.editable && !this.tickable){
9782                 inputblock = {
9783                     cls : 'has-feedback',
9784                     cn :  [
9785                         inputblock,
9786                         {
9787                             tag: 'button',
9788                             html : 'x',
9789                             cls : 'roo-combo-removable-btn close'
9790                         },
9791                         feedback
9792                     ] 
9793                 };
9794             } else {
9795                 inputblock = {
9796                     cls : 'has-feedback',
9797                     cn :  [
9798                         inputblock,
9799                         feedback
9800                     ] 
9801                 };
9802             }
9803
9804         } else {
9805             if(this.removable && !this.editable && !this.tickable){
9806                 inputblock = {
9807                     cls : 'roo-removable',
9808                     cn :  [
9809                         inputblock,
9810                         {
9811                             tag: 'button',
9812                             html : 'x',
9813                             cls : 'roo-combo-removable-btn close'
9814                         }
9815                     ] 
9816                 };
9817             }
9818         }
9819         
9820         if (this.before || this.after) {
9821             
9822             inputblock = {
9823                 cls : 'input-group',
9824                 cn :  [] 
9825             };
9826             if (this.before) {
9827                 inputblock.cn.push({
9828                     tag :'span',
9829                     cls : 'input-group-addon',
9830                     html : this.before
9831                 });
9832             }
9833             
9834             inputblock.cn.push(input);
9835             
9836             if(this.hasFeedback && !this.allowBlank){
9837                 inputblock.cls += ' has-feedback';
9838                 inputblock.cn.push(feedback);
9839             }
9840             
9841             if (this.after) {
9842                 inputblock.cn.push({
9843                     tag :'span',
9844                     cls : 'input-group-addon',
9845                     html : this.after
9846                 });
9847             }
9848             
9849         };
9850         
9851         var box = {
9852             tag: 'div',
9853             cn: [
9854                 {
9855                     tag: 'input',
9856                     type : 'hidden',
9857                     cls: 'form-hidden-field'
9858                 },
9859                 inputblock
9860             ]
9861             
9862         };
9863         
9864         if(this.multiple){
9865             box = {
9866                 tag: 'div',
9867                 cn: [
9868                     {
9869                         tag: 'input',
9870                         type : 'hidden',
9871                         cls: 'form-hidden-field'
9872                     },
9873                     {
9874                         tag: 'ul',
9875                         cls: 'roo-select2-choices',
9876                         cn:[
9877                             {
9878                                 tag: 'li',
9879                                 cls: 'roo-select2-search-field',
9880                                 cn: [
9881
9882                                     inputblock
9883                                 ]
9884                             }
9885                         ]
9886                     }
9887                 ]
9888             }
9889         };
9890         
9891         var combobox = {
9892             cls: 'roo-select2-container input-group',
9893             cn: [
9894                 box
9895 //                {
9896 //                    tag: 'ul',
9897 //                    cls: 'typeahead typeahead-long dropdown-menu',
9898 //                    style: 'display:none'
9899 //                }
9900             ]
9901         };
9902         
9903         if(!this.multiple && this.showToggleBtn){
9904             
9905             var caret = {
9906                         tag: 'span',
9907                         cls: 'caret'
9908              };
9909             if (this.caret != false) {
9910                 caret = {
9911                      tag: 'i',
9912                      cls: 'fa fa-' + this.caret
9913                 };
9914                 
9915             }
9916             
9917             combobox.cn.push({
9918                 tag :'span',
9919                 cls : 'input-group-addon btn dropdown-toggle',
9920                 cn : [
9921                     caret,
9922                     {
9923                         tag: 'span',
9924                         cls: 'combobox-clear',
9925                         cn  : [
9926                             {
9927                                 tag : 'i',
9928                                 cls: 'icon-remove'
9929                             }
9930                         ]
9931                     }
9932                 ]
9933
9934             })
9935         }
9936         
9937         if(this.multiple){
9938             combobox.cls += ' roo-select2-container-multi';
9939         }
9940         
9941         if (align ==='left' && this.fieldLabel.length) {
9942             
9943             cfg.cls += ' roo-form-group-label-left';
9944
9945             cfg.cn = [
9946                 {
9947                     tag : 'i',
9948                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9949                     tooltip : 'This field is required'
9950                 },
9951                 {
9952                     tag: 'label',
9953                     'for' :  id,
9954                     cls : 'control-label',
9955                     html : this.fieldLabel
9956
9957                 },
9958                 {
9959                     cls : "", 
9960                     cn: [
9961                         combobox
9962                     ]
9963                 }
9964
9965             ];
9966             
9967             var labelCfg = cfg.cn[1];
9968             var contentCfg = cfg.cn[2];
9969             
9970             if(this.indicatorpos == 'right'){
9971                 cfg.cn = [
9972                     {
9973                         tag: 'label',
9974                         'for' :  id,
9975                         cls : 'control-label',
9976                         cn : [
9977                             {
9978                                 tag : 'span',
9979                                 html : this.fieldLabel
9980                             },
9981                             {
9982                                 tag : 'i',
9983                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9984                                 tooltip : 'This field is required'
9985                             }
9986                         ]
9987                     },
9988                     {
9989                         cls : "", 
9990                         cn: [
9991                             combobox
9992                         ]
9993                     }
9994
9995                 ];
9996                 
9997                 labelCfg = cfg.cn[0];
9998                 contentCfg = cfg.cn[1];
9999             }
10000             
10001             if(this.labelWidth > 12){
10002                 labelCfg.style = "width: " + this.labelWidth + 'px';
10003             }
10004             
10005             if(this.labelWidth < 13 && this.labelmd == 0){
10006                 this.labelmd = this.labelWidth;
10007             }
10008             
10009             if(this.labellg > 0){
10010                 labelCfg.cls += ' col-lg-' + this.labellg;
10011                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10012             }
10013             
10014             if(this.labelmd > 0){
10015                 labelCfg.cls += ' col-md-' + this.labelmd;
10016                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10017             }
10018             
10019             if(this.labelsm > 0){
10020                 labelCfg.cls += ' col-sm-' + this.labelsm;
10021                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10022             }
10023             
10024             if(this.labelxs > 0){
10025                 labelCfg.cls += ' col-xs-' + this.labelxs;
10026                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10027             }
10028             
10029         } else if ( this.fieldLabel.length) {
10030 //                Roo.log(" label");
10031             cfg.cn = [
10032                 {
10033                    tag : 'i',
10034                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10035                    tooltip : 'This field is required'
10036                },
10037                {
10038                    tag: 'label',
10039                    //cls : 'input-group-addon',
10040                    html : this.fieldLabel
10041
10042                },
10043
10044                combobox
10045
10046             ];
10047             
10048             if(this.indicatorpos == 'right'){
10049                 
10050                 cfg.cn = [
10051                     {
10052                        tag: 'label',
10053                        cn : [
10054                            {
10055                                tag : 'span',
10056                                html : this.fieldLabel
10057                            },
10058                            {
10059                               tag : 'i',
10060                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10061                               tooltip : 'This field is required'
10062                            }
10063                        ]
10064
10065                     },
10066                     combobox
10067
10068                 ];
10069
10070             }
10071
10072         } else {
10073             
10074 //                Roo.log(" no label && no align");
10075                 cfg = combobox
10076                      
10077                 
10078         }
10079         
10080         var settings=this;
10081         ['xs','sm','md','lg'].map(function(size){
10082             if (settings[size]) {
10083                 cfg.cls += ' col-' + size + '-' + settings[size];
10084             }
10085         });
10086         
10087         return cfg;
10088         
10089     },
10090     
10091     
10092     
10093     // private
10094     onResize : function(w, h){
10095 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10096 //        if(typeof w == 'number'){
10097 //            var x = w - this.trigger.getWidth();
10098 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10099 //            this.trigger.setStyle('left', x+'px');
10100 //        }
10101     },
10102
10103     // private
10104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10105
10106     // private
10107     getResizeEl : function(){
10108         return this.inputEl();
10109     },
10110
10111     // private
10112     getPositionEl : function(){
10113         return this.inputEl();
10114     },
10115
10116     // private
10117     alignErrorIcon : function(){
10118         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10119     },
10120
10121     // private
10122     initEvents : function(){
10123         
10124         this.createList();
10125         
10126         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10127         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10128         if(!this.multiple && this.showToggleBtn){
10129             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10130             if(this.hideTrigger){
10131                 this.trigger.setDisplayed(false);
10132             }
10133             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10134         }
10135         
10136         if(this.multiple){
10137             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10138         }
10139         
10140         if(this.removable && !this.editable && !this.tickable){
10141             var close = this.closeTriggerEl();
10142             
10143             if(close){
10144                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10145                 close.on('click', this.removeBtnClick, this, close);
10146             }
10147         }
10148         
10149         //this.trigger.addClassOnOver('x-form-trigger-over');
10150         //this.trigger.addClassOnClick('x-form-trigger-click');
10151         
10152         //if(!this.width){
10153         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10154         //}
10155     },
10156     
10157     closeTriggerEl : function()
10158     {
10159         var close = this.el.select('.roo-combo-removable-btn', true).first();
10160         return close ? close : false;
10161     },
10162     
10163     removeBtnClick : function(e, h, el)
10164     {
10165         e.preventDefault();
10166         
10167         if(this.fireEvent("remove", this) !== false){
10168             this.reset();
10169             this.fireEvent("afterremove", this)
10170         }
10171     },
10172     
10173     createList : function()
10174     {
10175         this.list = Roo.get(document.body).createChild({
10176             tag: 'ul',
10177             cls: 'typeahead typeahead-long dropdown-menu',
10178             style: 'display:none'
10179         });
10180         
10181         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10182         
10183     },
10184
10185     // private
10186     initTrigger : function(){
10187        
10188     },
10189
10190     // private
10191     onDestroy : function(){
10192         if(this.trigger){
10193             this.trigger.removeAllListeners();
10194           //  this.trigger.remove();
10195         }
10196         //if(this.wrap){
10197         //    this.wrap.remove();
10198         //}
10199         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10200     },
10201
10202     // private
10203     onFocus : function(){
10204         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10205         /*
10206         if(!this.mimicing){
10207             this.wrap.addClass('x-trigger-wrap-focus');
10208             this.mimicing = true;
10209             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10210             if(this.monitorTab){
10211                 this.el.on("keydown", this.checkTab, this);
10212             }
10213         }
10214         */
10215     },
10216
10217     // private
10218     checkTab : function(e){
10219         if(e.getKey() == e.TAB){
10220             this.triggerBlur();
10221         }
10222     },
10223
10224     // private
10225     onBlur : function(){
10226         // do nothing
10227     },
10228
10229     // private
10230     mimicBlur : function(e, t){
10231         /*
10232         if(!this.wrap.contains(t) && this.validateBlur()){
10233             this.triggerBlur();
10234         }
10235         */
10236     },
10237
10238     // private
10239     triggerBlur : function(){
10240         this.mimicing = false;
10241         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10242         if(this.monitorTab){
10243             this.el.un("keydown", this.checkTab, this);
10244         }
10245         //this.wrap.removeClass('x-trigger-wrap-focus');
10246         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10247     },
10248
10249     // private
10250     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10251     validateBlur : function(e, t){
10252         return true;
10253     },
10254
10255     // private
10256     onDisable : function(){
10257         this.inputEl().dom.disabled = true;
10258         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10259         //if(this.wrap){
10260         //    this.wrap.addClass('x-item-disabled');
10261         //}
10262     },
10263
10264     // private
10265     onEnable : function(){
10266         this.inputEl().dom.disabled = false;
10267         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10268         //if(this.wrap){
10269         //    this.el.removeClass('x-item-disabled');
10270         //}
10271     },
10272
10273     // private
10274     onShow : function(){
10275         var ae = this.getActionEl();
10276         
10277         if(ae){
10278             ae.dom.style.display = '';
10279             ae.dom.style.visibility = 'visible';
10280         }
10281     },
10282
10283     // private
10284     
10285     onHide : function(){
10286         var ae = this.getActionEl();
10287         ae.dom.style.display = 'none';
10288     },
10289
10290     /**
10291      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10292      * by an implementing function.
10293      * @method
10294      * @param {EventObject} e
10295      */
10296     onTriggerClick : Roo.emptyFn
10297 });
10298  /*
10299  * Based on:
10300  * Ext JS Library 1.1.1
10301  * Copyright(c) 2006-2007, Ext JS, LLC.
10302  *
10303  * Originally Released Under LGPL - original licence link has changed is not relivant.
10304  *
10305  * Fork - LGPL
10306  * <script type="text/javascript">
10307  */
10308
10309
10310 /**
10311  * @class Roo.data.SortTypes
10312  * @singleton
10313  * Defines the default sorting (casting?) comparison functions used when sorting data.
10314  */
10315 Roo.data.SortTypes = {
10316     /**
10317      * Default sort that does nothing
10318      * @param {Mixed} s The value being converted
10319      * @return {Mixed} The comparison value
10320      */
10321     none : function(s){
10322         return s;
10323     },
10324     
10325     /**
10326      * The regular expression used to strip tags
10327      * @type {RegExp}
10328      * @property
10329      */
10330     stripTagsRE : /<\/?[^>]+>/gi,
10331     
10332     /**
10333      * Strips all HTML tags to sort on text only
10334      * @param {Mixed} s The value being converted
10335      * @return {String} The comparison value
10336      */
10337     asText : function(s){
10338         return String(s).replace(this.stripTagsRE, "");
10339     },
10340     
10341     /**
10342      * Strips all HTML tags to sort on text only - Case insensitive
10343      * @param {Mixed} s The value being converted
10344      * @return {String} The comparison value
10345      */
10346     asUCText : function(s){
10347         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10348     },
10349     
10350     /**
10351      * Case insensitive string
10352      * @param {Mixed} s The value being converted
10353      * @return {String} The comparison value
10354      */
10355     asUCString : function(s) {
10356         return String(s).toUpperCase();
10357     },
10358     
10359     /**
10360      * Date sorting
10361      * @param {Mixed} s The value being converted
10362      * @return {Number} The comparison value
10363      */
10364     asDate : function(s) {
10365         if(!s){
10366             return 0;
10367         }
10368         if(s instanceof Date){
10369             return s.getTime();
10370         }
10371         return Date.parse(String(s));
10372     },
10373     
10374     /**
10375      * Float sorting
10376      * @param {Mixed} s The value being converted
10377      * @return {Float} The comparison value
10378      */
10379     asFloat : function(s) {
10380         var val = parseFloat(String(s).replace(/,/g, ""));
10381         if(isNaN(val)) {
10382             val = 0;
10383         }
10384         return val;
10385     },
10386     
10387     /**
10388      * Integer sorting
10389      * @param {Mixed} s The value being converted
10390      * @return {Number} The comparison value
10391      */
10392     asInt : function(s) {
10393         var val = parseInt(String(s).replace(/,/g, ""));
10394         if(isNaN(val)) {
10395             val = 0;
10396         }
10397         return val;
10398     }
10399 };/*
10400  * Based on:
10401  * Ext JS Library 1.1.1
10402  * Copyright(c) 2006-2007, Ext JS, LLC.
10403  *
10404  * Originally Released Under LGPL - original licence link has changed is not relivant.
10405  *
10406  * Fork - LGPL
10407  * <script type="text/javascript">
10408  */
10409
10410 /**
10411 * @class Roo.data.Record
10412  * Instances of this class encapsulate both record <em>definition</em> information, and record
10413  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10414  * to access Records cached in an {@link Roo.data.Store} object.<br>
10415  * <p>
10416  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10417  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10418  * objects.<br>
10419  * <p>
10420  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10421  * @constructor
10422  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10423  * {@link #create}. The parameters are the same.
10424  * @param {Array} data An associative Array of data values keyed by the field name.
10425  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10426  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10427  * not specified an integer id is generated.
10428  */
10429 Roo.data.Record = function(data, id){
10430     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10431     this.data = data;
10432 };
10433
10434 /**
10435  * Generate a constructor for a specific record layout.
10436  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10437  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10438  * Each field definition object may contain the following properties: <ul>
10439  * <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,
10440  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10441  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10442  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10443  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10444  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10445  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10446  * this may be omitted.</p></li>
10447  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10448  * <ul><li>auto (Default, implies no conversion)</li>
10449  * <li>string</li>
10450  * <li>int</li>
10451  * <li>float</li>
10452  * <li>boolean</li>
10453  * <li>date</li></ul></p></li>
10454  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10455  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10456  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10457  * by the Reader into an object that will be stored in the Record. It is passed the
10458  * following parameters:<ul>
10459  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10460  * </ul></p></li>
10461  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10462  * </ul>
10463  * <br>usage:<br><pre><code>
10464 var TopicRecord = Roo.data.Record.create(
10465     {name: 'title', mapping: 'topic_title'},
10466     {name: 'author', mapping: 'username'},
10467     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10468     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10469     {name: 'lastPoster', mapping: 'user2'},
10470     {name: 'excerpt', mapping: 'post_text'}
10471 );
10472
10473 var myNewRecord = new TopicRecord({
10474     title: 'Do my job please',
10475     author: 'noobie',
10476     totalPosts: 1,
10477     lastPost: new Date(),
10478     lastPoster: 'Animal',
10479     excerpt: 'No way dude!'
10480 });
10481 myStore.add(myNewRecord);
10482 </code></pre>
10483  * @method create
10484  * @static
10485  */
10486 Roo.data.Record.create = function(o){
10487     var f = function(){
10488         f.superclass.constructor.apply(this, arguments);
10489     };
10490     Roo.extend(f, Roo.data.Record);
10491     var p = f.prototype;
10492     p.fields = new Roo.util.MixedCollection(false, function(field){
10493         return field.name;
10494     });
10495     for(var i = 0, len = o.length; i < len; i++){
10496         p.fields.add(new Roo.data.Field(o[i]));
10497     }
10498     f.getField = function(name){
10499         return p.fields.get(name);  
10500     };
10501     return f;
10502 };
10503
10504 Roo.data.Record.AUTO_ID = 1000;
10505 Roo.data.Record.EDIT = 'edit';
10506 Roo.data.Record.REJECT = 'reject';
10507 Roo.data.Record.COMMIT = 'commit';
10508
10509 Roo.data.Record.prototype = {
10510     /**
10511      * Readonly flag - true if this record has been modified.
10512      * @type Boolean
10513      */
10514     dirty : false,
10515     editing : false,
10516     error: null,
10517     modified: null,
10518
10519     // private
10520     join : function(store){
10521         this.store = store;
10522     },
10523
10524     /**
10525      * Set the named field to the specified value.
10526      * @param {String} name The name of the field to set.
10527      * @param {Object} value The value to set the field to.
10528      */
10529     set : function(name, value){
10530         if(this.data[name] == value){
10531             return;
10532         }
10533         this.dirty = true;
10534         if(!this.modified){
10535             this.modified = {};
10536         }
10537         if(typeof this.modified[name] == 'undefined'){
10538             this.modified[name] = this.data[name];
10539         }
10540         this.data[name] = value;
10541         if(!this.editing && this.store){
10542             this.store.afterEdit(this);
10543         }       
10544     },
10545
10546     /**
10547      * Get the value of the named field.
10548      * @param {String} name The name of the field to get the value of.
10549      * @return {Object} The value of the field.
10550      */
10551     get : function(name){
10552         return this.data[name]; 
10553     },
10554
10555     // private
10556     beginEdit : function(){
10557         this.editing = true;
10558         this.modified = {}; 
10559     },
10560
10561     // private
10562     cancelEdit : function(){
10563         this.editing = false;
10564         delete this.modified;
10565     },
10566
10567     // private
10568     endEdit : function(){
10569         this.editing = false;
10570         if(this.dirty && this.store){
10571             this.store.afterEdit(this);
10572         }
10573     },
10574
10575     /**
10576      * Usually called by the {@link Roo.data.Store} which owns the Record.
10577      * Rejects all changes made to the Record since either creation, or the last commit operation.
10578      * Modified fields are reverted to their original values.
10579      * <p>
10580      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10581      * of reject operations.
10582      */
10583     reject : function(){
10584         var m = this.modified;
10585         for(var n in m){
10586             if(typeof m[n] != "function"){
10587                 this.data[n] = m[n];
10588             }
10589         }
10590         this.dirty = false;
10591         delete this.modified;
10592         this.editing = false;
10593         if(this.store){
10594             this.store.afterReject(this);
10595         }
10596     },
10597
10598     /**
10599      * Usually called by the {@link Roo.data.Store} which owns the Record.
10600      * Commits all changes made to the Record since either creation, or the last commit operation.
10601      * <p>
10602      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10603      * of commit operations.
10604      */
10605     commit : function(){
10606         this.dirty = false;
10607         delete this.modified;
10608         this.editing = false;
10609         if(this.store){
10610             this.store.afterCommit(this);
10611         }
10612     },
10613
10614     // private
10615     hasError : function(){
10616         return this.error != null;
10617     },
10618
10619     // private
10620     clearError : function(){
10621         this.error = null;
10622     },
10623
10624     /**
10625      * Creates a copy of this record.
10626      * @param {String} id (optional) A new record id if you don't want to use this record's id
10627      * @return {Record}
10628      */
10629     copy : function(newId) {
10630         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10631     }
10632 };/*
10633  * Based on:
10634  * Ext JS Library 1.1.1
10635  * Copyright(c) 2006-2007, Ext JS, LLC.
10636  *
10637  * Originally Released Under LGPL - original licence link has changed is not relivant.
10638  *
10639  * Fork - LGPL
10640  * <script type="text/javascript">
10641  */
10642
10643
10644
10645 /**
10646  * @class Roo.data.Store
10647  * @extends Roo.util.Observable
10648  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10649  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10650  * <p>
10651  * 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
10652  * has no knowledge of the format of the data returned by the Proxy.<br>
10653  * <p>
10654  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10655  * instances from the data object. These records are cached and made available through accessor functions.
10656  * @constructor
10657  * Creates a new Store.
10658  * @param {Object} config A config object containing the objects needed for the Store to access data,
10659  * and read the data into Records.
10660  */
10661 Roo.data.Store = function(config){
10662     this.data = new Roo.util.MixedCollection(false);
10663     this.data.getKey = function(o){
10664         return o.id;
10665     };
10666     this.baseParams = {};
10667     // private
10668     this.paramNames = {
10669         "start" : "start",
10670         "limit" : "limit",
10671         "sort" : "sort",
10672         "dir" : "dir",
10673         "multisort" : "_multisort"
10674     };
10675
10676     if(config && config.data){
10677         this.inlineData = config.data;
10678         delete config.data;
10679     }
10680
10681     Roo.apply(this, config);
10682     
10683     if(this.reader){ // reader passed
10684         this.reader = Roo.factory(this.reader, Roo.data);
10685         this.reader.xmodule = this.xmodule || false;
10686         if(!this.recordType){
10687             this.recordType = this.reader.recordType;
10688         }
10689         if(this.reader.onMetaChange){
10690             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10691         }
10692     }
10693
10694     if(this.recordType){
10695         this.fields = this.recordType.prototype.fields;
10696     }
10697     this.modified = [];
10698
10699     this.addEvents({
10700         /**
10701          * @event datachanged
10702          * Fires when the data cache has changed, and a widget which is using this Store
10703          * as a Record cache should refresh its view.
10704          * @param {Store} this
10705          */
10706         datachanged : true,
10707         /**
10708          * @event metachange
10709          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10710          * @param {Store} this
10711          * @param {Object} meta The JSON metadata
10712          */
10713         metachange : true,
10714         /**
10715          * @event add
10716          * Fires when Records have been added to the Store
10717          * @param {Store} this
10718          * @param {Roo.data.Record[]} records The array of Records added
10719          * @param {Number} index The index at which the record(s) were added
10720          */
10721         add : true,
10722         /**
10723          * @event remove
10724          * Fires when a Record has been removed from the Store
10725          * @param {Store} this
10726          * @param {Roo.data.Record} record The Record that was removed
10727          * @param {Number} index The index at which the record was removed
10728          */
10729         remove : true,
10730         /**
10731          * @event update
10732          * Fires when a Record has been updated
10733          * @param {Store} this
10734          * @param {Roo.data.Record} record The Record that was updated
10735          * @param {String} operation The update operation being performed.  Value may be one of:
10736          * <pre><code>
10737  Roo.data.Record.EDIT
10738  Roo.data.Record.REJECT
10739  Roo.data.Record.COMMIT
10740          * </code></pre>
10741          */
10742         update : true,
10743         /**
10744          * @event clear
10745          * Fires when the data cache has been cleared.
10746          * @param {Store} this
10747          */
10748         clear : true,
10749         /**
10750          * @event beforeload
10751          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10752          * the load action will be canceled.
10753          * @param {Store} this
10754          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10755          */
10756         beforeload : true,
10757         /**
10758          * @event beforeloadadd
10759          * Fires after a new set of Records has been loaded.
10760          * @param {Store} this
10761          * @param {Roo.data.Record[]} records The Records that were loaded
10762          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10763          */
10764         beforeloadadd : true,
10765         /**
10766          * @event load
10767          * Fires after a new set of Records has been loaded, before they are added to the store.
10768          * @param {Store} this
10769          * @param {Roo.data.Record[]} records The Records that were loaded
10770          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10771          * @params {Object} return from reader
10772          */
10773         load : true,
10774         /**
10775          * @event loadexception
10776          * Fires if an exception occurs in the Proxy during loading.
10777          * Called with the signature of the Proxy's "loadexception" event.
10778          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10779          * 
10780          * @param {Proxy} 
10781          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10782          * @param {Object} load options 
10783          * @param {Object} jsonData from your request (normally this contains the Exception)
10784          */
10785         loadexception : true
10786     });
10787     
10788     if(this.proxy){
10789         this.proxy = Roo.factory(this.proxy, Roo.data);
10790         this.proxy.xmodule = this.xmodule || false;
10791         this.relayEvents(this.proxy,  ["loadexception"]);
10792     }
10793     this.sortToggle = {};
10794     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10795
10796     Roo.data.Store.superclass.constructor.call(this);
10797
10798     if(this.inlineData){
10799         this.loadData(this.inlineData);
10800         delete this.inlineData;
10801     }
10802 };
10803
10804 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10805      /**
10806     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10807     * without a remote query - used by combo/forms at present.
10808     */
10809     
10810     /**
10811     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10812     */
10813     /**
10814     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10815     */
10816     /**
10817     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10818     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10819     */
10820     /**
10821     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10822     * on any HTTP request
10823     */
10824     /**
10825     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10826     */
10827     /**
10828     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10829     */
10830     multiSort: false,
10831     /**
10832     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10833     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10834     */
10835     remoteSort : false,
10836
10837     /**
10838     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10839      * loaded or when a record is removed. (defaults to false).
10840     */
10841     pruneModifiedRecords : false,
10842
10843     // private
10844     lastOptions : null,
10845
10846     /**
10847      * Add Records to the Store and fires the add event.
10848      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10849      */
10850     add : function(records){
10851         records = [].concat(records);
10852         for(var i = 0, len = records.length; i < len; i++){
10853             records[i].join(this);
10854         }
10855         var index = this.data.length;
10856         this.data.addAll(records);
10857         this.fireEvent("add", this, records, index);
10858     },
10859
10860     /**
10861      * Remove a Record from the Store and fires the remove event.
10862      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10863      */
10864     remove : function(record){
10865         var index = this.data.indexOf(record);
10866         this.data.removeAt(index);
10867         if(this.pruneModifiedRecords){
10868             this.modified.remove(record);
10869         }
10870         this.fireEvent("remove", this, record, index);
10871     },
10872
10873     /**
10874      * Remove all Records from the Store and fires the clear event.
10875      */
10876     removeAll : function(){
10877         this.data.clear();
10878         if(this.pruneModifiedRecords){
10879             this.modified = [];
10880         }
10881         this.fireEvent("clear", this);
10882     },
10883
10884     /**
10885      * Inserts Records to the Store at the given index and fires the add event.
10886      * @param {Number} index The start index at which to insert the passed Records.
10887      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10888      */
10889     insert : function(index, records){
10890         records = [].concat(records);
10891         for(var i = 0, len = records.length; i < len; i++){
10892             this.data.insert(index, records[i]);
10893             records[i].join(this);
10894         }
10895         this.fireEvent("add", this, records, index);
10896     },
10897
10898     /**
10899      * Get the index within the cache of the passed Record.
10900      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10901      * @return {Number} The index of the passed Record. Returns -1 if not found.
10902      */
10903     indexOf : function(record){
10904         return this.data.indexOf(record);
10905     },
10906
10907     /**
10908      * Get the index within the cache of the Record with the passed id.
10909      * @param {String} id The id of the Record to find.
10910      * @return {Number} The index of the Record. Returns -1 if not found.
10911      */
10912     indexOfId : function(id){
10913         return this.data.indexOfKey(id);
10914     },
10915
10916     /**
10917      * Get the Record with the specified id.
10918      * @param {String} id The id of the Record to find.
10919      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10920      */
10921     getById : function(id){
10922         return this.data.key(id);
10923     },
10924
10925     /**
10926      * Get the Record at the specified index.
10927      * @param {Number} index The index of the Record to find.
10928      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10929      */
10930     getAt : function(index){
10931         return this.data.itemAt(index);
10932     },
10933
10934     /**
10935      * Returns a range of Records between specified indices.
10936      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10937      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10938      * @return {Roo.data.Record[]} An array of Records
10939      */
10940     getRange : function(start, end){
10941         return this.data.getRange(start, end);
10942     },
10943
10944     // private
10945     storeOptions : function(o){
10946         o = Roo.apply({}, o);
10947         delete o.callback;
10948         delete o.scope;
10949         this.lastOptions = o;
10950     },
10951
10952     /**
10953      * Loads the Record cache from the configured Proxy using the configured Reader.
10954      * <p>
10955      * If using remote paging, then the first load call must specify the <em>start</em>
10956      * and <em>limit</em> properties in the options.params property to establish the initial
10957      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10958      * <p>
10959      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10960      * and this call will return before the new data has been loaded. Perform any post-processing
10961      * in a callback function, or in a "load" event handler.</strong>
10962      * <p>
10963      * @param {Object} options An object containing properties which control loading options:<ul>
10964      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10965      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10966      * passed the following arguments:<ul>
10967      * <li>r : Roo.data.Record[]</li>
10968      * <li>options: Options object from the load call</li>
10969      * <li>success: Boolean success indicator</li></ul></li>
10970      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10971      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10972      * </ul>
10973      */
10974     load : function(options){
10975         options = options || {};
10976         if(this.fireEvent("beforeload", this, options) !== false){
10977             this.storeOptions(options);
10978             var p = Roo.apply(options.params || {}, this.baseParams);
10979             // if meta was not loaded from remote source.. try requesting it.
10980             if (!this.reader.metaFromRemote) {
10981                 p._requestMeta = 1;
10982             }
10983             if(this.sortInfo && this.remoteSort){
10984                 var pn = this.paramNames;
10985                 p[pn["sort"]] = this.sortInfo.field;
10986                 p[pn["dir"]] = this.sortInfo.direction;
10987             }
10988             if (this.multiSort) {
10989                 var pn = this.paramNames;
10990                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10991             }
10992             
10993             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10994         }
10995     },
10996
10997     /**
10998      * Reloads the Record cache from the configured Proxy using the configured Reader and
10999      * the options from the last load operation performed.
11000      * @param {Object} options (optional) An object containing properties which may override the options
11001      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11002      * the most recently used options are reused).
11003      */
11004     reload : function(options){
11005         this.load(Roo.applyIf(options||{}, this.lastOptions));
11006     },
11007
11008     // private
11009     // Called as a callback by the Reader during a load operation.
11010     loadRecords : function(o, options, success){
11011         if(!o || success === false){
11012             if(success !== false){
11013                 this.fireEvent("load", this, [], options, o);
11014             }
11015             if(options.callback){
11016                 options.callback.call(options.scope || this, [], options, false);
11017             }
11018             return;
11019         }
11020         // if data returned failure - throw an exception.
11021         if (o.success === false) {
11022             // show a message if no listener is registered.
11023             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11024                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11025             }
11026             // loadmask wil be hooked into this..
11027             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11028             return;
11029         }
11030         var r = o.records, t = o.totalRecords || r.length;
11031         
11032         this.fireEvent("beforeloadadd", this, r, options, o);
11033         
11034         if(!options || options.add !== true){
11035             if(this.pruneModifiedRecords){
11036                 this.modified = [];
11037             }
11038             for(var i = 0, len = r.length; i < len; i++){
11039                 r[i].join(this);
11040             }
11041             if(this.snapshot){
11042                 this.data = this.snapshot;
11043                 delete this.snapshot;
11044             }
11045             this.data.clear();
11046             this.data.addAll(r);
11047             this.totalLength = t;
11048             this.applySort();
11049             this.fireEvent("datachanged", this);
11050         }else{
11051             this.totalLength = Math.max(t, this.data.length+r.length);
11052             this.add(r);
11053         }
11054         
11055         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11056                 
11057             var e = new Roo.data.Record({});
11058
11059             e.set(this.parent.displayField, this.parent.emptyTitle);
11060             e.set(this.parent.valueField, '');
11061
11062             this.insert(0, e);
11063         }
11064             
11065         this.fireEvent("load", this, r, options, o);
11066         if(options.callback){
11067             options.callback.call(options.scope || this, r, options, true);
11068         }
11069     },
11070
11071
11072     /**
11073      * Loads data from a passed data block. A Reader which understands the format of the data
11074      * must have been configured in the constructor.
11075      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11076      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11077      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11078      */
11079     loadData : function(o, append){
11080         var r = this.reader.readRecords(o);
11081         this.loadRecords(r, {add: append}, true);
11082     },
11083
11084     /**
11085      * Gets the number of cached records.
11086      * <p>
11087      * <em>If using paging, this may not be the total size of the dataset. If the data object
11088      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11089      * the data set size</em>
11090      */
11091     getCount : function(){
11092         return this.data.length || 0;
11093     },
11094
11095     /**
11096      * Gets the total number of records in the dataset as returned by the server.
11097      * <p>
11098      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11099      * the dataset size</em>
11100      */
11101     getTotalCount : function(){
11102         return this.totalLength || 0;
11103     },
11104
11105     /**
11106      * Returns the sort state of the Store as an object with two properties:
11107      * <pre><code>
11108  field {String} The name of the field by which the Records are sorted
11109  direction {String} The sort order, "ASC" or "DESC"
11110      * </code></pre>
11111      */
11112     getSortState : function(){
11113         return this.sortInfo;
11114     },
11115
11116     // private
11117     applySort : function(){
11118         if(this.sortInfo && !this.remoteSort){
11119             var s = this.sortInfo, f = s.field;
11120             var st = this.fields.get(f).sortType;
11121             var fn = function(r1, r2){
11122                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11123                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11124             };
11125             this.data.sort(s.direction, fn);
11126             if(this.snapshot && this.snapshot != this.data){
11127                 this.snapshot.sort(s.direction, fn);
11128             }
11129         }
11130     },
11131
11132     /**
11133      * Sets the default sort column and order to be used by the next load operation.
11134      * @param {String} fieldName The name of the field to sort by.
11135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11136      */
11137     setDefaultSort : function(field, dir){
11138         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11139     },
11140
11141     /**
11142      * Sort the Records.
11143      * If remote sorting is used, the sort is performed on the server, and the cache is
11144      * reloaded. If local sorting is used, the cache is sorted internally.
11145      * @param {String} fieldName The name of the field to sort by.
11146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11147      */
11148     sort : function(fieldName, dir){
11149         var f = this.fields.get(fieldName);
11150         if(!dir){
11151             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11152             
11153             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11154                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11155             }else{
11156                 dir = f.sortDir;
11157             }
11158         }
11159         this.sortToggle[f.name] = dir;
11160         this.sortInfo = {field: f.name, direction: dir};
11161         if(!this.remoteSort){
11162             this.applySort();
11163             this.fireEvent("datachanged", this);
11164         }else{
11165             this.load(this.lastOptions);
11166         }
11167     },
11168
11169     /**
11170      * Calls the specified function for each of the Records in the cache.
11171      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11172      * Returning <em>false</em> aborts and exits the iteration.
11173      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11174      */
11175     each : function(fn, scope){
11176         this.data.each(fn, scope);
11177     },
11178
11179     /**
11180      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11181      * (e.g., during paging).
11182      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11183      */
11184     getModifiedRecords : function(){
11185         return this.modified;
11186     },
11187
11188     // private
11189     createFilterFn : function(property, value, anyMatch){
11190         if(!value.exec){ // not a regex
11191             value = String(value);
11192             if(value.length == 0){
11193                 return false;
11194             }
11195             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11196         }
11197         return function(r){
11198             return value.test(r.data[property]);
11199         };
11200     },
11201
11202     /**
11203      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11204      * @param {String} property A field on your records
11205      * @param {Number} start The record index to start at (defaults to 0)
11206      * @param {Number} end The last record index to include (defaults to length - 1)
11207      * @return {Number} The sum
11208      */
11209     sum : function(property, start, end){
11210         var rs = this.data.items, v = 0;
11211         start = start || 0;
11212         end = (end || end === 0) ? end : rs.length-1;
11213
11214         for(var i = start; i <= end; i++){
11215             v += (rs[i].data[property] || 0);
11216         }
11217         return v;
11218     },
11219
11220     /**
11221      * Filter the records by a specified property.
11222      * @param {String} field A field on your records
11223      * @param {String/RegExp} value Either a string that the field
11224      * should start with or a RegExp to test against the field
11225      * @param {Boolean} anyMatch True to match any part not just the beginning
11226      */
11227     filter : function(property, value, anyMatch){
11228         var fn = this.createFilterFn(property, value, anyMatch);
11229         return fn ? this.filterBy(fn) : this.clearFilter();
11230     },
11231
11232     /**
11233      * Filter by a function. The specified function will be called with each
11234      * record in this data source. If the function returns true the record is included,
11235      * otherwise it is filtered.
11236      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11237      * @param {Object} scope (optional) The scope of the function (defaults to this)
11238      */
11239     filterBy : function(fn, scope){
11240         this.snapshot = this.snapshot || this.data;
11241         this.data = this.queryBy(fn, scope||this);
11242         this.fireEvent("datachanged", this);
11243     },
11244
11245     /**
11246      * Query the records by a specified property.
11247      * @param {String} field A field on your records
11248      * @param {String/RegExp} value Either a string that the field
11249      * should start with or a RegExp to test against the field
11250      * @param {Boolean} anyMatch True to match any part not just the beginning
11251      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11252      */
11253     query : function(property, value, anyMatch){
11254         var fn = this.createFilterFn(property, value, anyMatch);
11255         return fn ? this.queryBy(fn) : this.data.clone();
11256     },
11257
11258     /**
11259      * Query by a function. The specified function will be called with each
11260      * record in this data source. If the function returns true the record is included
11261      * in the results.
11262      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11263      * @param {Object} scope (optional) The scope of the function (defaults to this)
11264       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11265      **/
11266     queryBy : function(fn, scope){
11267         var data = this.snapshot || this.data;
11268         return data.filterBy(fn, scope||this);
11269     },
11270
11271     /**
11272      * Collects unique values for a particular dataIndex from this store.
11273      * @param {String} dataIndex The property to collect
11274      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11275      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11276      * @return {Array} An array of the unique values
11277      **/
11278     collect : function(dataIndex, allowNull, bypassFilter){
11279         var d = (bypassFilter === true && this.snapshot) ?
11280                 this.snapshot.items : this.data.items;
11281         var v, sv, r = [], l = {};
11282         for(var i = 0, len = d.length; i < len; i++){
11283             v = d[i].data[dataIndex];
11284             sv = String(v);
11285             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11286                 l[sv] = true;
11287                 r[r.length] = v;
11288             }
11289         }
11290         return r;
11291     },
11292
11293     /**
11294      * Revert to a view of the Record cache with no filtering applied.
11295      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11296      */
11297     clearFilter : function(suppressEvent){
11298         if(this.snapshot && this.snapshot != this.data){
11299             this.data = this.snapshot;
11300             delete this.snapshot;
11301             if(suppressEvent !== true){
11302                 this.fireEvent("datachanged", this);
11303             }
11304         }
11305     },
11306
11307     // private
11308     afterEdit : function(record){
11309         if(this.modified.indexOf(record) == -1){
11310             this.modified.push(record);
11311         }
11312         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11313     },
11314     
11315     // private
11316     afterReject : function(record){
11317         this.modified.remove(record);
11318         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11319     },
11320
11321     // private
11322     afterCommit : function(record){
11323         this.modified.remove(record);
11324         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11325     },
11326
11327     /**
11328      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11329      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11330      */
11331     commitChanges : function(){
11332         var m = this.modified.slice(0);
11333         this.modified = [];
11334         for(var i = 0, len = m.length; i < len; i++){
11335             m[i].commit();
11336         }
11337     },
11338
11339     /**
11340      * Cancel outstanding changes on all changed records.
11341      */
11342     rejectChanges : function(){
11343         var m = this.modified.slice(0);
11344         this.modified = [];
11345         for(var i = 0, len = m.length; i < len; i++){
11346             m[i].reject();
11347         }
11348     },
11349
11350     onMetaChange : function(meta, rtype, o){
11351         this.recordType = rtype;
11352         this.fields = rtype.prototype.fields;
11353         delete this.snapshot;
11354         this.sortInfo = meta.sortInfo || this.sortInfo;
11355         this.modified = [];
11356         this.fireEvent('metachange', this, this.reader.meta);
11357     },
11358     
11359     moveIndex : function(data, type)
11360     {
11361         var index = this.indexOf(data);
11362         
11363         var newIndex = index + type;
11364         
11365         this.remove(data);
11366         
11367         this.insert(newIndex, data);
11368         
11369     }
11370 });/*
11371  * Based on:
11372  * Ext JS Library 1.1.1
11373  * Copyright(c) 2006-2007, Ext JS, LLC.
11374  *
11375  * Originally Released Under LGPL - original licence link has changed is not relivant.
11376  *
11377  * Fork - LGPL
11378  * <script type="text/javascript">
11379  */
11380
11381 /**
11382  * @class Roo.data.SimpleStore
11383  * @extends Roo.data.Store
11384  * Small helper class to make creating Stores from Array data easier.
11385  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11386  * @cfg {Array} fields An array of field definition objects, or field name strings.
11387  * @cfg {Array} data The multi-dimensional array of data
11388  * @constructor
11389  * @param {Object} config
11390  */
11391 Roo.data.SimpleStore = function(config){
11392     Roo.data.SimpleStore.superclass.constructor.call(this, {
11393         isLocal : true,
11394         reader: new Roo.data.ArrayReader({
11395                 id: config.id
11396             },
11397             Roo.data.Record.create(config.fields)
11398         ),
11399         proxy : new Roo.data.MemoryProxy(config.data)
11400     });
11401     this.load();
11402 };
11403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11404  * Based on:
11405  * Ext JS Library 1.1.1
11406  * Copyright(c) 2006-2007, Ext JS, LLC.
11407  *
11408  * Originally Released Under LGPL - original licence link has changed is not relivant.
11409  *
11410  * Fork - LGPL
11411  * <script type="text/javascript">
11412  */
11413
11414 /**
11415 /**
11416  * @extends Roo.data.Store
11417  * @class Roo.data.JsonStore
11418  * Small helper class to make creating Stores for JSON data easier. <br/>
11419 <pre><code>
11420 var store = new Roo.data.JsonStore({
11421     url: 'get-images.php',
11422     root: 'images',
11423     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11424 });
11425 </code></pre>
11426  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11427  * JsonReader and HttpProxy (unless inline data is provided).</b>
11428  * @cfg {Array} fields An array of field definition objects, or field name strings.
11429  * @constructor
11430  * @param {Object} config
11431  */
11432 Roo.data.JsonStore = function(c){
11433     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11434         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11435         reader: new Roo.data.JsonReader(c, c.fields)
11436     }));
11437 };
11438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11439  * Based on:
11440  * Ext JS Library 1.1.1
11441  * Copyright(c) 2006-2007, Ext JS, LLC.
11442  *
11443  * Originally Released Under LGPL - original licence link has changed is not relivant.
11444  *
11445  * Fork - LGPL
11446  * <script type="text/javascript">
11447  */
11448
11449  
11450 Roo.data.Field = function(config){
11451     if(typeof config == "string"){
11452         config = {name: config};
11453     }
11454     Roo.apply(this, config);
11455     
11456     if(!this.type){
11457         this.type = "auto";
11458     }
11459     
11460     var st = Roo.data.SortTypes;
11461     // named sortTypes are supported, here we look them up
11462     if(typeof this.sortType == "string"){
11463         this.sortType = st[this.sortType];
11464     }
11465     
11466     // set default sortType for strings and dates
11467     if(!this.sortType){
11468         switch(this.type){
11469             case "string":
11470                 this.sortType = st.asUCString;
11471                 break;
11472             case "date":
11473                 this.sortType = st.asDate;
11474                 break;
11475             default:
11476                 this.sortType = st.none;
11477         }
11478     }
11479
11480     // define once
11481     var stripRe = /[\$,%]/g;
11482
11483     // prebuilt conversion function for this field, instead of
11484     // switching every time we're reading a value
11485     if(!this.convert){
11486         var cv, dateFormat = this.dateFormat;
11487         switch(this.type){
11488             case "":
11489             case "auto":
11490             case undefined:
11491                 cv = function(v){ return v; };
11492                 break;
11493             case "string":
11494                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11495                 break;
11496             case "int":
11497                 cv = function(v){
11498                     return v !== undefined && v !== null && v !== '' ?
11499                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11500                     };
11501                 break;
11502             case "float":
11503                 cv = function(v){
11504                     return v !== undefined && v !== null && v !== '' ?
11505                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11506                     };
11507                 break;
11508             case "bool":
11509             case "boolean":
11510                 cv = function(v){ return v === true || v === "true" || v == 1; };
11511                 break;
11512             case "date":
11513                 cv = function(v){
11514                     if(!v){
11515                         return '';
11516                     }
11517                     if(v instanceof Date){
11518                         return v;
11519                     }
11520                     if(dateFormat){
11521                         if(dateFormat == "timestamp"){
11522                             return new Date(v*1000);
11523                         }
11524                         return Date.parseDate(v, dateFormat);
11525                     }
11526                     var parsed = Date.parse(v);
11527                     return parsed ? new Date(parsed) : null;
11528                 };
11529              break;
11530             
11531         }
11532         this.convert = cv;
11533     }
11534 };
11535
11536 Roo.data.Field.prototype = {
11537     dateFormat: null,
11538     defaultValue: "",
11539     mapping: null,
11540     sortType : null,
11541     sortDir : "ASC"
11542 };/*
11543  * Based on:
11544  * Ext JS Library 1.1.1
11545  * Copyright(c) 2006-2007, Ext JS, LLC.
11546  *
11547  * Originally Released Under LGPL - original licence link has changed is not relivant.
11548  *
11549  * Fork - LGPL
11550  * <script type="text/javascript">
11551  */
11552  
11553 // Base class for reading structured data from a data source.  This class is intended to be
11554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11555
11556 /**
11557  * @class Roo.data.DataReader
11558  * Base class for reading structured data from a data source.  This class is intended to be
11559  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11560  */
11561
11562 Roo.data.DataReader = function(meta, recordType){
11563     
11564     this.meta = meta;
11565     
11566     this.recordType = recordType instanceof Array ? 
11567         Roo.data.Record.create(recordType) : recordType;
11568 };
11569
11570 Roo.data.DataReader.prototype = {
11571      /**
11572      * Create an empty record
11573      * @param {Object} data (optional) - overlay some values
11574      * @return {Roo.data.Record} record created.
11575      */
11576     newRow :  function(d) {
11577         var da =  {};
11578         this.recordType.prototype.fields.each(function(c) {
11579             switch( c.type) {
11580                 case 'int' : da[c.name] = 0; break;
11581                 case 'date' : da[c.name] = new Date(); break;
11582                 case 'float' : da[c.name] = 0.0; break;
11583                 case 'boolean' : da[c.name] = false; break;
11584                 default : da[c.name] = ""; break;
11585             }
11586             
11587         });
11588         return new this.recordType(Roo.apply(da, d));
11589     }
11590     
11591 };/*
11592  * Based on:
11593  * Ext JS Library 1.1.1
11594  * Copyright(c) 2006-2007, Ext JS, LLC.
11595  *
11596  * Originally Released Under LGPL - original licence link has changed is not relivant.
11597  *
11598  * Fork - LGPL
11599  * <script type="text/javascript">
11600  */
11601
11602 /**
11603  * @class Roo.data.DataProxy
11604  * @extends Roo.data.Observable
11605  * This class is an abstract base class for implementations which provide retrieval of
11606  * unformatted data objects.<br>
11607  * <p>
11608  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11609  * (of the appropriate type which knows how to parse the data object) to provide a block of
11610  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11611  * <p>
11612  * Custom implementations must implement the load method as described in
11613  * {@link Roo.data.HttpProxy#load}.
11614  */
11615 Roo.data.DataProxy = function(){
11616     this.addEvents({
11617         /**
11618          * @event beforeload
11619          * Fires before a network request is made to retrieve a data object.
11620          * @param {Object} This DataProxy object.
11621          * @param {Object} params The params parameter to the load function.
11622          */
11623         beforeload : true,
11624         /**
11625          * @event load
11626          * Fires before the load method's callback is called.
11627          * @param {Object} This DataProxy object.
11628          * @param {Object} o The data object.
11629          * @param {Object} arg The callback argument object passed to the load function.
11630          */
11631         load : true,
11632         /**
11633          * @event loadexception
11634          * Fires if an Exception occurs during data retrieval.
11635          * @param {Object} This DataProxy object.
11636          * @param {Object} o The data object.
11637          * @param {Object} arg The callback argument object passed to the load function.
11638          * @param {Object} e The Exception.
11639          */
11640         loadexception : true
11641     });
11642     Roo.data.DataProxy.superclass.constructor.call(this);
11643 };
11644
11645 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11646
11647     /**
11648      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11649      */
11650 /*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660 /**
11661  * @class Roo.data.MemoryProxy
11662  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11663  * to the Reader when its load method is called.
11664  * @constructor
11665  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11666  */
11667 Roo.data.MemoryProxy = function(data){
11668     if (data.data) {
11669         data = data.data;
11670     }
11671     Roo.data.MemoryProxy.superclass.constructor.call(this);
11672     this.data = data;
11673 };
11674
11675 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11676     
11677     /**
11678      * Load data from the requested source (in this case an in-memory
11679      * data object passed to the constructor), read the data object into
11680      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11681      * process that block using the passed callback.
11682      * @param {Object} params This parameter is not used by the MemoryProxy class.
11683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11684      * object into a block of Roo.data.Records.
11685      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11686      * The function must be passed <ul>
11687      * <li>The Record block object</li>
11688      * <li>The "arg" argument from the load function</li>
11689      * <li>A boolean success indicator</li>
11690      * </ul>
11691      * @param {Object} scope The scope in which to call the callback
11692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11693      */
11694     load : function(params, reader, callback, scope, arg){
11695         params = params || {};
11696         var result;
11697         try {
11698             result = reader.readRecords(this.data);
11699         }catch(e){
11700             this.fireEvent("loadexception", this, arg, null, e);
11701             callback.call(scope, null, arg, false);
11702             return;
11703         }
11704         callback.call(scope, result, arg, true);
11705     },
11706     
11707     // private
11708     update : function(params, records){
11709         
11710     }
11711 });/*
11712  * Based on:
11713  * Ext JS Library 1.1.1
11714  * Copyright(c) 2006-2007, Ext JS, LLC.
11715  *
11716  * Originally Released Under LGPL - original licence link has changed is not relivant.
11717  *
11718  * Fork - LGPL
11719  * <script type="text/javascript">
11720  */
11721 /**
11722  * @class Roo.data.HttpProxy
11723  * @extends Roo.data.DataProxy
11724  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11725  * configured to reference a certain URL.<br><br>
11726  * <p>
11727  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11728  * from which the running page was served.<br><br>
11729  * <p>
11730  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11731  * <p>
11732  * Be aware that to enable the browser to parse an XML document, the server must set
11733  * the Content-Type header in the HTTP response to "text/xml".
11734  * @constructor
11735  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11736  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11737  * will be used to make the request.
11738  */
11739 Roo.data.HttpProxy = function(conn){
11740     Roo.data.HttpProxy.superclass.constructor.call(this);
11741     // is conn a conn config or a real conn?
11742     this.conn = conn;
11743     this.useAjax = !conn || !conn.events;
11744   
11745 };
11746
11747 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11748     // thse are take from connection...
11749     
11750     /**
11751      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11752      */
11753     /**
11754      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11755      * extra parameters to each request made by this object. (defaults to undefined)
11756      */
11757     /**
11758      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11759      *  to each request made by this object. (defaults to undefined)
11760      */
11761     /**
11762      * @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)
11763      */
11764     /**
11765      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11766      */
11767      /**
11768      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11769      * @type Boolean
11770      */
11771   
11772
11773     /**
11774      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11775      * @type Boolean
11776      */
11777     /**
11778      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11779      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11780      * a finer-grained basis than the DataProxy events.
11781      */
11782     getConnection : function(){
11783         return this.useAjax ? Roo.Ajax : this.conn;
11784     },
11785
11786     /**
11787      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11788      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11789      * process that block using the passed callback.
11790      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11791      * for the request to the remote server.
11792      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11793      * object into a block of Roo.data.Records.
11794      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11795      * The function must be passed <ul>
11796      * <li>The Record block object</li>
11797      * <li>The "arg" argument from the load function</li>
11798      * <li>A boolean success indicator</li>
11799      * </ul>
11800      * @param {Object} scope The scope in which to call the callback
11801      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11802      */
11803     load : function(params, reader, callback, scope, arg){
11804         if(this.fireEvent("beforeload", this, params) !== false){
11805             var  o = {
11806                 params : params || {},
11807                 request: {
11808                     callback : callback,
11809                     scope : scope,
11810                     arg : arg
11811                 },
11812                 reader: reader,
11813                 callback : this.loadResponse,
11814                 scope: this
11815             };
11816             if(this.useAjax){
11817                 Roo.applyIf(o, this.conn);
11818                 if(this.activeRequest){
11819                     Roo.Ajax.abort(this.activeRequest);
11820                 }
11821                 this.activeRequest = Roo.Ajax.request(o);
11822             }else{
11823                 this.conn.request(o);
11824             }
11825         }else{
11826             callback.call(scope||this, null, arg, false);
11827         }
11828     },
11829
11830     // private
11831     loadResponse : function(o, success, response){
11832         delete this.activeRequest;
11833         if(!success){
11834             this.fireEvent("loadexception", this, o, response);
11835             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11836             return;
11837         }
11838         var result;
11839         try {
11840             result = o.reader.read(response);
11841         }catch(e){
11842             this.fireEvent("loadexception", this, o, response, e);
11843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11844             return;
11845         }
11846         
11847         this.fireEvent("load", this, o, o.request.arg);
11848         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11849     },
11850
11851     // private
11852     update : function(dataSet){
11853
11854     },
11855
11856     // private
11857     updateResponse : function(dataSet){
11858
11859     }
11860 });/*
11861  * Based on:
11862  * Ext JS Library 1.1.1
11863  * Copyright(c) 2006-2007, Ext JS, LLC.
11864  *
11865  * Originally Released Under LGPL - original licence link has changed is not relivant.
11866  *
11867  * Fork - LGPL
11868  * <script type="text/javascript">
11869  */
11870
11871 /**
11872  * @class Roo.data.ScriptTagProxy
11873  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11874  * other than the originating domain of the running page.<br><br>
11875  * <p>
11876  * <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
11877  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11878  * <p>
11879  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11880  * source code that is used as the source inside a &lt;script> tag.<br><br>
11881  * <p>
11882  * In order for the browser to process the returned data, the server must wrap the data object
11883  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11884  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11885  * depending on whether the callback name was passed:
11886  * <p>
11887  * <pre><code>
11888 boolean scriptTag = false;
11889 String cb = request.getParameter("callback");
11890 if (cb != null) {
11891     scriptTag = true;
11892     response.setContentType("text/javascript");
11893 } else {
11894     response.setContentType("application/x-json");
11895 }
11896 Writer out = response.getWriter();
11897 if (scriptTag) {
11898     out.write(cb + "(");
11899 }
11900 out.print(dataBlock.toJsonString());
11901 if (scriptTag) {
11902     out.write(");");
11903 }
11904 </pre></code>
11905  *
11906  * @constructor
11907  * @param {Object} config A configuration object.
11908  */
11909 Roo.data.ScriptTagProxy = function(config){
11910     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11911     Roo.apply(this, config);
11912     this.head = document.getElementsByTagName("head")[0];
11913 };
11914
11915 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11916
11917 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11918     /**
11919      * @cfg {String} url The URL from which to request the data object.
11920      */
11921     /**
11922      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11923      */
11924     timeout : 30000,
11925     /**
11926      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11927      * the server the name of the callback function set up by the load call to process the returned data object.
11928      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11929      * javascript output which calls this named function passing the data object as its only parameter.
11930      */
11931     callbackParam : "callback",
11932     /**
11933      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11934      * name to the request.
11935      */
11936     nocache : true,
11937
11938     /**
11939      * Load data from the configured URL, read the data object into
11940      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11941      * process that block using the passed callback.
11942      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11943      * for the request to the remote server.
11944      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11945      * object into a block of Roo.data.Records.
11946      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11947      * The function must be passed <ul>
11948      * <li>The Record block object</li>
11949      * <li>The "arg" argument from the load function</li>
11950      * <li>A boolean success indicator</li>
11951      * </ul>
11952      * @param {Object} scope The scope in which to call the callback
11953      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11954      */
11955     load : function(params, reader, callback, scope, arg){
11956         if(this.fireEvent("beforeload", this, params) !== false){
11957
11958             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11959
11960             var url = this.url;
11961             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11962             if(this.nocache){
11963                 url += "&_dc=" + (new Date().getTime());
11964             }
11965             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11966             var trans = {
11967                 id : transId,
11968                 cb : "stcCallback"+transId,
11969                 scriptId : "stcScript"+transId,
11970                 params : params,
11971                 arg : arg,
11972                 url : url,
11973                 callback : callback,
11974                 scope : scope,
11975                 reader : reader
11976             };
11977             var conn = this;
11978
11979             window[trans.cb] = function(o){
11980                 conn.handleResponse(o, trans);
11981             };
11982
11983             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11984
11985             if(this.autoAbort !== false){
11986                 this.abort();
11987             }
11988
11989             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11990
11991             var script = document.createElement("script");
11992             script.setAttribute("src", url);
11993             script.setAttribute("type", "text/javascript");
11994             script.setAttribute("id", trans.scriptId);
11995             this.head.appendChild(script);
11996
11997             this.trans = trans;
11998         }else{
11999             callback.call(scope||this, null, arg, false);
12000         }
12001     },
12002
12003     // private
12004     isLoading : function(){
12005         return this.trans ? true : false;
12006     },
12007
12008     /**
12009      * Abort the current server request.
12010      */
12011     abort : function(){
12012         if(this.isLoading()){
12013             this.destroyTrans(this.trans);
12014         }
12015     },
12016
12017     // private
12018     destroyTrans : function(trans, isLoaded){
12019         this.head.removeChild(document.getElementById(trans.scriptId));
12020         clearTimeout(trans.timeoutId);
12021         if(isLoaded){
12022             window[trans.cb] = undefined;
12023             try{
12024                 delete window[trans.cb];
12025             }catch(e){}
12026         }else{
12027             // if hasn't been loaded, wait for load to remove it to prevent script error
12028             window[trans.cb] = function(){
12029                 window[trans.cb] = undefined;
12030                 try{
12031                     delete window[trans.cb];
12032                 }catch(e){}
12033             };
12034         }
12035     },
12036
12037     // private
12038     handleResponse : function(o, trans){
12039         this.trans = false;
12040         this.destroyTrans(trans, true);
12041         var result;
12042         try {
12043             result = trans.reader.readRecords(o);
12044         }catch(e){
12045             this.fireEvent("loadexception", this, o, trans.arg, e);
12046             trans.callback.call(trans.scope||window, null, trans.arg, false);
12047             return;
12048         }
12049         this.fireEvent("load", this, o, trans.arg);
12050         trans.callback.call(trans.scope||window, result, trans.arg, true);
12051     },
12052
12053     // private
12054     handleFailure : function(trans){
12055         this.trans = false;
12056         this.destroyTrans(trans, false);
12057         this.fireEvent("loadexception", this, null, trans.arg);
12058         trans.callback.call(trans.scope||window, null, trans.arg, false);
12059     }
12060 });/*
12061  * Based on:
12062  * Ext JS Library 1.1.1
12063  * Copyright(c) 2006-2007, Ext JS, LLC.
12064  *
12065  * Originally Released Under LGPL - original licence link has changed is not relivant.
12066  *
12067  * Fork - LGPL
12068  * <script type="text/javascript">
12069  */
12070
12071 /**
12072  * @class Roo.data.JsonReader
12073  * @extends Roo.data.DataReader
12074  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12075  * based on mappings in a provided Roo.data.Record constructor.
12076  * 
12077  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12078  * in the reply previously. 
12079  * 
12080  * <p>
12081  * Example code:
12082  * <pre><code>
12083 var RecordDef = Roo.data.Record.create([
12084     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12085     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12086 ]);
12087 var myReader = new Roo.data.JsonReader({
12088     totalProperty: "results",    // The property which contains the total dataset size (optional)
12089     root: "rows",                // The property which contains an Array of row objects
12090     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12091 }, RecordDef);
12092 </code></pre>
12093  * <p>
12094  * This would consume a JSON file like this:
12095  * <pre><code>
12096 { 'results': 2, 'rows': [
12097     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12098     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12099 }
12100 </code></pre>
12101  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12102  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12103  * paged from the remote server.
12104  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12105  * @cfg {String} root name of the property which contains the Array of row objects.
12106  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12107  * @cfg {Array} fields Array of field definition objects
12108  * @constructor
12109  * Create a new JsonReader
12110  * @param {Object} meta Metadata configuration options
12111  * @param {Object} recordType Either an Array of field definition objects,
12112  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12113  */
12114 Roo.data.JsonReader = function(meta, recordType){
12115     
12116     meta = meta || {};
12117     // set some defaults:
12118     Roo.applyIf(meta, {
12119         totalProperty: 'total',
12120         successProperty : 'success',
12121         root : 'data',
12122         id : 'id'
12123     });
12124     
12125     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12126 };
12127 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12128     
12129     /**
12130      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12131      * Used by Store query builder to append _requestMeta to params.
12132      * 
12133      */
12134     metaFromRemote : false,
12135     /**
12136      * This method is only used by a DataProxy which has retrieved data from a remote server.
12137      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12138      * @return {Object} data A data block which is used by an Roo.data.Store object as
12139      * a cache of Roo.data.Records.
12140      */
12141     read : function(response){
12142         var json = response.responseText;
12143        
12144         var o = /* eval:var:o */ eval("("+json+")");
12145         if(!o) {
12146             throw {message: "JsonReader.read: Json object not found"};
12147         }
12148         
12149         if(o.metaData){
12150             
12151             delete this.ef;
12152             this.metaFromRemote = true;
12153             this.meta = o.metaData;
12154             this.recordType = Roo.data.Record.create(o.metaData.fields);
12155             this.onMetaChange(this.meta, this.recordType, o);
12156         }
12157         return this.readRecords(o);
12158     },
12159
12160     // private function a store will implement
12161     onMetaChange : function(meta, recordType, o){
12162
12163     },
12164
12165     /**
12166          * @ignore
12167          */
12168     simpleAccess: function(obj, subsc) {
12169         return obj[subsc];
12170     },
12171
12172         /**
12173          * @ignore
12174          */
12175     getJsonAccessor: function(){
12176         var re = /[\[\.]/;
12177         return function(expr) {
12178             try {
12179                 return(re.test(expr))
12180                     ? new Function("obj", "return obj." + expr)
12181                     : function(obj){
12182                         return obj[expr];
12183                     };
12184             } catch(e){}
12185             return Roo.emptyFn;
12186         };
12187     }(),
12188
12189     /**
12190      * Create a data block containing Roo.data.Records from an XML document.
12191      * @param {Object} o An object which contains an Array of row objects in the property specified
12192      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12193      * which contains the total size of the dataset.
12194      * @return {Object} data A data block which is used by an Roo.data.Store object as
12195      * a cache of Roo.data.Records.
12196      */
12197     readRecords : function(o){
12198         /**
12199          * After any data loads, the raw JSON data is available for further custom processing.
12200          * @type Object
12201          */
12202         this.o = o;
12203         var s = this.meta, Record = this.recordType,
12204             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12205
12206 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12207         if (!this.ef) {
12208             if(s.totalProperty) {
12209                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12210                 }
12211                 if(s.successProperty) {
12212                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12213                 }
12214                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12215                 if (s.id) {
12216                         var g = this.getJsonAccessor(s.id);
12217                         this.getId = function(rec) {
12218                                 var r = g(rec);  
12219                                 return (r === undefined || r === "") ? null : r;
12220                         };
12221                 } else {
12222                         this.getId = function(){return null;};
12223                 }
12224             this.ef = [];
12225             for(var jj = 0; jj < fl; jj++){
12226                 f = fi[jj];
12227                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12228                 this.ef[jj] = this.getJsonAccessor(map);
12229             }
12230         }
12231
12232         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12233         if(s.totalProperty){
12234             var vt = parseInt(this.getTotal(o), 10);
12235             if(!isNaN(vt)){
12236                 totalRecords = vt;
12237             }
12238         }
12239         if(s.successProperty){
12240             var vs = this.getSuccess(o);
12241             if(vs === false || vs === 'false'){
12242                 success = false;
12243             }
12244         }
12245         var records = [];
12246         for(var i = 0; i < c; i++){
12247                 var n = root[i];
12248             var values = {};
12249             var id = this.getId(n);
12250             for(var j = 0; j < fl; j++){
12251                 f = fi[j];
12252             var v = this.ef[j](n);
12253             if (!f.convert) {
12254                 Roo.log('missing convert for ' + f.name);
12255                 Roo.log(f);
12256                 continue;
12257             }
12258             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12259             }
12260             var record = new Record(values, id);
12261             record.json = n;
12262             records[i] = record;
12263         }
12264         return {
12265             raw : o,
12266             success : success,
12267             records : records,
12268             totalRecords : totalRecords
12269         };
12270     }
12271 });/*
12272  * Based on:
12273  * Ext JS Library 1.1.1
12274  * Copyright(c) 2006-2007, Ext JS, LLC.
12275  *
12276  * Originally Released Under LGPL - original licence link has changed is not relivant.
12277  *
12278  * Fork - LGPL
12279  * <script type="text/javascript">
12280  */
12281
12282 /**
12283  * @class Roo.data.ArrayReader
12284  * @extends Roo.data.DataReader
12285  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12286  * Each element of that Array represents a row of data fields. The
12287  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12288  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12289  * <p>
12290  * Example code:.
12291  * <pre><code>
12292 var RecordDef = Roo.data.Record.create([
12293     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12294     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12295 ]);
12296 var myReader = new Roo.data.ArrayReader({
12297     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12298 }, RecordDef);
12299 </code></pre>
12300  * <p>
12301  * This would consume an Array like this:
12302  * <pre><code>
12303 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12304   </code></pre>
12305  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12306  * @constructor
12307  * Create a new JsonReader
12308  * @param {Object} meta Metadata configuration options.
12309  * @param {Object} recordType Either an Array of field definition objects
12310  * as specified to {@link Roo.data.Record#create},
12311  * or an {@link Roo.data.Record} object
12312  * created using {@link Roo.data.Record#create}.
12313  */
12314 Roo.data.ArrayReader = function(meta, recordType){
12315     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12316 };
12317
12318 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12319     /**
12320      * Create a data block containing Roo.data.Records from an XML document.
12321      * @param {Object} o An Array of row objects which represents the dataset.
12322      * @return {Object} data A data block which is used by an Roo.data.Store object as
12323      * a cache of Roo.data.Records.
12324      */
12325     readRecords : function(o){
12326         var sid = this.meta ? this.meta.id : null;
12327         var recordType = this.recordType, fields = recordType.prototype.fields;
12328         var records = [];
12329         var root = o;
12330             for(var i = 0; i < root.length; i++){
12331                     var n = root[i];
12332                 var values = {};
12333                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12334                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12335                 var f = fields.items[j];
12336                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12337                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12338                 v = f.convert(v);
12339                 values[f.name] = v;
12340             }
12341                 var record = new recordType(values, id);
12342                 record.json = n;
12343                 records[records.length] = record;
12344             }
12345             return {
12346                 records : records,
12347                 totalRecords : records.length
12348             };
12349     }
12350 });/*
12351  * - LGPL
12352  * * 
12353  */
12354
12355 /**
12356  * @class Roo.bootstrap.ComboBox
12357  * @extends Roo.bootstrap.TriggerField
12358  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12359  * @cfg {Boolean} append (true|false) default false
12360  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12361  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12362  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12363  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12364  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12365  * @cfg {Boolean} animate default true
12366  * @cfg {Boolean} emptyResultText only for touch device
12367  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12368  * @cfg {String} emptyTitle default ''
12369  * @constructor
12370  * Create a new ComboBox.
12371  * @param {Object} config Configuration options
12372  */
12373 Roo.bootstrap.ComboBox = function(config){
12374     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12375     this.addEvents({
12376         /**
12377          * @event expand
12378          * Fires when the dropdown list is expanded
12379              * @param {Roo.bootstrap.ComboBox} combo This combo box
12380              */
12381         'expand' : true,
12382         /**
12383          * @event collapse
12384          * Fires when the dropdown list is collapsed
12385              * @param {Roo.bootstrap.ComboBox} combo This combo box
12386              */
12387         'collapse' : true,
12388         /**
12389          * @event beforeselect
12390          * Fires before a list item is selected. Return false to cancel the selection.
12391              * @param {Roo.bootstrap.ComboBox} combo This combo box
12392              * @param {Roo.data.Record} record The data record returned from the underlying store
12393              * @param {Number} index The index of the selected item in the dropdown list
12394              */
12395         'beforeselect' : true,
12396         /**
12397          * @event select
12398          * Fires when a list item is selected
12399              * @param {Roo.bootstrap.ComboBox} combo This combo box
12400              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12401              * @param {Number} index The index of the selected item in the dropdown list
12402              */
12403         'select' : true,
12404         /**
12405          * @event beforequery
12406          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12407          * The event object passed has these properties:
12408              * @param {Roo.bootstrap.ComboBox} combo This combo box
12409              * @param {String} query The query
12410              * @param {Boolean} forceAll true to force "all" query
12411              * @param {Boolean} cancel true to cancel the query
12412              * @param {Object} e The query event object
12413              */
12414         'beforequery': true,
12415          /**
12416          * @event add
12417          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12418              * @param {Roo.bootstrap.ComboBox} combo This combo box
12419              */
12420         'add' : true,
12421         /**
12422          * @event edit
12423          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12424              * @param {Roo.bootstrap.ComboBox} combo This combo box
12425              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12426              */
12427         'edit' : true,
12428         /**
12429          * @event remove
12430          * Fires when the remove value from the combobox array
12431              * @param {Roo.bootstrap.ComboBox} combo This combo box
12432              */
12433         'remove' : true,
12434         /**
12435          * @event afterremove
12436          * Fires when the remove value from the combobox array
12437              * @param {Roo.bootstrap.ComboBox} combo This combo box
12438              */
12439         'afterremove' : true,
12440         /**
12441          * @event specialfilter
12442          * Fires when specialfilter
12443             * @param {Roo.bootstrap.ComboBox} combo This combo box
12444             */
12445         'specialfilter' : true,
12446         /**
12447          * @event tick
12448          * Fires when tick the element
12449             * @param {Roo.bootstrap.ComboBox} combo This combo box
12450             */
12451         'tick' : true,
12452         /**
12453          * @event touchviewdisplay
12454          * Fires when touch view require special display (default is using displayField)
12455             * @param {Roo.bootstrap.ComboBox} combo This combo box
12456             * @param {Object} cfg set html .
12457             */
12458         'touchviewdisplay' : true
12459         
12460     });
12461     
12462     this.item = [];
12463     this.tickItems = [];
12464     
12465     this.selectedIndex = -1;
12466     if(this.mode == 'local'){
12467         if(config.queryDelay === undefined){
12468             this.queryDelay = 10;
12469         }
12470         if(config.minChars === undefined){
12471             this.minChars = 0;
12472         }
12473     }
12474 };
12475
12476 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12477      
12478     /**
12479      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12480      * rendering into an Roo.Editor, defaults to false)
12481      */
12482     /**
12483      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12484      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12485      */
12486     /**
12487      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12488      */
12489     /**
12490      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12491      * the dropdown list (defaults to undefined, with no header element)
12492      */
12493
12494      /**
12495      * @cfg {String/Roo.Template} tpl The template to use to render the output
12496      */
12497      
12498      /**
12499      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12500      */
12501     listWidth: undefined,
12502     /**
12503      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12504      * mode = 'remote' or 'text' if mode = 'local')
12505      */
12506     displayField: undefined,
12507     
12508     /**
12509      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12510      * mode = 'remote' or 'value' if mode = 'local'). 
12511      * Note: use of a valueField requires the user make a selection
12512      * in order for a value to be mapped.
12513      */
12514     valueField: undefined,
12515     /**
12516      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12517      */
12518     modalTitle : '',
12519     
12520     /**
12521      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12522      * field's data value (defaults to the underlying DOM element's name)
12523      */
12524     hiddenName: undefined,
12525     /**
12526      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12527      */
12528     listClass: '',
12529     /**
12530      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12531      */
12532     selectedClass: 'active',
12533     
12534     /**
12535      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12536      */
12537     shadow:'sides',
12538     /**
12539      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12540      * anchor positions (defaults to 'tl-bl')
12541      */
12542     listAlign: 'tl-bl?',
12543     /**
12544      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12545      */
12546     maxHeight: 300,
12547     /**
12548      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12549      * query specified by the allQuery config option (defaults to 'query')
12550      */
12551     triggerAction: 'query',
12552     /**
12553      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12554      * (defaults to 4, does not apply if editable = false)
12555      */
12556     minChars : 4,
12557     /**
12558      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12559      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12560      */
12561     typeAhead: false,
12562     /**
12563      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12564      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12565      */
12566     queryDelay: 500,
12567     /**
12568      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12569      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12570      */
12571     pageSize: 0,
12572     /**
12573      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12574      * when editable = true (defaults to false)
12575      */
12576     selectOnFocus:false,
12577     /**
12578      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12579      */
12580     queryParam: 'query',
12581     /**
12582      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12583      * when mode = 'remote' (defaults to 'Loading...')
12584      */
12585     loadingText: 'Loading...',
12586     /**
12587      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12588      */
12589     resizable: false,
12590     /**
12591      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12592      */
12593     handleHeight : 8,
12594     /**
12595      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12596      * traditional select (defaults to true)
12597      */
12598     editable: true,
12599     /**
12600      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12601      */
12602     allQuery: '',
12603     /**
12604      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12605      */
12606     mode: 'remote',
12607     /**
12608      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12609      * listWidth has a higher value)
12610      */
12611     minListWidth : 70,
12612     /**
12613      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12614      * allow the user to set arbitrary text into the field (defaults to false)
12615      */
12616     forceSelection:false,
12617     /**
12618      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12619      * if typeAhead = true (defaults to 250)
12620      */
12621     typeAheadDelay : 250,
12622     /**
12623      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12624      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12625      */
12626     valueNotFoundText : undefined,
12627     /**
12628      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12629      */
12630     blockFocus : false,
12631     
12632     /**
12633      * @cfg {Boolean} disableClear Disable showing of clear button.
12634      */
12635     disableClear : false,
12636     /**
12637      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12638      */
12639     alwaysQuery : false,
12640     
12641     /**
12642      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12643      */
12644     multiple : false,
12645     
12646     /**
12647      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12648      */
12649     invalidClass : "has-warning",
12650     
12651     /**
12652      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12653      */
12654     validClass : "has-success",
12655     
12656     /**
12657      * @cfg {Boolean} specialFilter (true|false) special filter default false
12658      */
12659     specialFilter : false,
12660     
12661     /**
12662      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12663      */
12664     mobileTouchView : true,
12665     
12666     /**
12667      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12668      */
12669     useNativeIOS : false,
12670     
12671     ios_options : false,
12672     
12673     //private
12674     addicon : false,
12675     editicon: false,
12676     
12677     page: 0,
12678     hasQuery: false,
12679     append: false,
12680     loadNext: false,
12681     autoFocus : true,
12682     tickable : false,
12683     btnPosition : 'right',
12684     triggerList : true,
12685     showToggleBtn : true,
12686     animate : true,
12687     emptyResultText: 'Empty',
12688     triggerText : 'Select',
12689     emptyTitle : '',
12690     
12691     // element that contains real text value.. (when hidden is used..)
12692     
12693     getAutoCreate : function()
12694     {   
12695         var cfg = false;
12696         //render
12697         /*
12698          * Render classic select for iso
12699          */
12700         
12701         if(Roo.isIOS && this.useNativeIOS){
12702             cfg = this.getAutoCreateNativeIOS();
12703             return cfg;
12704         }
12705         
12706         /*
12707          * Touch Devices
12708          */
12709         
12710         if(Roo.isTouch && this.mobileTouchView){
12711             cfg = this.getAutoCreateTouchView();
12712             return cfg;;
12713         }
12714         
12715         /*
12716          *  Normal ComboBox
12717          */
12718         if(!this.tickable){
12719             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12720             return cfg;
12721         }
12722         
12723         /*
12724          *  ComboBox with tickable selections
12725          */
12726              
12727         var align = this.labelAlign || this.parentLabelAlign();
12728         
12729         cfg = {
12730             cls : 'form-group roo-combobox-tickable' //input-group
12731         };
12732         
12733         var btn_text_select = '';
12734         var btn_text_done = '';
12735         var btn_text_cancel = '';
12736         
12737         if (this.btn_text_show) {
12738             btn_text_select = 'Select';
12739             btn_text_done = 'Done';
12740             btn_text_cancel = 'Cancel'; 
12741         }
12742         
12743         var buttons = {
12744             tag : 'div',
12745             cls : 'tickable-buttons',
12746             cn : [
12747                 {
12748                     tag : 'button',
12749                     type : 'button',
12750                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12751                     //html : this.triggerText
12752                     html: btn_text_select
12753                 },
12754                 {
12755                     tag : 'button',
12756                     type : 'button',
12757                     name : 'ok',
12758                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12759                     //html : 'Done'
12760                     html: btn_text_done
12761                 },
12762                 {
12763                     tag : 'button',
12764                     type : 'button',
12765                     name : 'cancel',
12766                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12767                     //html : 'Cancel'
12768                     html: btn_text_cancel
12769                 }
12770             ]
12771         };
12772         
12773         if(this.editable){
12774             buttons.cn.unshift({
12775                 tag: 'input',
12776                 cls: 'roo-select2-search-field-input'
12777             });
12778         }
12779         
12780         var _this = this;
12781         
12782         Roo.each(buttons.cn, function(c){
12783             if (_this.size) {
12784                 c.cls += ' btn-' + _this.size;
12785             }
12786
12787             if (_this.disabled) {
12788                 c.disabled = true;
12789             }
12790         });
12791         
12792         var box = {
12793             tag: 'div',
12794             cn: [
12795                 {
12796                     tag: 'input',
12797                     type : 'hidden',
12798                     cls: 'form-hidden-field'
12799                 },
12800                 {
12801                     tag: 'ul',
12802                     cls: 'roo-select2-choices',
12803                     cn:[
12804                         {
12805                             tag: 'li',
12806                             cls: 'roo-select2-search-field',
12807                             cn: [
12808                                 buttons
12809                             ]
12810                         }
12811                     ]
12812                 }
12813             ]
12814         };
12815         
12816         var combobox = {
12817             cls: 'roo-select2-container input-group roo-select2-container-multi',
12818             cn: [
12819                 box
12820 //                {
12821 //                    tag: 'ul',
12822 //                    cls: 'typeahead typeahead-long dropdown-menu',
12823 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12824 //                }
12825             ]
12826         };
12827         
12828         if(this.hasFeedback && !this.allowBlank){
12829             
12830             var feedback = {
12831                 tag: 'span',
12832                 cls: 'glyphicon form-control-feedback'
12833             };
12834
12835             combobox.cn.push(feedback);
12836         }
12837         
12838         
12839         if (align ==='left' && this.fieldLabel.length) {
12840             
12841             cfg.cls += ' roo-form-group-label-left';
12842             
12843             cfg.cn = [
12844                 {
12845                     tag : 'i',
12846                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12847                     tooltip : 'This field is required'
12848                 },
12849                 {
12850                     tag: 'label',
12851                     'for' :  id,
12852                     cls : 'control-label',
12853                     html : this.fieldLabel
12854
12855                 },
12856                 {
12857                     cls : "", 
12858                     cn: [
12859                         combobox
12860                     ]
12861                 }
12862
12863             ];
12864             
12865             var labelCfg = cfg.cn[1];
12866             var contentCfg = cfg.cn[2];
12867             
12868
12869             if(this.indicatorpos == 'right'){
12870                 
12871                 cfg.cn = [
12872                     {
12873                         tag: 'label',
12874                         'for' :  id,
12875                         cls : 'control-label',
12876                         cn : [
12877                             {
12878                                 tag : 'span',
12879                                 html : this.fieldLabel
12880                             },
12881                             {
12882                                 tag : 'i',
12883                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12884                                 tooltip : 'This field is required'
12885                             }
12886                         ]
12887                     },
12888                     {
12889                         cls : "",
12890                         cn: [
12891                             combobox
12892                         ]
12893                     }
12894
12895                 ];
12896                 
12897                 
12898                 
12899                 labelCfg = cfg.cn[0];
12900                 contentCfg = cfg.cn[1];
12901             
12902             }
12903             
12904             if(this.labelWidth > 12){
12905                 labelCfg.style = "width: " + this.labelWidth + 'px';
12906             }
12907             
12908             if(this.labelWidth < 13 && this.labelmd == 0){
12909                 this.labelmd = this.labelWidth;
12910             }
12911             
12912             if(this.labellg > 0){
12913                 labelCfg.cls += ' col-lg-' + this.labellg;
12914                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12915             }
12916             
12917             if(this.labelmd > 0){
12918                 labelCfg.cls += ' col-md-' + this.labelmd;
12919                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12920             }
12921             
12922             if(this.labelsm > 0){
12923                 labelCfg.cls += ' col-sm-' + this.labelsm;
12924                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12925             }
12926             
12927             if(this.labelxs > 0){
12928                 labelCfg.cls += ' col-xs-' + this.labelxs;
12929                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12930             }
12931                 
12932                 
12933         } else if ( this.fieldLabel.length) {
12934 //                Roo.log(" label");
12935                  cfg.cn = [
12936                     {
12937                         tag : 'i',
12938                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12939                         tooltip : 'This field is required'
12940                     },
12941                     {
12942                         tag: 'label',
12943                         //cls : 'input-group-addon',
12944                         html : this.fieldLabel
12945                     },
12946                     combobox
12947                 ];
12948                 
12949                 if(this.indicatorpos == 'right'){
12950                     cfg.cn = [
12951                         {
12952                             tag: 'label',
12953                             //cls : 'input-group-addon',
12954                             html : this.fieldLabel
12955                         },
12956                         {
12957                             tag : 'i',
12958                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12959                             tooltip : 'This field is required'
12960                         },
12961                         combobox
12962                     ];
12963                     
12964                 }
12965
12966         } else {
12967             
12968 //                Roo.log(" no label && no align");
12969                 cfg = combobox
12970                      
12971                 
12972         }
12973          
12974         var settings=this;
12975         ['xs','sm','md','lg'].map(function(size){
12976             if (settings[size]) {
12977                 cfg.cls += ' col-' + size + '-' + settings[size];
12978             }
12979         });
12980         
12981         return cfg;
12982         
12983     },
12984     
12985     _initEventsCalled : false,
12986     
12987     // private
12988     initEvents: function()
12989     {   
12990         if (this._initEventsCalled) { // as we call render... prevent looping...
12991             return;
12992         }
12993         this._initEventsCalled = true;
12994         
12995         if (!this.store) {
12996             throw "can not find store for combo";
12997         }
12998         
12999         this.store = Roo.factory(this.store, Roo.data);
13000         this.store.parent = this;
13001         
13002         // if we are building from html. then this element is so complex, that we can not really
13003         // use the rendered HTML.
13004         // so we have to trash and replace the previous code.
13005         if (Roo.XComponent.build_from_html) {
13006             // remove this element....
13007             var e = this.el.dom, k=0;
13008             while (e ) { e = e.previousSibling;  ++k;}
13009
13010             this.el.remove();
13011             
13012             this.el=false;
13013             this.rendered = false;
13014             
13015             this.render(this.parent().getChildContainer(true), k);
13016         }
13017         
13018         if(Roo.isIOS && this.useNativeIOS){
13019             this.initIOSView();
13020             return;
13021         }
13022         
13023         /*
13024          * Touch Devices
13025          */
13026         
13027         if(Roo.isTouch && this.mobileTouchView){
13028             this.initTouchView();
13029             return;
13030         }
13031         
13032         if(this.tickable){
13033             this.initTickableEvents();
13034             return;
13035         }
13036         
13037         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13038         
13039         if(this.hiddenName){
13040             
13041             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13042             
13043             this.hiddenField.dom.value =
13044                 this.hiddenValue !== undefined ? this.hiddenValue :
13045                 this.value !== undefined ? this.value : '';
13046
13047             // prevent input submission
13048             this.el.dom.removeAttribute('name');
13049             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13050              
13051              
13052         }
13053         //if(Roo.isGecko){
13054         //    this.el.dom.setAttribute('autocomplete', 'off');
13055         //}
13056         
13057         var cls = 'x-combo-list';
13058         
13059         //this.list = new Roo.Layer({
13060         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13061         //});
13062         
13063         var _this = this;
13064         
13065         (function(){
13066             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13067             _this.list.setWidth(lw);
13068         }).defer(100);
13069         
13070         this.list.on('mouseover', this.onViewOver, this);
13071         this.list.on('mousemove', this.onViewMove, this);
13072         this.list.on('scroll', this.onViewScroll, this);
13073         
13074         /*
13075         this.list.swallowEvent('mousewheel');
13076         this.assetHeight = 0;
13077
13078         if(this.title){
13079             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13080             this.assetHeight += this.header.getHeight();
13081         }
13082
13083         this.innerList = this.list.createChild({cls:cls+'-inner'});
13084         this.innerList.on('mouseover', this.onViewOver, this);
13085         this.innerList.on('mousemove', this.onViewMove, this);
13086         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13087         
13088         if(this.allowBlank && !this.pageSize && !this.disableClear){
13089             this.footer = this.list.createChild({cls:cls+'-ft'});
13090             this.pageTb = new Roo.Toolbar(this.footer);
13091            
13092         }
13093         if(this.pageSize){
13094             this.footer = this.list.createChild({cls:cls+'-ft'});
13095             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13096                     {pageSize: this.pageSize});
13097             
13098         }
13099         
13100         if (this.pageTb && this.allowBlank && !this.disableClear) {
13101             var _this = this;
13102             this.pageTb.add(new Roo.Toolbar.Fill(), {
13103                 cls: 'x-btn-icon x-btn-clear',
13104                 text: '&#160;',
13105                 handler: function()
13106                 {
13107                     _this.collapse();
13108                     _this.clearValue();
13109                     _this.onSelect(false, -1);
13110                 }
13111             });
13112         }
13113         if (this.footer) {
13114             this.assetHeight += this.footer.getHeight();
13115         }
13116         */
13117             
13118         if(!this.tpl){
13119             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13120         }
13121
13122         this.view = new Roo.View(this.list, this.tpl, {
13123             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13124         });
13125         //this.view.wrapEl.setDisplayed(false);
13126         this.view.on('click', this.onViewClick, this);
13127         
13128         
13129         this.store.on('beforeload', this.onBeforeLoad, this);
13130         this.store.on('load', this.onLoad, this);
13131         this.store.on('loadexception', this.onLoadException, this);
13132         /*
13133         if(this.resizable){
13134             this.resizer = new Roo.Resizable(this.list,  {
13135                pinned:true, handles:'se'
13136             });
13137             this.resizer.on('resize', function(r, w, h){
13138                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13139                 this.listWidth = w;
13140                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13141                 this.restrictHeight();
13142             }, this);
13143             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13144         }
13145         */
13146         if(!this.editable){
13147             this.editable = true;
13148             this.setEditable(false);
13149         }
13150         
13151         /*
13152         
13153         if (typeof(this.events.add.listeners) != 'undefined') {
13154             
13155             this.addicon = this.wrap.createChild(
13156                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13157        
13158             this.addicon.on('click', function(e) {
13159                 this.fireEvent('add', this);
13160             }, this);
13161         }
13162         if (typeof(this.events.edit.listeners) != 'undefined') {
13163             
13164             this.editicon = this.wrap.createChild(
13165                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13166             if (this.addicon) {
13167                 this.editicon.setStyle('margin-left', '40px');
13168             }
13169             this.editicon.on('click', function(e) {
13170                 
13171                 // we fire even  if inothing is selected..
13172                 this.fireEvent('edit', this, this.lastData );
13173                 
13174             }, this);
13175         }
13176         */
13177         
13178         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13179             "up" : function(e){
13180                 this.inKeyMode = true;
13181                 this.selectPrev();
13182             },
13183
13184             "down" : function(e){
13185                 if(!this.isExpanded()){
13186                     this.onTriggerClick();
13187                 }else{
13188                     this.inKeyMode = true;
13189                     this.selectNext();
13190                 }
13191             },
13192
13193             "enter" : function(e){
13194 //                this.onViewClick();
13195                 //return true;
13196                 this.collapse();
13197                 
13198                 if(this.fireEvent("specialkey", this, e)){
13199                     this.onViewClick(false);
13200                 }
13201                 
13202                 return true;
13203             },
13204
13205             "esc" : function(e){
13206                 this.collapse();
13207             },
13208
13209             "tab" : function(e){
13210                 this.collapse();
13211                 
13212                 if(this.fireEvent("specialkey", this, e)){
13213                     this.onViewClick(false);
13214                 }
13215                 
13216                 return true;
13217             },
13218
13219             scope : this,
13220
13221             doRelay : function(foo, bar, hname){
13222                 if(hname == 'down' || this.scope.isExpanded()){
13223                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13224                 }
13225                 return true;
13226             },
13227
13228             forceKeyDown: true
13229         });
13230         
13231         
13232         this.queryDelay = Math.max(this.queryDelay || 10,
13233                 this.mode == 'local' ? 10 : 250);
13234         
13235         
13236         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13237         
13238         if(this.typeAhead){
13239             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13240         }
13241         if(this.editable !== false){
13242             this.inputEl().on("keyup", this.onKeyUp, this);
13243         }
13244         if(this.forceSelection){
13245             this.inputEl().on('blur', this.doForce, this);
13246         }
13247         
13248         if(this.multiple){
13249             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13250             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13251         }
13252     },
13253     
13254     initTickableEvents: function()
13255     {   
13256         this.createList();
13257         
13258         if(this.hiddenName){
13259             
13260             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13261             
13262             this.hiddenField.dom.value =
13263                 this.hiddenValue !== undefined ? this.hiddenValue :
13264                 this.value !== undefined ? this.value : '';
13265
13266             // prevent input submission
13267             this.el.dom.removeAttribute('name');
13268             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13269              
13270              
13271         }
13272         
13273 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13274         
13275         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13276         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13277         if(this.triggerList){
13278             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13279         }
13280          
13281         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13282         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13283         
13284         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13285         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13286         
13287         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13288         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13289         
13290         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13291         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13292         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13293         
13294         this.okBtn.hide();
13295         this.cancelBtn.hide();
13296         
13297         var _this = this;
13298         
13299         (function(){
13300             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13301             _this.list.setWidth(lw);
13302         }).defer(100);
13303         
13304         this.list.on('mouseover', this.onViewOver, this);
13305         this.list.on('mousemove', this.onViewMove, this);
13306         
13307         this.list.on('scroll', this.onViewScroll, this);
13308         
13309         if(!this.tpl){
13310             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>';
13311         }
13312
13313         this.view = new Roo.View(this.list, this.tpl, {
13314             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13315         });
13316         
13317         //this.view.wrapEl.setDisplayed(false);
13318         this.view.on('click', this.onViewClick, this);
13319         
13320         
13321         
13322         this.store.on('beforeload', this.onBeforeLoad, this);
13323         this.store.on('load', this.onLoad, this);
13324         this.store.on('loadexception', this.onLoadException, this);
13325         
13326         if(this.editable){
13327             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13328                 "up" : function(e){
13329                     this.inKeyMode = true;
13330                     this.selectPrev();
13331                 },
13332
13333                 "down" : function(e){
13334                     this.inKeyMode = true;
13335                     this.selectNext();
13336                 },
13337
13338                 "enter" : function(e){
13339                     if(this.fireEvent("specialkey", this, e)){
13340                         this.onViewClick(false);
13341                     }
13342                     
13343                     return true;
13344                 },
13345
13346                 "esc" : function(e){
13347                     this.onTickableFooterButtonClick(e, false, false);
13348                 },
13349
13350                 "tab" : function(e){
13351                     this.fireEvent("specialkey", this, e);
13352                     
13353                     this.onTickableFooterButtonClick(e, false, false);
13354                     
13355                     return true;
13356                 },
13357
13358                 scope : this,
13359
13360                 doRelay : function(e, fn, key){
13361                     if(this.scope.isExpanded()){
13362                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13363                     }
13364                     return true;
13365                 },
13366
13367                 forceKeyDown: true
13368             });
13369         }
13370         
13371         this.queryDelay = Math.max(this.queryDelay || 10,
13372                 this.mode == 'local' ? 10 : 250);
13373         
13374         
13375         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13376         
13377         if(this.typeAhead){
13378             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13379         }
13380         
13381         if(this.editable !== false){
13382             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13383         }
13384         
13385         this.indicator = this.indicatorEl();
13386         
13387         if(this.indicator){
13388             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13389             this.indicator.hide();
13390         }
13391         
13392     },
13393
13394     onDestroy : function(){
13395         if(this.view){
13396             this.view.setStore(null);
13397             this.view.el.removeAllListeners();
13398             this.view.el.remove();
13399             this.view.purgeListeners();
13400         }
13401         if(this.list){
13402             this.list.dom.innerHTML  = '';
13403         }
13404         
13405         if(this.store){
13406             this.store.un('beforeload', this.onBeforeLoad, this);
13407             this.store.un('load', this.onLoad, this);
13408             this.store.un('loadexception', this.onLoadException, this);
13409         }
13410         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13411     },
13412
13413     // private
13414     fireKey : function(e){
13415         if(e.isNavKeyPress() && !this.list.isVisible()){
13416             this.fireEvent("specialkey", this, e);
13417         }
13418     },
13419
13420     // private
13421     onResize: function(w, h){
13422 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13423 //        
13424 //        if(typeof w != 'number'){
13425 //            // we do not handle it!?!?
13426 //            return;
13427 //        }
13428 //        var tw = this.trigger.getWidth();
13429 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13430 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13431 //        var x = w - tw;
13432 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13433 //            
13434 //        //this.trigger.setStyle('left', x+'px');
13435 //        
13436 //        if(this.list && this.listWidth === undefined){
13437 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13438 //            this.list.setWidth(lw);
13439 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13440 //        }
13441         
13442     
13443         
13444     },
13445
13446     /**
13447      * Allow or prevent the user from directly editing the field text.  If false is passed,
13448      * the user will only be able to select from the items defined in the dropdown list.  This method
13449      * is the runtime equivalent of setting the 'editable' config option at config time.
13450      * @param {Boolean} value True to allow the user to directly edit the field text
13451      */
13452     setEditable : function(value){
13453         if(value == this.editable){
13454             return;
13455         }
13456         this.editable = value;
13457         if(!value){
13458             this.inputEl().dom.setAttribute('readOnly', true);
13459             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13460             this.inputEl().addClass('x-combo-noedit');
13461         }else{
13462             this.inputEl().dom.setAttribute('readOnly', false);
13463             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13464             this.inputEl().removeClass('x-combo-noedit');
13465         }
13466     },
13467
13468     // private
13469     
13470     onBeforeLoad : function(combo,opts){
13471         if(!this.hasFocus){
13472             return;
13473         }
13474          if (!opts.add) {
13475             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13476          }
13477         this.restrictHeight();
13478         this.selectedIndex = -1;
13479     },
13480
13481     // private
13482     onLoad : function(){
13483         
13484         this.hasQuery = false;
13485         
13486         if(!this.hasFocus){
13487             return;
13488         }
13489         
13490         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13491             this.loading.hide();
13492         }
13493         
13494         if(this.store.getCount() > 0){
13495             
13496             this.expand();
13497             this.restrictHeight();
13498             if(this.lastQuery == this.allQuery){
13499                 if(this.editable && !this.tickable){
13500                     this.inputEl().dom.select();
13501                 }
13502                 
13503                 if(
13504                     !this.selectByValue(this.value, true) &&
13505                     this.autoFocus && 
13506                     (
13507                         !this.store.lastOptions ||
13508                         typeof(this.store.lastOptions.add) == 'undefined' || 
13509                         this.store.lastOptions.add != true
13510                     )
13511                 ){
13512                     this.select(0, true);
13513                 }
13514             }else{
13515                 if(this.autoFocus){
13516                     this.selectNext();
13517                 }
13518                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13519                     this.taTask.delay(this.typeAheadDelay);
13520                 }
13521             }
13522         }else{
13523             this.onEmptyResults();
13524         }
13525         
13526         //this.el.focus();
13527     },
13528     // private
13529     onLoadException : function()
13530     {
13531         this.hasQuery = false;
13532         
13533         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13534             this.loading.hide();
13535         }
13536         
13537         if(this.tickable && this.editable){
13538             return;
13539         }
13540         
13541         this.collapse();
13542         // only causes errors at present
13543         //Roo.log(this.store.reader.jsonData);
13544         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13545             // fixme
13546             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13547         //}
13548         
13549         
13550     },
13551     // private
13552     onTypeAhead : function(){
13553         if(this.store.getCount() > 0){
13554             var r = this.store.getAt(0);
13555             var newValue = r.data[this.displayField];
13556             var len = newValue.length;
13557             var selStart = this.getRawValue().length;
13558             
13559             if(selStart != len){
13560                 this.setRawValue(newValue);
13561                 this.selectText(selStart, newValue.length);
13562             }
13563         }
13564     },
13565
13566     // private
13567     onSelect : function(record, index){
13568         
13569         if(this.fireEvent('beforeselect', this, record, index) !== false){
13570         
13571             this.setFromData(index > -1 ? record.data : false);
13572             
13573             this.collapse();
13574             this.fireEvent('select', this, record, index);
13575         }
13576     },
13577
13578     /**
13579      * Returns the currently selected field value or empty string if no value is set.
13580      * @return {String} value The selected value
13581      */
13582     getValue : function()
13583     {
13584         if(Roo.isIOS && this.useNativeIOS){
13585             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13586         }
13587         
13588         if(this.multiple){
13589             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13590         }
13591         
13592         if(this.valueField){
13593             return typeof this.value != 'undefined' ? this.value : '';
13594         }else{
13595             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13596         }
13597     },
13598     
13599     getRawValue : function()
13600     {
13601         if(Roo.isIOS && this.useNativeIOS){
13602             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13603         }
13604         
13605         var v = this.inputEl().getValue();
13606         
13607         return v;
13608     },
13609
13610     /**
13611      * Clears any text/value currently set in the field
13612      */
13613     clearValue : function(){
13614         
13615         if(this.hiddenField){
13616             this.hiddenField.dom.value = '';
13617         }
13618         this.value = '';
13619         this.setRawValue('');
13620         this.lastSelectionText = '';
13621         this.lastData = false;
13622         
13623         var close = this.closeTriggerEl();
13624         
13625         if(close){
13626             close.hide();
13627         }
13628         
13629         this.validate();
13630         
13631     },
13632
13633     /**
13634      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13635      * will be displayed in the field.  If the value does not match the data value of an existing item,
13636      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13637      * Otherwise the field will be blank (although the value will still be set).
13638      * @param {String} value The value to match
13639      */
13640     setValue : function(v)
13641     {
13642         if(Roo.isIOS && this.useNativeIOS){
13643             this.setIOSValue(v);
13644             return;
13645         }
13646         
13647         if(this.multiple){
13648             this.syncValue();
13649             return;
13650         }
13651         
13652         var text = v;
13653         if(this.valueField){
13654             var r = this.findRecord(this.valueField, v);
13655             if(r){
13656                 text = r.data[this.displayField];
13657             }else if(this.valueNotFoundText !== undefined){
13658                 text = this.valueNotFoundText;
13659             }
13660         }
13661         this.lastSelectionText = text;
13662         if(this.hiddenField){
13663             this.hiddenField.dom.value = v;
13664         }
13665         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13666         this.value = v;
13667         
13668         var close = this.closeTriggerEl();
13669         
13670         if(close){
13671             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13672         }
13673         
13674         this.validate();
13675     },
13676     /**
13677      * @property {Object} the last set data for the element
13678      */
13679     
13680     lastData : false,
13681     /**
13682      * Sets the value of the field based on a object which is related to the record format for the store.
13683      * @param {Object} value the value to set as. or false on reset?
13684      */
13685     setFromData : function(o){
13686         
13687         if(this.multiple){
13688             this.addItem(o);
13689             return;
13690         }
13691             
13692         var dv = ''; // display value
13693         var vv = ''; // value value..
13694         this.lastData = o;
13695         if (this.displayField) {
13696             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13697         } else {
13698             // this is an error condition!!!
13699             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13700         }
13701         
13702         if(this.valueField){
13703             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13704         }
13705         
13706         var close = this.closeTriggerEl();
13707         
13708         if(close){
13709             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13710         }
13711         
13712         if(this.hiddenField){
13713             this.hiddenField.dom.value = vv;
13714             
13715             this.lastSelectionText = dv;
13716             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13717             this.value = vv;
13718             return;
13719         }
13720         // no hidden field.. - we store the value in 'value', but still display
13721         // display field!!!!
13722         this.lastSelectionText = dv;
13723         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13724         this.value = vv;
13725         
13726         
13727         
13728     },
13729     // private
13730     reset : function(){
13731         // overridden so that last data is reset..
13732         
13733         if(this.multiple){
13734             this.clearItem();
13735             return;
13736         }
13737         
13738         this.setValue(this.originalValue);
13739         //this.clearInvalid();
13740         this.lastData = false;
13741         if (this.view) {
13742             this.view.clearSelections();
13743         }
13744         
13745         this.validate();
13746     },
13747     // private
13748     findRecord : function(prop, value){
13749         var record;
13750         if(this.store.getCount() > 0){
13751             this.store.each(function(r){
13752                 if(r.data[prop] == value){
13753                     record = r;
13754                     return false;
13755                 }
13756                 return true;
13757             });
13758         }
13759         return record;
13760     },
13761     
13762     getName: function()
13763     {
13764         // returns hidden if it's set..
13765         if (!this.rendered) {return ''};
13766         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13767         
13768     },
13769     // private
13770     onViewMove : function(e, t){
13771         this.inKeyMode = false;
13772     },
13773
13774     // private
13775     onViewOver : function(e, t){
13776         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13777             return;
13778         }
13779         var item = this.view.findItemFromChild(t);
13780         
13781         if(item){
13782             var index = this.view.indexOf(item);
13783             this.select(index, false);
13784         }
13785     },
13786
13787     // private
13788     onViewClick : function(view, doFocus, el, e)
13789     {
13790         var index = this.view.getSelectedIndexes()[0];
13791         
13792         var r = this.store.getAt(index);
13793         
13794         if(this.tickable){
13795             
13796             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13797                 return;
13798             }
13799             
13800             var rm = false;
13801             var _this = this;
13802             
13803             Roo.each(this.tickItems, function(v,k){
13804                 
13805                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13806                     Roo.log(v);
13807                     _this.tickItems.splice(k, 1);
13808                     
13809                     if(typeof(e) == 'undefined' && view == false){
13810                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13811                     }
13812                     
13813                     rm = true;
13814                     return;
13815                 }
13816             });
13817             
13818             if(rm){
13819                 return;
13820             }
13821             
13822             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13823                 this.tickItems.push(r.data);
13824             }
13825             
13826             if(typeof(e) == 'undefined' && view == false){
13827                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13828             }
13829                     
13830             return;
13831         }
13832         
13833         if(r){
13834             this.onSelect(r, index);
13835         }
13836         if(doFocus !== false && !this.blockFocus){
13837             this.inputEl().focus();
13838         }
13839     },
13840
13841     // private
13842     restrictHeight : function(){
13843         //this.innerList.dom.style.height = '';
13844         //var inner = this.innerList.dom;
13845         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13846         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13847         //this.list.beginUpdate();
13848         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13849         this.list.alignTo(this.inputEl(), this.listAlign);
13850         this.list.alignTo(this.inputEl(), this.listAlign);
13851         //this.list.endUpdate();
13852     },
13853
13854     // private
13855     onEmptyResults : function(){
13856         
13857         if(this.tickable && this.editable){
13858             this.restrictHeight();
13859             return;
13860         }
13861         
13862         this.collapse();
13863     },
13864
13865     /**
13866      * Returns true if the dropdown list is expanded, else false.
13867      */
13868     isExpanded : function(){
13869         return this.list.isVisible();
13870     },
13871
13872     /**
13873      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13874      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13875      * @param {String} value The data value of the item to select
13876      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13877      * selected item if it is not currently in view (defaults to true)
13878      * @return {Boolean} True if the value matched an item in the list, else false
13879      */
13880     selectByValue : function(v, scrollIntoView){
13881         if(v !== undefined && v !== null){
13882             var r = this.findRecord(this.valueField || this.displayField, v);
13883             if(r){
13884                 this.select(this.store.indexOf(r), scrollIntoView);
13885                 return true;
13886             }
13887         }
13888         return false;
13889     },
13890
13891     /**
13892      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13893      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13894      * @param {Number} index The zero-based index of the list item to select
13895      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13896      * selected item if it is not currently in view (defaults to true)
13897      */
13898     select : function(index, scrollIntoView){
13899         this.selectedIndex = index;
13900         this.view.select(index);
13901         if(scrollIntoView !== false){
13902             var el = this.view.getNode(index);
13903             /*
13904              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13905              */
13906             if(el){
13907                 this.list.scrollChildIntoView(el, false);
13908             }
13909         }
13910     },
13911
13912     // private
13913     selectNext : function(){
13914         var ct = this.store.getCount();
13915         if(ct > 0){
13916             if(this.selectedIndex == -1){
13917                 this.select(0);
13918             }else if(this.selectedIndex < ct-1){
13919                 this.select(this.selectedIndex+1);
13920             }
13921         }
13922     },
13923
13924     // private
13925     selectPrev : function(){
13926         var ct = this.store.getCount();
13927         if(ct > 0){
13928             if(this.selectedIndex == -1){
13929                 this.select(0);
13930             }else if(this.selectedIndex != 0){
13931                 this.select(this.selectedIndex-1);
13932             }
13933         }
13934     },
13935
13936     // private
13937     onKeyUp : function(e){
13938         if(this.editable !== false && !e.isSpecialKey()){
13939             this.lastKey = e.getKey();
13940             this.dqTask.delay(this.queryDelay);
13941         }
13942     },
13943
13944     // private
13945     validateBlur : function(){
13946         return !this.list || !this.list.isVisible();   
13947     },
13948
13949     // private
13950     initQuery : function(){
13951         
13952         var v = this.getRawValue();
13953         
13954         if(this.tickable && this.editable){
13955             v = this.tickableInputEl().getValue();
13956         }
13957         
13958         this.doQuery(v);
13959     },
13960
13961     // private
13962     doForce : function(){
13963         if(this.inputEl().dom.value.length > 0){
13964             this.inputEl().dom.value =
13965                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13966              
13967         }
13968     },
13969
13970     /**
13971      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13972      * query allowing the query action to be canceled if needed.
13973      * @param {String} query The SQL query to execute
13974      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13975      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13976      * saved in the current store (defaults to false)
13977      */
13978     doQuery : function(q, forceAll){
13979         
13980         if(q === undefined || q === null){
13981             q = '';
13982         }
13983         var qe = {
13984             query: q,
13985             forceAll: forceAll,
13986             combo: this,
13987             cancel:false
13988         };
13989         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13990             return false;
13991         }
13992         q = qe.query;
13993         
13994         forceAll = qe.forceAll;
13995         if(forceAll === true || (q.length >= this.minChars)){
13996             
13997             this.hasQuery = true;
13998             
13999             if(this.lastQuery != q || this.alwaysQuery){
14000                 this.lastQuery = q;
14001                 if(this.mode == 'local'){
14002                     this.selectedIndex = -1;
14003                     if(forceAll){
14004                         this.store.clearFilter();
14005                     }else{
14006                         
14007                         if(this.specialFilter){
14008                             this.fireEvent('specialfilter', this);
14009                             this.onLoad();
14010                             return;
14011                         }
14012                         
14013                         this.store.filter(this.displayField, q);
14014                     }
14015                     
14016                     this.store.fireEvent("datachanged", this.store);
14017                     
14018                     this.onLoad();
14019                     
14020                     
14021                 }else{
14022                     
14023                     this.store.baseParams[this.queryParam] = q;
14024                     
14025                     var options = {params : this.getParams(q)};
14026                     
14027                     if(this.loadNext){
14028                         options.add = true;
14029                         options.params.start = this.page * this.pageSize;
14030                     }
14031                     
14032                     this.store.load(options);
14033                     
14034                     /*
14035                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14036                      *  we should expand the list on onLoad
14037                      *  so command out it
14038                      */
14039 //                    this.expand();
14040                 }
14041             }else{
14042                 this.selectedIndex = -1;
14043                 this.onLoad();   
14044             }
14045         }
14046         
14047         this.loadNext = false;
14048     },
14049     
14050     // private
14051     getParams : function(q){
14052         var p = {};
14053         //p[this.queryParam] = q;
14054         
14055         if(this.pageSize){
14056             p.start = 0;
14057             p.limit = this.pageSize;
14058         }
14059         return p;
14060     },
14061
14062     /**
14063      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14064      */
14065     collapse : function(){
14066         if(!this.isExpanded()){
14067             return;
14068         }
14069         
14070         this.list.hide();
14071         
14072         this.hasFocus = false;
14073         
14074         if(this.tickable){
14075             this.okBtn.hide();
14076             this.cancelBtn.hide();
14077             this.trigger.show();
14078             
14079             if(this.editable){
14080                 this.tickableInputEl().dom.value = '';
14081                 this.tickableInputEl().blur();
14082             }
14083             
14084         }
14085         
14086         Roo.get(document).un('mousedown', this.collapseIf, this);
14087         Roo.get(document).un('mousewheel', this.collapseIf, this);
14088         if (!this.editable) {
14089             Roo.get(document).un('keydown', this.listKeyPress, this);
14090         }
14091         this.fireEvent('collapse', this);
14092         
14093         this.validate();
14094     },
14095
14096     // private
14097     collapseIf : function(e){
14098         var in_combo  = e.within(this.el);
14099         var in_list =  e.within(this.list);
14100         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14101         
14102         if (in_combo || in_list || is_list) {
14103             //e.stopPropagation();
14104             return;
14105         }
14106         
14107         if(this.tickable){
14108             this.onTickableFooterButtonClick(e, false, false);
14109         }
14110
14111         this.collapse();
14112         
14113     },
14114
14115     /**
14116      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14117      */
14118     expand : function(){
14119        
14120         if(this.isExpanded() || !this.hasFocus){
14121             return;
14122         }
14123         
14124         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14125         this.list.setWidth(lw);
14126         
14127         Roo.log('expand');
14128         
14129         this.list.show();
14130         
14131         this.restrictHeight();
14132         
14133         if(this.tickable){
14134             
14135             this.tickItems = Roo.apply([], this.item);
14136             
14137             this.okBtn.show();
14138             this.cancelBtn.show();
14139             this.trigger.hide();
14140             
14141             if(this.editable){
14142                 this.tickableInputEl().focus();
14143             }
14144             
14145         }
14146         
14147         Roo.get(document).on('mousedown', this.collapseIf, this);
14148         Roo.get(document).on('mousewheel', this.collapseIf, this);
14149         if (!this.editable) {
14150             Roo.get(document).on('keydown', this.listKeyPress, this);
14151         }
14152         
14153         this.fireEvent('expand', this);
14154     },
14155
14156     // private
14157     // Implements the default empty TriggerField.onTriggerClick function
14158     onTriggerClick : function(e)
14159     {
14160         Roo.log('trigger click');
14161         
14162         if(this.disabled || !this.triggerList){
14163             return;
14164         }
14165         
14166         this.page = 0;
14167         this.loadNext = false;
14168         
14169         if(this.isExpanded()){
14170             this.collapse();
14171             if (!this.blockFocus) {
14172                 this.inputEl().focus();
14173             }
14174             
14175         }else {
14176             this.hasFocus = true;
14177             if(this.triggerAction == 'all') {
14178                 this.doQuery(this.allQuery, true);
14179             } else {
14180                 this.doQuery(this.getRawValue());
14181             }
14182             if (!this.blockFocus) {
14183                 this.inputEl().focus();
14184             }
14185         }
14186     },
14187     
14188     onTickableTriggerClick : function(e)
14189     {
14190         if(this.disabled){
14191             return;
14192         }
14193         
14194         this.page = 0;
14195         this.loadNext = false;
14196         this.hasFocus = true;
14197         
14198         if(this.triggerAction == 'all') {
14199             this.doQuery(this.allQuery, true);
14200         } else {
14201             this.doQuery(this.getRawValue());
14202         }
14203     },
14204     
14205     onSearchFieldClick : function(e)
14206     {
14207         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14208             this.onTickableFooterButtonClick(e, false, false);
14209             return;
14210         }
14211         
14212         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14213             return;
14214         }
14215         
14216         this.page = 0;
14217         this.loadNext = false;
14218         this.hasFocus = true;
14219         
14220         if(this.triggerAction == 'all') {
14221             this.doQuery(this.allQuery, true);
14222         } else {
14223             this.doQuery(this.getRawValue());
14224         }
14225     },
14226     
14227     listKeyPress : function(e)
14228     {
14229         //Roo.log('listkeypress');
14230         // scroll to first matching element based on key pres..
14231         if (e.isSpecialKey()) {
14232             return false;
14233         }
14234         var k = String.fromCharCode(e.getKey()).toUpperCase();
14235         //Roo.log(k);
14236         var match  = false;
14237         var csel = this.view.getSelectedNodes();
14238         var cselitem = false;
14239         if (csel.length) {
14240             var ix = this.view.indexOf(csel[0]);
14241             cselitem  = this.store.getAt(ix);
14242             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14243                 cselitem = false;
14244             }
14245             
14246         }
14247         
14248         this.store.each(function(v) { 
14249             if (cselitem) {
14250                 // start at existing selection.
14251                 if (cselitem.id == v.id) {
14252                     cselitem = false;
14253                 }
14254                 return true;
14255             }
14256                 
14257             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14258                 match = this.store.indexOf(v);
14259                 return false;
14260             }
14261             return true;
14262         }, this);
14263         
14264         if (match === false) {
14265             return true; // no more action?
14266         }
14267         // scroll to?
14268         this.view.select(match);
14269         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14270         sn.scrollIntoView(sn.dom.parentNode, false);
14271     },
14272     
14273     onViewScroll : function(e, t){
14274         
14275         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){
14276             return;
14277         }
14278         
14279         this.hasQuery = true;
14280         
14281         this.loading = this.list.select('.loading', true).first();
14282         
14283         if(this.loading === null){
14284             this.list.createChild({
14285                 tag: 'div',
14286                 cls: 'loading roo-select2-more-results roo-select2-active',
14287                 html: 'Loading more results...'
14288             });
14289             
14290             this.loading = this.list.select('.loading', true).first();
14291             
14292             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14293             
14294             this.loading.hide();
14295         }
14296         
14297         this.loading.show();
14298         
14299         var _combo = this;
14300         
14301         this.page++;
14302         this.loadNext = true;
14303         
14304         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14305         
14306         return;
14307     },
14308     
14309     addItem : function(o)
14310     {   
14311         var dv = ''; // display value
14312         
14313         if (this.displayField) {
14314             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14315         } else {
14316             // this is an error condition!!!
14317             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14318         }
14319         
14320         if(!dv.length){
14321             return;
14322         }
14323         
14324         var choice = this.choices.createChild({
14325             tag: 'li',
14326             cls: 'roo-select2-search-choice',
14327             cn: [
14328                 {
14329                     tag: 'div',
14330                     html: dv
14331                 },
14332                 {
14333                     tag: 'a',
14334                     href: '#',
14335                     cls: 'roo-select2-search-choice-close fa fa-times',
14336                     tabindex: '-1'
14337                 }
14338             ]
14339             
14340         }, this.searchField);
14341         
14342         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14343         
14344         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14345         
14346         this.item.push(o);
14347         
14348         this.lastData = o;
14349         
14350         this.syncValue();
14351         
14352         this.inputEl().dom.value = '';
14353         
14354         this.validate();
14355     },
14356     
14357     onRemoveItem : function(e, _self, o)
14358     {
14359         e.preventDefault();
14360         
14361         this.lastItem = Roo.apply([], this.item);
14362         
14363         var index = this.item.indexOf(o.data) * 1;
14364         
14365         if( index < 0){
14366             Roo.log('not this item?!');
14367             return;
14368         }
14369         
14370         this.item.splice(index, 1);
14371         o.item.remove();
14372         
14373         this.syncValue();
14374         
14375         this.fireEvent('remove', this, e);
14376         
14377         this.validate();
14378         
14379     },
14380     
14381     syncValue : function()
14382     {
14383         if(!this.item.length){
14384             this.clearValue();
14385             return;
14386         }
14387             
14388         var value = [];
14389         var _this = this;
14390         Roo.each(this.item, function(i){
14391             if(_this.valueField){
14392                 value.push(i[_this.valueField]);
14393                 return;
14394             }
14395
14396             value.push(i);
14397         });
14398
14399         this.value = value.join(',');
14400
14401         if(this.hiddenField){
14402             this.hiddenField.dom.value = this.value;
14403         }
14404         
14405         this.store.fireEvent("datachanged", this.store);
14406         
14407         this.validate();
14408     },
14409     
14410     clearItem : function()
14411     {
14412         if(!this.multiple){
14413             return;
14414         }
14415         
14416         this.item = [];
14417         
14418         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14419            c.remove();
14420         });
14421         
14422         this.syncValue();
14423         
14424         this.validate();
14425         
14426         if(this.tickable && !Roo.isTouch){
14427             this.view.refresh();
14428         }
14429     },
14430     
14431     inputEl: function ()
14432     {
14433         if(Roo.isIOS && this.useNativeIOS){
14434             return this.el.select('select.roo-ios-select', true).first();
14435         }
14436         
14437         if(Roo.isTouch && this.mobileTouchView){
14438             return this.el.select('input.form-control',true).first();
14439         }
14440         
14441         if(this.tickable){
14442             return this.searchField;
14443         }
14444         
14445         return this.el.select('input.form-control',true).first();
14446     },
14447     
14448     onTickableFooterButtonClick : function(e, btn, el)
14449     {
14450         e.preventDefault();
14451         
14452         this.lastItem = Roo.apply([], this.item);
14453         
14454         if(btn && btn.name == 'cancel'){
14455             this.tickItems = Roo.apply([], this.item);
14456             this.collapse();
14457             return;
14458         }
14459         
14460         this.clearItem();
14461         
14462         var _this = this;
14463         
14464         Roo.each(this.tickItems, function(o){
14465             _this.addItem(o);
14466         });
14467         
14468         this.collapse();
14469         
14470     },
14471     
14472     validate : function()
14473     {
14474         var v = this.getRawValue();
14475         
14476         if(this.multiple){
14477             v = this.getValue();
14478         }
14479         
14480         if(this.disabled || this.allowBlank || v.length){
14481             this.markValid();
14482             return true;
14483         }
14484         
14485         this.markInvalid();
14486         return false;
14487     },
14488     
14489     tickableInputEl : function()
14490     {
14491         if(!this.tickable || !this.editable){
14492             return this.inputEl();
14493         }
14494         
14495         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14496     },
14497     
14498     
14499     getAutoCreateTouchView : function()
14500     {
14501         var id = Roo.id();
14502         
14503         var cfg = {
14504             cls: 'form-group' //input-group
14505         };
14506         
14507         var input =  {
14508             tag: 'input',
14509             id : id,
14510             type : this.inputType,
14511             cls : 'form-control x-combo-noedit',
14512             autocomplete: 'new-password',
14513             placeholder : this.placeholder || '',
14514             readonly : true
14515         };
14516         
14517         if (this.name) {
14518             input.name = this.name;
14519         }
14520         
14521         if (this.size) {
14522             input.cls += ' input-' + this.size;
14523         }
14524         
14525         if (this.disabled) {
14526             input.disabled = true;
14527         }
14528         
14529         var inputblock = {
14530             cls : '',
14531             cn : [
14532                 input
14533             ]
14534         };
14535         
14536         if(this.before){
14537             inputblock.cls += ' input-group';
14538             
14539             inputblock.cn.unshift({
14540                 tag :'span',
14541                 cls : 'input-group-addon',
14542                 html : this.before
14543             });
14544         }
14545         
14546         if(this.removable && !this.multiple){
14547             inputblock.cls += ' roo-removable';
14548             
14549             inputblock.cn.push({
14550                 tag: 'button',
14551                 html : 'x',
14552                 cls : 'roo-combo-removable-btn close'
14553             });
14554         }
14555
14556         if(this.hasFeedback && !this.allowBlank){
14557             
14558             inputblock.cls += ' has-feedback';
14559             
14560             inputblock.cn.push({
14561                 tag: 'span',
14562                 cls: 'glyphicon form-control-feedback'
14563             });
14564             
14565         }
14566         
14567         if (this.after) {
14568             
14569             inputblock.cls += (this.before) ? '' : ' input-group';
14570             
14571             inputblock.cn.push({
14572                 tag :'span',
14573                 cls : 'input-group-addon',
14574                 html : this.after
14575             });
14576         }
14577
14578         var box = {
14579             tag: 'div',
14580             cn: [
14581                 {
14582                     tag: 'input',
14583                     type : 'hidden',
14584                     cls: 'form-hidden-field'
14585                 },
14586                 inputblock
14587             ]
14588             
14589         };
14590         
14591         if(this.multiple){
14592             box = {
14593                 tag: 'div',
14594                 cn: [
14595                     {
14596                         tag: 'input',
14597                         type : 'hidden',
14598                         cls: 'form-hidden-field'
14599                     },
14600                     {
14601                         tag: 'ul',
14602                         cls: 'roo-select2-choices',
14603                         cn:[
14604                             {
14605                                 tag: 'li',
14606                                 cls: 'roo-select2-search-field',
14607                                 cn: [
14608
14609                                     inputblock
14610                                 ]
14611                             }
14612                         ]
14613                     }
14614                 ]
14615             }
14616         };
14617         
14618         var combobox = {
14619             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14620             cn: [
14621                 box
14622             ]
14623         };
14624         
14625         if(!this.multiple && this.showToggleBtn){
14626             
14627             var caret = {
14628                         tag: 'span',
14629                         cls: 'caret'
14630             };
14631             
14632             if (this.caret != false) {
14633                 caret = {
14634                      tag: 'i',
14635                      cls: 'fa fa-' + this.caret
14636                 };
14637                 
14638             }
14639             
14640             combobox.cn.push({
14641                 tag :'span',
14642                 cls : 'input-group-addon btn dropdown-toggle',
14643                 cn : [
14644                     caret,
14645                     {
14646                         tag: 'span',
14647                         cls: 'combobox-clear',
14648                         cn  : [
14649                             {
14650                                 tag : 'i',
14651                                 cls: 'icon-remove'
14652                             }
14653                         ]
14654                     }
14655                 ]
14656
14657             })
14658         }
14659         
14660         if(this.multiple){
14661             combobox.cls += ' roo-select2-container-multi';
14662         }
14663         
14664         var align = this.labelAlign || this.parentLabelAlign();
14665         
14666         if (align ==='left' && this.fieldLabel.length) {
14667
14668             cfg.cn = [
14669                 {
14670                    tag : 'i',
14671                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14672                    tooltip : 'This field is required'
14673                 },
14674                 {
14675                     tag: 'label',
14676                     cls : 'control-label',
14677                     html : this.fieldLabel
14678
14679                 },
14680                 {
14681                     cls : '', 
14682                     cn: [
14683                         combobox
14684                     ]
14685                 }
14686             ];
14687             
14688             var labelCfg = cfg.cn[1];
14689             var contentCfg = cfg.cn[2];
14690             
14691
14692             if(this.indicatorpos == 'right'){
14693                 cfg.cn = [
14694                     {
14695                         tag: 'label',
14696                         cls : 'control-label',
14697                         html : this.fieldLabel,
14698                         cn : [
14699                             {
14700                                tag : 'i',
14701                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14702                                tooltip : 'This field is required'
14703                             }
14704                         ]
14705                     },
14706                     {
14707                         cls : '', 
14708                         cn: [
14709                             combobox
14710                         ]
14711                     }
14712                 ];
14713             }
14714             
14715             labelCfg = cfg.cn[0];
14716             contentCfg = cfg.cn[2];
14717             
14718             if(this.labelWidth > 12){
14719                 labelCfg.style = "width: " + this.labelWidth + 'px';
14720             }
14721             
14722             if(this.labelWidth < 13 && this.labelmd == 0){
14723                 this.labelmd = this.labelWidth;
14724             }
14725             
14726             if(this.labellg > 0){
14727                 labelCfg.cls += ' col-lg-' + this.labellg;
14728                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14729             }
14730             
14731             if(this.labelmd > 0){
14732                 labelCfg.cls += ' col-md-' + this.labelmd;
14733                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14734             }
14735             
14736             if(this.labelsm > 0){
14737                 labelCfg.cls += ' col-sm-' + this.labelsm;
14738                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14739             }
14740             
14741             if(this.labelxs > 0){
14742                 labelCfg.cls += ' col-xs-' + this.labelxs;
14743                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14744             }
14745                 
14746                 
14747         } else if ( this.fieldLabel.length) {
14748             cfg.cn = [
14749                 {
14750                    tag : 'i',
14751                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14752                    tooltip : 'This field is required'
14753                 },
14754                 {
14755                     tag: 'label',
14756                     cls : 'control-label',
14757                     html : this.fieldLabel
14758
14759                 },
14760                 {
14761                     cls : '', 
14762                     cn: [
14763                         combobox
14764                     ]
14765                 }
14766             ];
14767             
14768             if(this.indicatorpos == 'right'){
14769                 cfg.cn = [
14770                     {
14771                         tag: 'label',
14772                         cls : 'control-label',
14773                         html : this.fieldLabel,
14774                         cn : [
14775                             {
14776                                tag : 'i',
14777                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14778                                tooltip : 'This field is required'
14779                             }
14780                         ]
14781                     },
14782                     {
14783                         cls : '', 
14784                         cn: [
14785                             combobox
14786                         ]
14787                     }
14788                 ];
14789             }
14790         } else {
14791             cfg.cn = combobox;    
14792         }
14793         
14794         
14795         var settings = this;
14796         
14797         ['xs','sm','md','lg'].map(function(size){
14798             if (settings[size]) {
14799                 cfg.cls += ' col-' + size + '-' + settings[size];
14800             }
14801         });
14802         
14803         return cfg;
14804     },
14805     
14806     initTouchView : function()
14807     {
14808         this.renderTouchView();
14809         
14810         this.touchViewEl.on('scroll', function(){
14811             this.el.dom.scrollTop = 0;
14812         }, this);
14813         
14814         this.originalValue = this.getValue();
14815         
14816         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14817         
14818         this.inputEl().on("click", this.showTouchView, this);
14819         if (this.triggerEl) {
14820             this.triggerEl.on("click", this.showTouchView, this);
14821         }
14822         
14823         
14824         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14825         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14826         
14827         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14828         
14829         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14830         this.store.on('load', this.onTouchViewLoad, this);
14831         this.store.on('loadexception', this.onTouchViewLoadException, this);
14832         
14833         if(this.hiddenName){
14834             
14835             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14836             
14837             this.hiddenField.dom.value =
14838                 this.hiddenValue !== undefined ? this.hiddenValue :
14839                 this.value !== undefined ? this.value : '';
14840         
14841             this.el.dom.removeAttribute('name');
14842             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14843         }
14844         
14845         if(this.multiple){
14846             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14847             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14848         }
14849         
14850         if(this.removable && !this.multiple){
14851             var close = this.closeTriggerEl();
14852             if(close){
14853                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14854                 close.on('click', this.removeBtnClick, this, close);
14855             }
14856         }
14857         /*
14858          * fix the bug in Safari iOS8
14859          */
14860         this.inputEl().on("focus", function(e){
14861             document.activeElement.blur();
14862         }, this);
14863         
14864         return;
14865         
14866         
14867     },
14868     
14869     renderTouchView : function()
14870     {
14871         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14872         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14873         
14874         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14875         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14876         
14877         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14878         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14879         this.touchViewBodyEl.setStyle('overflow', 'auto');
14880         
14881         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14882         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14883         
14884         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14885         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14886         
14887     },
14888     
14889     showTouchView : function()
14890     {
14891         if(this.disabled){
14892             return;
14893         }
14894         
14895         this.touchViewHeaderEl.hide();
14896
14897         if(this.modalTitle.length){
14898             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14899             this.touchViewHeaderEl.show();
14900         }
14901
14902         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14903         this.touchViewEl.show();
14904
14905         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14906         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14907                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14908
14909         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14910
14911         if(this.modalTitle.length){
14912             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14913         }
14914         
14915         this.touchViewBodyEl.setHeight(bodyHeight);
14916
14917         if(this.animate){
14918             var _this = this;
14919             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14920         }else{
14921             this.touchViewEl.addClass('in');
14922         }
14923
14924         this.doTouchViewQuery();
14925         
14926     },
14927     
14928     hideTouchView : function()
14929     {
14930         this.touchViewEl.removeClass('in');
14931
14932         if(this.animate){
14933             var _this = this;
14934             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14935         }else{
14936             this.touchViewEl.setStyle('display', 'none');
14937         }
14938         
14939     },
14940     
14941     setTouchViewValue : function()
14942     {
14943         if(this.multiple){
14944             this.clearItem();
14945         
14946             var _this = this;
14947
14948             Roo.each(this.tickItems, function(o){
14949                 this.addItem(o);
14950             }, this);
14951         }
14952         
14953         this.hideTouchView();
14954     },
14955     
14956     doTouchViewQuery : function()
14957     {
14958         var qe = {
14959             query: '',
14960             forceAll: true,
14961             combo: this,
14962             cancel:false
14963         };
14964         
14965         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14966             return false;
14967         }
14968         
14969         if(!this.alwaysQuery || this.mode == 'local'){
14970             this.onTouchViewLoad();
14971             return;
14972         }
14973         
14974         this.store.load();
14975     },
14976     
14977     onTouchViewBeforeLoad : function(combo,opts)
14978     {
14979         return;
14980     },
14981
14982     // private
14983     onTouchViewLoad : function()
14984     {
14985         if(this.store.getCount() < 1){
14986             this.onTouchViewEmptyResults();
14987             return;
14988         }
14989         
14990         this.clearTouchView();
14991         
14992         var rawValue = this.getRawValue();
14993         
14994         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14995         
14996         this.tickItems = [];
14997         
14998         this.store.data.each(function(d, rowIndex){
14999             var row = this.touchViewListGroup.createChild(template);
15000             
15001             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15002                 row.addClass(d.data.cls);
15003             }
15004             
15005             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15006                 var cfg = {
15007                     data : d.data,
15008                     html : d.data[this.displayField]
15009                 };
15010                 
15011                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15012                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15013                 }
15014             }
15015             row.removeClass('selected');
15016             if(!this.multiple && this.valueField &&
15017                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15018             {
15019                 // radio buttons..
15020                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15021                 row.addClass('selected');
15022             }
15023             
15024             if(this.multiple && this.valueField &&
15025                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15026             {
15027                 
15028                 // checkboxes...
15029                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15030                 this.tickItems.push(d.data);
15031             }
15032             
15033             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15034             
15035         }, this);
15036         
15037         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15038         
15039         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15040
15041         if(this.modalTitle.length){
15042             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15043         }
15044
15045         var listHeight = this.touchViewListGroup.getHeight();
15046         
15047         var _this = this;
15048         
15049         if(firstChecked && listHeight > bodyHeight){
15050             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15051         }
15052         
15053     },
15054     
15055     onTouchViewLoadException : function()
15056     {
15057         this.hideTouchView();
15058     },
15059     
15060     onTouchViewEmptyResults : function()
15061     {
15062         this.clearTouchView();
15063         
15064         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15065         
15066         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15067         
15068     },
15069     
15070     clearTouchView : function()
15071     {
15072         this.touchViewListGroup.dom.innerHTML = '';
15073     },
15074     
15075     onTouchViewClick : function(e, el, o)
15076     {
15077         e.preventDefault();
15078         
15079         var row = o.row;
15080         var rowIndex = o.rowIndex;
15081         
15082         var r = this.store.getAt(rowIndex);
15083         
15084         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15085             
15086             if(!this.multiple){
15087                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15088                     c.dom.removeAttribute('checked');
15089                 }, this);
15090
15091                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15092
15093                 this.setFromData(r.data);
15094
15095                 var close = this.closeTriggerEl();
15096
15097                 if(close){
15098                     close.show();
15099                 }
15100
15101                 this.hideTouchView();
15102
15103                 this.fireEvent('select', this, r, rowIndex);
15104
15105                 return;
15106             }
15107
15108             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15109                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15110                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15111                 return;
15112             }
15113
15114             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15115             this.addItem(r.data);
15116             this.tickItems.push(r.data);
15117         }
15118     },
15119     
15120     getAutoCreateNativeIOS : function()
15121     {
15122         var cfg = {
15123             cls: 'form-group' //input-group,
15124         };
15125         
15126         var combobox =  {
15127             tag: 'select',
15128             cls : 'roo-ios-select'
15129         };
15130         
15131         if (this.name) {
15132             combobox.name = this.name;
15133         }
15134         
15135         if (this.disabled) {
15136             combobox.disabled = true;
15137         }
15138         
15139         var settings = this;
15140         
15141         ['xs','sm','md','lg'].map(function(size){
15142             if (settings[size]) {
15143                 cfg.cls += ' col-' + size + '-' + settings[size];
15144             }
15145         });
15146         
15147         cfg.cn = combobox;
15148         
15149         return cfg;
15150         
15151     },
15152     
15153     initIOSView : function()
15154     {
15155         this.store.on('load', this.onIOSViewLoad, this);
15156         
15157         return;
15158     },
15159     
15160     onIOSViewLoad : function()
15161     {
15162         if(this.store.getCount() < 1){
15163             return;
15164         }
15165         
15166         this.clearIOSView();
15167         
15168         if(this.allowBlank) {
15169             
15170             var default_text = '-- SELECT --';
15171             
15172             var opt = this.inputEl().createChild({
15173                 tag: 'option',
15174                 value : 0,
15175                 html : default_text
15176             });
15177             
15178             var o = {};
15179             o[this.valueField] = 0;
15180             o[this.displayField] = default_text;
15181             
15182             this.ios_options.push({
15183                 data : o,
15184                 el : opt
15185             });
15186             
15187         }
15188         
15189         this.store.data.each(function(d, rowIndex){
15190             
15191             var html = '';
15192             
15193             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15194                 html = d.data[this.displayField];
15195             }
15196             
15197             var value = '';
15198             
15199             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15200                 value = d.data[this.valueField];
15201             }
15202             
15203             var option = {
15204                 tag: 'option',
15205                 value : value,
15206                 html : html
15207             };
15208             
15209             if(this.value == d.data[this.valueField]){
15210                 option['selected'] = true;
15211             }
15212             
15213             var opt = this.inputEl().createChild(option);
15214             
15215             this.ios_options.push({
15216                 data : d.data,
15217                 el : opt
15218             });
15219             
15220         }, this);
15221         
15222         this.inputEl().on('change', function(){
15223            this.fireEvent('select', this);
15224         }, this);
15225         
15226     },
15227     
15228     clearIOSView: function()
15229     {
15230         this.inputEl().dom.innerHTML = '';
15231         
15232         this.ios_options = [];
15233     },
15234     
15235     setIOSValue: function(v)
15236     {
15237         this.value = v;
15238         
15239         if(!this.ios_options){
15240             return;
15241         }
15242         
15243         Roo.each(this.ios_options, function(opts){
15244            
15245            opts.el.dom.removeAttribute('selected');
15246            
15247            if(opts.data[this.valueField] != v){
15248                return;
15249            }
15250            
15251            opts.el.dom.setAttribute('selected', true);
15252            
15253         }, this);
15254     }
15255
15256     /** 
15257     * @cfg {Boolean} grow 
15258     * @hide 
15259     */
15260     /** 
15261     * @cfg {Number} growMin 
15262     * @hide 
15263     */
15264     /** 
15265     * @cfg {Number} growMax 
15266     * @hide 
15267     */
15268     /**
15269      * @hide
15270      * @method autoSize
15271      */
15272 });
15273
15274 Roo.apply(Roo.bootstrap.ComboBox,  {
15275     
15276     header : {
15277         tag: 'div',
15278         cls: 'modal-header',
15279         cn: [
15280             {
15281                 tag: 'h4',
15282                 cls: 'modal-title'
15283             }
15284         ]
15285     },
15286     
15287     body : {
15288         tag: 'div',
15289         cls: 'modal-body',
15290         cn: [
15291             {
15292                 tag: 'ul',
15293                 cls: 'list-group'
15294             }
15295         ]
15296     },
15297     
15298     listItemRadio : {
15299         tag: 'li',
15300         cls: 'list-group-item',
15301         cn: [
15302             {
15303                 tag: 'span',
15304                 cls: 'roo-combobox-list-group-item-value'
15305             },
15306             {
15307                 tag: 'div',
15308                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15309                 cn: [
15310                     {
15311                         tag: 'input',
15312                         type: 'radio'
15313                     },
15314                     {
15315                         tag: 'label'
15316                     }
15317                 ]
15318             }
15319         ]
15320     },
15321     
15322     listItemCheckbox : {
15323         tag: 'li',
15324         cls: 'list-group-item',
15325         cn: [
15326             {
15327                 tag: 'span',
15328                 cls: 'roo-combobox-list-group-item-value'
15329             },
15330             {
15331                 tag: 'div',
15332                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15333                 cn: [
15334                     {
15335                         tag: 'input',
15336                         type: 'checkbox'
15337                     },
15338                     {
15339                         tag: 'label'
15340                     }
15341                 ]
15342             }
15343         ]
15344     },
15345     
15346     emptyResult : {
15347         tag: 'div',
15348         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15349     },
15350     
15351     footer : {
15352         tag: 'div',
15353         cls: 'modal-footer',
15354         cn: [
15355             {
15356                 tag: 'div',
15357                 cls: 'row',
15358                 cn: [
15359                     {
15360                         tag: 'div',
15361                         cls: 'col-xs-6 text-left',
15362                         cn: {
15363                             tag: 'button',
15364                             cls: 'btn btn-danger roo-touch-view-cancel',
15365                             html: 'Cancel'
15366                         }
15367                     },
15368                     {
15369                         tag: 'div',
15370                         cls: 'col-xs-6 text-right',
15371                         cn: {
15372                             tag: 'button',
15373                             cls: 'btn btn-success roo-touch-view-ok',
15374                             html: 'OK'
15375                         }
15376                     }
15377                 ]
15378             }
15379         ]
15380         
15381     }
15382 });
15383
15384 Roo.apply(Roo.bootstrap.ComboBox,  {
15385     
15386     touchViewTemplate : {
15387         tag: 'div',
15388         cls: 'modal fade roo-combobox-touch-view',
15389         cn: [
15390             {
15391                 tag: 'div',
15392                 cls: 'modal-dialog',
15393                 style : 'position:fixed', // we have to fix position....
15394                 cn: [
15395                     {
15396                         tag: 'div',
15397                         cls: 'modal-content',
15398                         cn: [
15399                             Roo.bootstrap.ComboBox.header,
15400                             Roo.bootstrap.ComboBox.body,
15401                             Roo.bootstrap.ComboBox.footer
15402                         ]
15403                     }
15404                 ]
15405             }
15406         ]
15407     }
15408 });/*
15409  * Based on:
15410  * Ext JS Library 1.1.1
15411  * Copyright(c) 2006-2007, Ext JS, LLC.
15412  *
15413  * Originally Released Under LGPL - original licence link has changed is not relivant.
15414  *
15415  * Fork - LGPL
15416  * <script type="text/javascript">
15417  */
15418
15419 /**
15420  * @class Roo.View
15421  * @extends Roo.util.Observable
15422  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15423  * This class also supports single and multi selection modes. <br>
15424  * Create a data model bound view:
15425  <pre><code>
15426  var store = new Roo.data.Store(...);
15427
15428  var view = new Roo.View({
15429     el : "my-element",
15430     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15431  
15432     singleSelect: true,
15433     selectedClass: "ydataview-selected",
15434     store: store
15435  });
15436
15437  // listen for node click?
15438  view.on("click", function(vw, index, node, e){
15439  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15440  });
15441
15442  // load XML data
15443  dataModel.load("foobar.xml");
15444  </code></pre>
15445  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15446  * <br><br>
15447  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15448  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15449  * 
15450  * Note: old style constructor is still suported (container, template, config)
15451  * 
15452  * @constructor
15453  * Create a new View
15454  * @param {Object} config The config object
15455  * 
15456  */
15457 Roo.View = function(config, depreciated_tpl, depreciated_config){
15458     
15459     this.parent = false;
15460     
15461     if (typeof(depreciated_tpl) == 'undefined') {
15462         // new way.. - universal constructor.
15463         Roo.apply(this, config);
15464         this.el  = Roo.get(this.el);
15465     } else {
15466         // old format..
15467         this.el  = Roo.get(config);
15468         this.tpl = depreciated_tpl;
15469         Roo.apply(this, depreciated_config);
15470     }
15471     this.wrapEl  = this.el.wrap().wrap();
15472     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15473     
15474     
15475     if(typeof(this.tpl) == "string"){
15476         this.tpl = new Roo.Template(this.tpl);
15477     } else {
15478         // support xtype ctors..
15479         this.tpl = new Roo.factory(this.tpl, Roo);
15480     }
15481     
15482     
15483     this.tpl.compile();
15484     
15485     /** @private */
15486     this.addEvents({
15487         /**
15488          * @event beforeclick
15489          * Fires before a click is processed. Returns false to cancel the default action.
15490          * @param {Roo.View} this
15491          * @param {Number} index The index of the target node
15492          * @param {HTMLElement} node The target node
15493          * @param {Roo.EventObject} e The raw event object
15494          */
15495             "beforeclick" : true,
15496         /**
15497          * @event click
15498          * Fires when a template node is clicked.
15499          * @param {Roo.View} this
15500          * @param {Number} index The index of the target node
15501          * @param {HTMLElement} node The target node
15502          * @param {Roo.EventObject} e The raw event object
15503          */
15504             "click" : true,
15505         /**
15506          * @event dblclick
15507          * Fires when a template node is double clicked.
15508          * @param {Roo.View} this
15509          * @param {Number} index The index of the target node
15510          * @param {HTMLElement} node The target node
15511          * @param {Roo.EventObject} e The raw event object
15512          */
15513             "dblclick" : true,
15514         /**
15515          * @event contextmenu
15516          * Fires when a template node is right clicked.
15517          * @param {Roo.View} this
15518          * @param {Number} index The index of the target node
15519          * @param {HTMLElement} node The target node
15520          * @param {Roo.EventObject} e The raw event object
15521          */
15522             "contextmenu" : true,
15523         /**
15524          * @event selectionchange
15525          * Fires when the selected nodes change.
15526          * @param {Roo.View} this
15527          * @param {Array} selections Array of the selected nodes
15528          */
15529             "selectionchange" : true,
15530     
15531         /**
15532          * @event beforeselect
15533          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15534          * @param {Roo.View} this
15535          * @param {HTMLElement} node The node to be selected
15536          * @param {Array} selections Array of currently selected nodes
15537          */
15538             "beforeselect" : true,
15539         /**
15540          * @event preparedata
15541          * Fires on every row to render, to allow you to change the data.
15542          * @param {Roo.View} this
15543          * @param {Object} data to be rendered (change this)
15544          */
15545           "preparedata" : true
15546           
15547           
15548         });
15549
15550
15551
15552     this.el.on({
15553         "click": this.onClick,
15554         "dblclick": this.onDblClick,
15555         "contextmenu": this.onContextMenu,
15556         scope:this
15557     });
15558
15559     this.selections = [];
15560     this.nodes = [];
15561     this.cmp = new Roo.CompositeElementLite([]);
15562     if(this.store){
15563         this.store = Roo.factory(this.store, Roo.data);
15564         this.setStore(this.store, true);
15565     }
15566     
15567     if ( this.footer && this.footer.xtype) {
15568            
15569          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15570         
15571         this.footer.dataSource = this.store;
15572         this.footer.container = fctr;
15573         this.footer = Roo.factory(this.footer, Roo);
15574         fctr.insertFirst(this.el);
15575         
15576         // this is a bit insane - as the paging toolbar seems to detach the el..
15577 //        dom.parentNode.parentNode.parentNode
15578          // they get detached?
15579     }
15580     
15581     
15582     Roo.View.superclass.constructor.call(this);
15583     
15584     
15585 };
15586
15587 Roo.extend(Roo.View, Roo.util.Observable, {
15588     
15589      /**
15590      * @cfg {Roo.data.Store} store Data store to load data from.
15591      */
15592     store : false,
15593     
15594     /**
15595      * @cfg {String|Roo.Element} el The container element.
15596      */
15597     el : '',
15598     
15599     /**
15600      * @cfg {String|Roo.Template} tpl The template used by this View 
15601      */
15602     tpl : false,
15603     /**
15604      * @cfg {String} dataName the named area of the template to use as the data area
15605      *                          Works with domtemplates roo-name="name"
15606      */
15607     dataName: false,
15608     /**
15609      * @cfg {String} selectedClass The css class to add to selected nodes
15610      */
15611     selectedClass : "x-view-selected",
15612      /**
15613      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15614      */
15615     emptyText : "",
15616     
15617     /**
15618      * @cfg {String} text to display on mask (default Loading)
15619      */
15620     mask : false,
15621     /**
15622      * @cfg {Boolean} multiSelect Allow multiple selection
15623      */
15624     multiSelect : false,
15625     /**
15626      * @cfg {Boolean} singleSelect Allow single selection
15627      */
15628     singleSelect:  false,
15629     
15630     /**
15631      * @cfg {Boolean} toggleSelect - selecting 
15632      */
15633     toggleSelect : false,
15634     
15635     /**
15636      * @cfg {Boolean} tickable - selecting 
15637      */
15638     tickable : false,
15639     
15640     /**
15641      * Returns the element this view is bound to.
15642      * @return {Roo.Element}
15643      */
15644     getEl : function(){
15645         return this.wrapEl;
15646     },
15647     
15648     
15649
15650     /**
15651      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15652      */
15653     refresh : function(){
15654         //Roo.log('refresh');
15655         var t = this.tpl;
15656         
15657         // if we are using something like 'domtemplate', then
15658         // the what gets used is:
15659         // t.applySubtemplate(NAME, data, wrapping data..)
15660         // the outer template then get' applied with
15661         //     the store 'extra data'
15662         // and the body get's added to the
15663         //      roo-name="data" node?
15664         //      <span class='roo-tpl-{name}'></span> ?????
15665         
15666         
15667         
15668         this.clearSelections();
15669         this.el.update("");
15670         var html = [];
15671         var records = this.store.getRange();
15672         if(records.length < 1) {
15673             
15674             // is this valid??  = should it render a template??
15675             
15676             this.el.update(this.emptyText);
15677             return;
15678         }
15679         var el = this.el;
15680         if (this.dataName) {
15681             this.el.update(t.apply(this.store.meta)); //????
15682             el = this.el.child('.roo-tpl-' + this.dataName);
15683         }
15684         
15685         for(var i = 0, len = records.length; i < len; i++){
15686             var data = this.prepareData(records[i].data, i, records[i]);
15687             this.fireEvent("preparedata", this, data, i, records[i]);
15688             
15689             var d = Roo.apply({}, data);
15690             
15691             if(this.tickable){
15692                 Roo.apply(d, {'roo-id' : Roo.id()});
15693                 
15694                 var _this = this;
15695             
15696                 Roo.each(this.parent.item, function(item){
15697                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15698                         return;
15699                     }
15700                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15701                 });
15702             }
15703             
15704             html[html.length] = Roo.util.Format.trim(
15705                 this.dataName ?
15706                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15707                     t.apply(d)
15708             );
15709         }
15710         
15711         
15712         
15713         el.update(html.join(""));
15714         this.nodes = el.dom.childNodes;
15715         this.updateIndexes(0);
15716     },
15717     
15718
15719     /**
15720      * Function to override to reformat the data that is sent to
15721      * the template for each node.
15722      * DEPRICATED - use the preparedata event handler.
15723      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15724      * a JSON object for an UpdateManager bound view).
15725      */
15726     prepareData : function(data, index, record)
15727     {
15728         this.fireEvent("preparedata", this, data, index, record);
15729         return data;
15730     },
15731
15732     onUpdate : function(ds, record){
15733         // Roo.log('on update');   
15734         this.clearSelections();
15735         var index = this.store.indexOf(record);
15736         var n = this.nodes[index];
15737         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15738         n.parentNode.removeChild(n);
15739         this.updateIndexes(index, index);
15740     },
15741
15742     
15743     
15744 // --------- FIXME     
15745     onAdd : function(ds, records, index)
15746     {
15747         //Roo.log(['on Add', ds, records, index] );        
15748         this.clearSelections();
15749         if(this.nodes.length == 0){
15750             this.refresh();
15751             return;
15752         }
15753         var n = this.nodes[index];
15754         for(var i = 0, len = records.length; i < len; i++){
15755             var d = this.prepareData(records[i].data, i, records[i]);
15756             if(n){
15757                 this.tpl.insertBefore(n, d);
15758             }else{
15759                 
15760                 this.tpl.append(this.el, d);
15761             }
15762         }
15763         this.updateIndexes(index);
15764     },
15765
15766     onRemove : function(ds, record, index){
15767        // Roo.log('onRemove');
15768         this.clearSelections();
15769         var el = this.dataName  ?
15770             this.el.child('.roo-tpl-' + this.dataName) :
15771             this.el; 
15772         
15773         el.dom.removeChild(this.nodes[index]);
15774         this.updateIndexes(index);
15775     },
15776
15777     /**
15778      * Refresh an individual node.
15779      * @param {Number} index
15780      */
15781     refreshNode : function(index){
15782         this.onUpdate(this.store, this.store.getAt(index));
15783     },
15784
15785     updateIndexes : function(startIndex, endIndex){
15786         var ns = this.nodes;
15787         startIndex = startIndex || 0;
15788         endIndex = endIndex || ns.length - 1;
15789         for(var i = startIndex; i <= endIndex; i++){
15790             ns[i].nodeIndex = i;
15791         }
15792     },
15793
15794     /**
15795      * Changes the data store this view uses and refresh the view.
15796      * @param {Store} store
15797      */
15798     setStore : function(store, initial){
15799         if(!initial && this.store){
15800             this.store.un("datachanged", this.refresh);
15801             this.store.un("add", this.onAdd);
15802             this.store.un("remove", this.onRemove);
15803             this.store.un("update", this.onUpdate);
15804             this.store.un("clear", this.refresh);
15805             this.store.un("beforeload", this.onBeforeLoad);
15806             this.store.un("load", this.onLoad);
15807             this.store.un("loadexception", this.onLoad);
15808         }
15809         if(store){
15810           
15811             store.on("datachanged", this.refresh, this);
15812             store.on("add", this.onAdd, this);
15813             store.on("remove", this.onRemove, this);
15814             store.on("update", this.onUpdate, this);
15815             store.on("clear", this.refresh, this);
15816             store.on("beforeload", this.onBeforeLoad, this);
15817             store.on("load", this.onLoad, this);
15818             store.on("loadexception", this.onLoad, this);
15819         }
15820         
15821         if(store){
15822             this.refresh();
15823         }
15824     },
15825     /**
15826      * onbeforeLoad - masks the loading area.
15827      *
15828      */
15829     onBeforeLoad : function(store,opts)
15830     {
15831          //Roo.log('onBeforeLoad');   
15832         if (!opts.add) {
15833             this.el.update("");
15834         }
15835         this.el.mask(this.mask ? this.mask : "Loading" ); 
15836     },
15837     onLoad : function ()
15838     {
15839         this.el.unmask();
15840     },
15841     
15842
15843     /**
15844      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15845      * @param {HTMLElement} node
15846      * @return {HTMLElement} The template node
15847      */
15848     findItemFromChild : function(node){
15849         var el = this.dataName  ?
15850             this.el.child('.roo-tpl-' + this.dataName,true) :
15851             this.el.dom; 
15852         
15853         if(!node || node.parentNode == el){
15854                     return node;
15855             }
15856             var p = node.parentNode;
15857             while(p && p != el){
15858             if(p.parentNode == el){
15859                 return p;
15860             }
15861             p = p.parentNode;
15862         }
15863             return null;
15864     },
15865
15866     /** @ignore */
15867     onClick : function(e){
15868         var item = this.findItemFromChild(e.getTarget());
15869         if(item){
15870             var index = this.indexOf(item);
15871             if(this.onItemClick(item, index, e) !== false){
15872                 this.fireEvent("click", this, index, item, e);
15873             }
15874         }else{
15875             this.clearSelections();
15876         }
15877     },
15878
15879     /** @ignore */
15880     onContextMenu : function(e){
15881         var item = this.findItemFromChild(e.getTarget());
15882         if(item){
15883             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15884         }
15885     },
15886
15887     /** @ignore */
15888     onDblClick : function(e){
15889         var item = this.findItemFromChild(e.getTarget());
15890         if(item){
15891             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15892         }
15893     },
15894
15895     onItemClick : function(item, index, e)
15896     {
15897         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15898             return false;
15899         }
15900         if (this.toggleSelect) {
15901             var m = this.isSelected(item) ? 'unselect' : 'select';
15902             //Roo.log(m);
15903             var _t = this;
15904             _t[m](item, true, false);
15905             return true;
15906         }
15907         if(this.multiSelect || this.singleSelect){
15908             if(this.multiSelect && e.shiftKey && this.lastSelection){
15909                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15910             }else{
15911                 this.select(item, this.multiSelect && e.ctrlKey);
15912                 this.lastSelection = item;
15913             }
15914             
15915             if(!this.tickable){
15916                 e.preventDefault();
15917             }
15918             
15919         }
15920         return true;
15921     },
15922
15923     /**
15924      * Get the number of selected nodes.
15925      * @return {Number}
15926      */
15927     getSelectionCount : function(){
15928         return this.selections.length;
15929     },
15930
15931     /**
15932      * Get the currently selected nodes.
15933      * @return {Array} An array of HTMLElements
15934      */
15935     getSelectedNodes : function(){
15936         return this.selections;
15937     },
15938
15939     /**
15940      * Get the indexes of the selected nodes.
15941      * @return {Array}
15942      */
15943     getSelectedIndexes : function(){
15944         var indexes = [], s = this.selections;
15945         for(var i = 0, len = s.length; i < len; i++){
15946             indexes.push(s[i].nodeIndex);
15947         }
15948         return indexes;
15949     },
15950
15951     /**
15952      * Clear all selections
15953      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15954      */
15955     clearSelections : function(suppressEvent){
15956         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15957             this.cmp.elements = this.selections;
15958             this.cmp.removeClass(this.selectedClass);
15959             this.selections = [];
15960             if(!suppressEvent){
15961                 this.fireEvent("selectionchange", this, this.selections);
15962             }
15963         }
15964     },
15965
15966     /**
15967      * Returns true if the passed node is selected
15968      * @param {HTMLElement/Number} node The node or node index
15969      * @return {Boolean}
15970      */
15971     isSelected : function(node){
15972         var s = this.selections;
15973         if(s.length < 1){
15974             return false;
15975         }
15976         node = this.getNode(node);
15977         return s.indexOf(node) !== -1;
15978     },
15979
15980     /**
15981      * Selects nodes.
15982      * @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
15983      * @param {Boolean} keepExisting (optional) true to keep existing selections
15984      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15985      */
15986     select : function(nodeInfo, keepExisting, suppressEvent){
15987         if(nodeInfo instanceof Array){
15988             if(!keepExisting){
15989                 this.clearSelections(true);
15990             }
15991             for(var i = 0, len = nodeInfo.length; i < len; i++){
15992                 this.select(nodeInfo[i], true, true);
15993             }
15994             return;
15995         } 
15996         var node = this.getNode(nodeInfo);
15997         if(!node || this.isSelected(node)){
15998             return; // already selected.
15999         }
16000         if(!keepExisting){
16001             this.clearSelections(true);
16002         }
16003         
16004         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16005             Roo.fly(node).addClass(this.selectedClass);
16006             this.selections.push(node);
16007             if(!suppressEvent){
16008                 this.fireEvent("selectionchange", this, this.selections);
16009             }
16010         }
16011         
16012         
16013     },
16014       /**
16015      * Unselects nodes.
16016      * @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
16017      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16018      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16019      */
16020     unselect : function(nodeInfo, keepExisting, suppressEvent)
16021     {
16022         if(nodeInfo instanceof Array){
16023             Roo.each(this.selections, function(s) {
16024                 this.unselect(s, nodeInfo);
16025             }, this);
16026             return;
16027         }
16028         var node = this.getNode(nodeInfo);
16029         if(!node || !this.isSelected(node)){
16030             //Roo.log("not selected");
16031             return; // not selected.
16032         }
16033         // fireevent???
16034         var ns = [];
16035         Roo.each(this.selections, function(s) {
16036             if (s == node ) {
16037                 Roo.fly(node).removeClass(this.selectedClass);
16038
16039                 return;
16040             }
16041             ns.push(s);
16042         },this);
16043         
16044         this.selections= ns;
16045         this.fireEvent("selectionchange", this, this.selections);
16046     },
16047
16048     /**
16049      * Gets a template node.
16050      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16051      * @return {HTMLElement} The node or null if it wasn't found
16052      */
16053     getNode : function(nodeInfo){
16054         if(typeof nodeInfo == "string"){
16055             return document.getElementById(nodeInfo);
16056         }else if(typeof nodeInfo == "number"){
16057             return this.nodes[nodeInfo];
16058         }
16059         return nodeInfo;
16060     },
16061
16062     /**
16063      * Gets a range template nodes.
16064      * @param {Number} startIndex
16065      * @param {Number} endIndex
16066      * @return {Array} An array of nodes
16067      */
16068     getNodes : function(start, end){
16069         var ns = this.nodes;
16070         start = start || 0;
16071         end = typeof end == "undefined" ? ns.length - 1 : end;
16072         var nodes = [];
16073         if(start <= end){
16074             for(var i = start; i <= end; i++){
16075                 nodes.push(ns[i]);
16076             }
16077         } else{
16078             for(var i = start; i >= end; i--){
16079                 nodes.push(ns[i]);
16080             }
16081         }
16082         return nodes;
16083     },
16084
16085     /**
16086      * Finds the index of the passed node
16087      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16088      * @return {Number} The index of the node or -1
16089      */
16090     indexOf : function(node){
16091         node = this.getNode(node);
16092         if(typeof node.nodeIndex == "number"){
16093             return node.nodeIndex;
16094         }
16095         var ns = this.nodes;
16096         for(var i = 0, len = ns.length; i < len; i++){
16097             if(ns[i] == node){
16098                 return i;
16099             }
16100         }
16101         return -1;
16102     }
16103 });
16104 /*
16105  * - LGPL
16106  *
16107  * based on jquery fullcalendar
16108  * 
16109  */
16110
16111 Roo.bootstrap = Roo.bootstrap || {};
16112 /**
16113  * @class Roo.bootstrap.Calendar
16114  * @extends Roo.bootstrap.Component
16115  * Bootstrap Calendar class
16116  * @cfg {Boolean} loadMask (true|false) default false
16117  * @cfg {Object} header generate the user specific header of the calendar, default false
16118
16119  * @constructor
16120  * Create a new Container
16121  * @param {Object} config The config object
16122  */
16123
16124
16125
16126 Roo.bootstrap.Calendar = function(config){
16127     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16128      this.addEvents({
16129         /**
16130              * @event select
16131              * Fires when a date is selected
16132              * @param {DatePicker} this
16133              * @param {Date} date The selected date
16134              */
16135         'select': true,
16136         /**
16137              * @event monthchange
16138              * Fires when the displayed month changes 
16139              * @param {DatePicker} this
16140              * @param {Date} date The selected month
16141              */
16142         'monthchange': true,
16143         /**
16144              * @event evententer
16145              * Fires when mouse over an event
16146              * @param {Calendar} this
16147              * @param {event} Event
16148              */
16149         'evententer': true,
16150         /**
16151              * @event eventleave
16152              * Fires when the mouse leaves an
16153              * @param {Calendar} this
16154              * @param {event}
16155              */
16156         'eventleave': true,
16157         /**
16158              * @event eventclick
16159              * Fires when the mouse click an
16160              * @param {Calendar} this
16161              * @param {event}
16162              */
16163         'eventclick': true
16164         
16165     });
16166
16167 };
16168
16169 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16170     
16171      /**
16172      * @cfg {Number} startDay
16173      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16174      */
16175     startDay : 0,
16176     
16177     loadMask : false,
16178     
16179     header : false,
16180       
16181     getAutoCreate : function(){
16182         
16183         
16184         var fc_button = function(name, corner, style, content ) {
16185             return Roo.apply({},{
16186                 tag : 'span',
16187                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16188                          (corner.length ?
16189                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16190                             ''
16191                         ),
16192                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16193                 unselectable: 'on'
16194             });
16195         };
16196         
16197         var header = {};
16198         
16199         if(!this.header){
16200             header = {
16201                 tag : 'table',
16202                 cls : 'fc-header',
16203                 style : 'width:100%',
16204                 cn : [
16205                     {
16206                         tag: 'tr',
16207                         cn : [
16208                             {
16209                                 tag : 'td',
16210                                 cls : 'fc-header-left',
16211                                 cn : [
16212                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16213                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16214                                     { tag: 'span', cls: 'fc-header-space' },
16215                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16216
16217
16218                                 ]
16219                             },
16220
16221                             {
16222                                 tag : 'td',
16223                                 cls : 'fc-header-center',
16224                                 cn : [
16225                                     {
16226                                         tag: 'span',
16227                                         cls: 'fc-header-title',
16228                                         cn : {
16229                                             tag: 'H2',
16230                                             html : 'month / year'
16231                                         }
16232                                     }
16233
16234                                 ]
16235                             },
16236                             {
16237                                 tag : 'td',
16238                                 cls : 'fc-header-right',
16239                                 cn : [
16240                               /*      fc_button('month', 'left', '', 'month' ),
16241                                     fc_button('week', '', '', 'week' ),
16242                                     fc_button('day', 'right', '', 'day' )
16243                                 */    
16244
16245                                 ]
16246                             }
16247
16248                         ]
16249                     }
16250                 ]
16251             };
16252         }
16253         
16254         header = this.header;
16255         
16256        
16257         var cal_heads = function() {
16258             var ret = [];
16259             // fixme - handle this.
16260             
16261             for (var i =0; i < Date.dayNames.length; i++) {
16262                 var d = Date.dayNames[i];
16263                 ret.push({
16264                     tag: 'th',
16265                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16266                     html : d.substring(0,3)
16267                 });
16268                 
16269             }
16270             ret[0].cls += ' fc-first';
16271             ret[6].cls += ' fc-last';
16272             return ret;
16273         };
16274         var cal_cell = function(n) {
16275             return  {
16276                 tag: 'td',
16277                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16278                 cn : [
16279                     {
16280                         cn : [
16281                             {
16282                                 cls: 'fc-day-number',
16283                                 html: 'D'
16284                             },
16285                             {
16286                                 cls: 'fc-day-content',
16287                              
16288                                 cn : [
16289                                      {
16290                                         style: 'position: relative;' // height: 17px;
16291                                     }
16292                                 ]
16293                             }
16294                             
16295                             
16296                         ]
16297                     }
16298                 ]
16299                 
16300             }
16301         };
16302         var cal_rows = function() {
16303             
16304             var ret = [];
16305             for (var r = 0; r < 6; r++) {
16306                 var row= {
16307                     tag : 'tr',
16308                     cls : 'fc-week',
16309                     cn : []
16310                 };
16311                 
16312                 for (var i =0; i < Date.dayNames.length; i++) {
16313                     var d = Date.dayNames[i];
16314                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16315
16316                 }
16317                 row.cn[0].cls+=' fc-first';
16318                 row.cn[0].cn[0].style = 'min-height:90px';
16319                 row.cn[6].cls+=' fc-last';
16320                 ret.push(row);
16321                 
16322             }
16323             ret[0].cls += ' fc-first';
16324             ret[4].cls += ' fc-prev-last';
16325             ret[5].cls += ' fc-last';
16326             return ret;
16327             
16328         };
16329         
16330         var cal_table = {
16331             tag: 'table',
16332             cls: 'fc-border-separate',
16333             style : 'width:100%',
16334             cellspacing  : 0,
16335             cn : [
16336                 { 
16337                     tag: 'thead',
16338                     cn : [
16339                         { 
16340                             tag: 'tr',
16341                             cls : 'fc-first fc-last',
16342                             cn : cal_heads()
16343                         }
16344                     ]
16345                 },
16346                 { 
16347                     tag: 'tbody',
16348                     cn : cal_rows()
16349                 }
16350                   
16351             ]
16352         };
16353          
16354          var cfg = {
16355             cls : 'fc fc-ltr',
16356             cn : [
16357                 header,
16358                 {
16359                     cls : 'fc-content',
16360                     style : "position: relative;",
16361                     cn : [
16362                         {
16363                             cls : 'fc-view fc-view-month fc-grid',
16364                             style : 'position: relative',
16365                             unselectable : 'on',
16366                             cn : [
16367                                 {
16368                                     cls : 'fc-event-container',
16369                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16370                                 },
16371                                 cal_table
16372                             ]
16373                         }
16374                     ]
16375     
16376                 }
16377            ] 
16378             
16379         };
16380         
16381          
16382         
16383         return cfg;
16384     },
16385     
16386     
16387     initEvents : function()
16388     {
16389         if(!this.store){
16390             throw "can not find store for calendar";
16391         }
16392         
16393         var mark = {
16394             tag: "div",
16395             cls:"x-dlg-mask",
16396             style: "text-align:center",
16397             cn: [
16398                 {
16399                     tag: "div",
16400                     style: "background-color:white;width:50%;margin:250 auto",
16401                     cn: [
16402                         {
16403                             tag: "img",
16404                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16405                         },
16406                         {
16407                             tag: "span",
16408                             html: "Loading"
16409                         }
16410                         
16411                     ]
16412                 }
16413             ]
16414         };
16415         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16416         
16417         var size = this.el.select('.fc-content', true).first().getSize();
16418         this.maskEl.setSize(size.width, size.height);
16419         this.maskEl.enableDisplayMode("block");
16420         if(!this.loadMask){
16421             this.maskEl.hide();
16422         }
16423         
16424         this.store = Roo.factory(this.store, Roo.data);
16425         this.store.on('load', this.onLoad, this);
16426         this.store.on('beforeload', this.onBeforeLoad, this);
16427         
16428         this.resize();
16429         
16430         this.cells = this.el.select('.fc-day',true);
16431         //Roo.log(this.cells);
16432         this.textNodes = this.el.query('.fc-day-number');
16433         this.cells.addClassOnOver('fc-state-hover');
16434         
16435         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16436         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16437         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16438         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16439         
16440         this.on('monthchange', this.onMonthChange, this);
16441         
16442         this.update(new Date().clearTime());
16443     },
16444     
16445     resize : function() {
16446         var sz  = this.el.getSize();
16447         
16448         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16449         this.el.select('.fc-day-content div',true).setHeight(34);
16450     },
16451     
16452     
16453     // private
16454     showPrevMonth : function(e){
16455         this.update(this.activeDate.add("mo", -1));
16456     },
16457     showToday : function(e){
16458         this.update(new Date().clearTime());
16459     },
16460     // private
16461     showNextMonth : function(e){
16462         this.update(this.activeDate.add("mo", 1));
16463     },
16464
16465     // private
16466     showPrevYear : function(){
16467         this.update(this.activeDate.add("y", -1));
16468     },
16469
16470     // private
16471     showNextYear : function(){
16472         this.update(this.activeDate.add("y", 1));
16473     },
16474
16475     
16476    // private
16477     update : function(date)
16478     {
16479         var vd = this.activeDate;
16480         this.activeDate = date;
16481 //        if(vd && this.el){
16482 //            var t = date.getTime();
16483 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16484 //                Roo.log('using add remove');
16485 //                
16486 //                this.fireEvent('monthchange', this, date);
16487 //                
16488 //                this.cells.removeClass("fc-state-highlight");
16489 //                this.cells.each(function(c){
16490 //                   if(c.dateValue == t){
16491 //                       c.addClass("fc-state-highlight");
16492 //                       setTimeout(function(){
16493 //                            try{c.dom.firstChild.focus();}catch(e){}
16494 //                       }, 50);
16495 //                       return false;
16496 //                   }
16497 //                   return true;
16498 //                });
16499 //                return;
16500 //            }
16501 //        }
16502         
16503         var days = date.getDaysInMonth();
16504         
16505         var firstOfMonth = date.getFirstDateOfMonth();
16506         var startingPos = firstOfMonth.getDay()-this.startDay;
16507         
16508         if(startingPos < this.startDay){
16509             startingPos += 7;
16510         }
16511         
16512         var pm = date.add(Date.MONTH, -1);
16513         var prevStart = pm.getDaysInMonth()-startingPos;
16514 //        
16515         this.cells = this.el.select('.fc-day',true);
16516         this.textNodes = this.el.query('.fc-day-number');
16517         this.cells.addClassOnOver('fc-state-hover');
16518         
16519         var cells = this.cells.elements;
16520         var textEls = this.textNodes;
16521         
16522         Roo.each(cells, function(cell){
16523             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16524         });
16525         
16526         days += startingPos;
16527
16528         // convert everything to numbers so it's fast
16529         var day = 86400000;
16530         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16531         //Roo.log(d);
16532         //Roo.log(pm);
16533         //Roo.log(prevStart);
16534         
16535         var today = new Date().clearTime().getTime();
16536         var sel = date.clearTime().getTime();
16537         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16538         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16539         var ddMatch = this.disabledDatesRE;
16540         var ddText = this.disabledDatesText;
16541         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16542         var ddaysText = this.disabledDaysText;
16543         var format = this.format;
16544         
16545         var setCellClass = function(cal, cell){
16546             cell.row = 0;
16547             cell.events = [];
16548             cell.more = [];
16549             //Roo.log('set Cell Class');
16550             cell.title = "";
16551             var t = d.getTime();
16552             
16553             //Roo.log(d);
16554             
16555             cell.dateValue = t;
16556             if(t == today){
16557                 cell.className += " fc-today";
16558                 cell.className += " fc-state-highlight";
16559                 cell.title = cal.todayText;
16560             }
16561             if(t == sel){
16562                 // disable highlight in other month..
16563                 //cell.className += " fc-state-highlight";
16564                 
16565             }
16566             // disabling
16567             if(t < min) {
16568                 cell.className = " fc-state-disabled";
16569                 cell.title = cal.minText;
16570                 return;
16571             }
16572             if(t > max) {
16573                 cell.className = " fc-state-disabled";
16574                 cell.title = cal.maxText;
16575                 return;
16576             }
16577             if(ddays){
16578                 if(ddays.indexOf(d.getDay()) != -1){
16579                     cell.title = ddaysText;
16580                     cell.className = " fc-state-disabled";
16581                 }
16582             }
16583             if(ddMatch && format){
16584                 var fvalue = d.dateFormat(format);
16585                 if(ddMatch.test(fvalue)){
16586                     cell.title = ddText.replace("%0", fvalue);
16587                     cell.className = " fc-state-disabled";
16588                 }
16589             }
16590             
16591             if (!cell.initialClassName) {
16592                 cell.initialClassName = cell.dom.className;
16593             }
16594             
16595             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16596         };
16597
16598         var i = 0;
16599         
16600         for(; i < startingPos; i++) {
16601             textEls[i].innerHTML = (++prevStart);
16602             d.setDate(d.getDate()+1);
16603             
16604             cells[i].className = "fc-past fc-other-month";
16605             setCellClass(this, cells[i]);
16606         }
16607         
16608         var intDay = 0;
16609         
16610         for(; i < days; i++){
16611             intDay = i - startingPos + 1;
16612             textEls[i].innerHTML = (intDay);
16613             d.setDate(d.getDate()+1);
16614             
16615             cells[i].className = ''; // "x-date-active";
16616             setCellClass(this, cells[i]);
16617         }
16618         var extraDays = 0;
16619         
16620         for(; i < 42; i++) {
16621             textEls[i].innerHTML = (++extraDays);
16622             d.setDate(d.getDate()+1);
16623             
16624             cells[i].className = "fc-future fc-other-month";
16625             setCellClass(this, cells[i]);
16626         }
16627         
16628         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16629         
16630         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16631         
16632         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16633         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16634         
16635         if(totalRows != 6){
16636             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16637             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16638         }
16639         
16640         this.fireEvent('monthchange', this, date);
16641         
16642         
16643         /*
16644         if(!this.internalRender){
16645             var main = this.el.dom.firstChild;
16646             var w = main.offsetWidth;
16647             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16648             Roo.fly(main).setWidth(w);
16649             this.internalRender = true;
16650             // opera does not respect the auto grow header center column
16651             // then, after it gets a width opera refuses to recalculate
16652             // without a second pass
16653             if(Roo.isOpera && !this.secondPass){
16654                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16655                 this.secondPass = true;
16656                 this.update.defer(10, this, [date]);
16657             }
16658         }
16659         */
16660         
16661     },
16662     
16663     findCell : function(dt) {
16664         dt = dt.clearTime().getTime();
16665         var ret = false;
16666         this.cells.each(function(c){
16667             //Roo.log("check " +c.dateValue + '?=' + dt);
16668             if(c.dateValue == dt){
16669                 ret = c;
16670                 return false;
16671             }
16672             return true;
16673         });
16674         
16675         return ret;
16676     },
16677     
16678     findCells : function(ev) {
16679         var s = ev.start.clone().clearTime().getTime();
16680        // Roo.log(s);
16681         var e= ev.end.clone().clearTime().getTime();
16682        // Roo.log(e);
16683         var ret = [];
16684         this.cells.each(function(c){
16685              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16686             
16687             if(c.dateValue > e){
16688                 return ;
16689             }
16690             if(c.dateValue < s){
16691                 return ;
16692             }
16693             ret.push(c);
16694         });
16695         
16696         return ret;    
16697     },
16698     
16699 //    findBestRow: function(cells)
16700 //    {
16701 //        var ret = 0;
16702 //        
16703 //        for (var i =0 ; i < cells.length;i++) {
16704 //            ret  = Math.max(cells[i].rows || 0,ret);
16705 //        }
16706 //        return ret;
16707 //        
16708 //    },
16709     
16710     
16711     addItem : function(ev)
16712     {
16713         // look for vertical location slot in
16714         var cells = this.findCells(ev);
16715         
16716 //        ev.row = this.findBestRow(cells);
16717         
16718         // work out the location.
16719         
16720         var crow = false;
16721         var rows = [];
16722         for(var i =0; i < cells.length; i++) {
16723             
16724             cells[i].row = cells[0].row;
16725             
16726             if(i == 0){
16727                 cells[i].row = cells[i].row + 1;
16728             }
16729             
16730             if (!crow) {
16731                 crow = {
16732                     start : cells[i],
16733                     end :  cells[i]
16734                 };
16735                 continue;
16736             }
16737             if (crow.start.getY() == cells[i].getY()) {
16738                 // on same row.
16739                 crow.end = cells[i];
16740                 continue;
16741             }
16742             // different row.
16743             rows.push(crow);
16744             crow = {
16745                 start: cells[i],
16746                 end : cells[i]
16747             };
16748             
16749         }
16750         
16751         rows.push(crow);
16752         ev.els = [];
16753         ev.rows = rows;
16754         ev.cells = cells;
16755         
16756         cells[0].events.push(ev);
16757         
16758         this.calevents.push(ev);
16759     },
16760     
16761     clearEvents: function() {
16762         
16763         if(!this.calevents){
16764             return;
16765         }
16766         
16767         Roo.each(this.cells.elements, function(c){
16768             c.row = 0;
16769             c.events = [];
16770             c.more = [];
16771         });
16772         
16773         Roo.each(this.calevents, function(e) {
16774             Roo.each(e.els, function(el) {
16775                 el.un('mouseenter' ,this.onEventEnter, this);
16776                 el.un('mouseleave' ,this.onEventLeave, this);
16777                 el.remove();
16778             },this);
16779         },this);
16780         
16781         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16782             e.remove();
16783         });
16784         
16785     },
16786     
16787     renderEvents: function()
16788     {   
16789         var _this = this;
16790         
16791         this.cells.each(function(c) {
16792             
16793             if(c.row < 5){
16794                 return;
16795             }
16796             
16797             var ev = c.events;
16798             
16799             var r = 4;
16800             if(c.row != c.events.length){
16801                 r = 4 - (4 - (c.row - c.events.length));
16802             }
16803             
16804             c.events = ev.slice(0, r);
16805             c.more = ev.slice(r);
16806             
16807             if(c.more.length && c.more.length == 1){
16808                 c.events.push(c.more.pop());
16809             }
16810             
16811             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16812             
16813         });
16814             
16815         this.cells.each(function(c) {
16816             
16817             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16818             
16819             
16820             for (var e = 0; e < c.events.length; e++){
16821                 var ev = c.events[e];
16822                 var rows = ev.rows;
16823                 
16824                 for(var i = 0; i < rows.length; i++) {
16825                 
16826                     // how many rows should it span..
16827
16828                     var  cfg = {
16829                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16830                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16831
16832                         unselectable : "on",
16833                         cn : [
16834                             {
16835                                 cls: 'fc-event-inner',
16836                                 cn : [
16837     //                                {
16838     //                                  tag:'span',
16839     //                                  cls: 'fc-event-time',
16840     //                                  html : cells.length > 1 ? '' : ev.time
16841     //                                },
16842                                     {
16843                                       tag:'span',
16844                                       cls: 'fc-event-title',
16845                                       html : String.format('{0}', ev.title)
16846                                     }
16847
16848
16849                                 ]
16850                             },
16851                             {
16852                                 cls: 'ui-resizable-handle ui-resizable-e',
16853                                 html : '&nbsp;&nbsp;&nbsp'
16854                             }
16855
16856                         ]
16857                     };
16858
16859                     if (i == 0) {
16860                         cfg.cls += ' fc-event-start';
16861                     }
16862                     if ((i+1) == rows.length) {
16863                         cfg.cls += ' fc-event-end';
16864                     }
16865
16866                     var ctr = _this.el.select('.fc-event-container',true).first();
16867                     var cg = ctr.createChild(cfg);
16868
16869                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16870                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16871
16872                     var r = (c.more.length) ? 1 : 0;
16873                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16874                     cg.setWidth(ebox.right - sbox.x -2);
16875
16876                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16877                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16878                     cg.on('click', _this.onEventClick, _this, ev);
16879
16880                     ev.els.push(cg);
16881                     
16882                 }
16883                 
16884             }
16885             
16886             
16887             if(c.more.length){
16888                 var  cfg = {
16889                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16890                     style : 'position: absolute',
16891                     unselectable : "on",
16892                     cn : [
16893                         {
16894                             cls: 'fc-event-inner',
16895                             cn : [
16896                                 {
16897                                   tag:'span',
16898                                   cls: 'fc-event-title',
16899                                   html : 'More'
16900                                 }
16901
16902
16903                             ]
16904                         },
16905                         {
16906                             cls: 'ui-resizable-handle ui-resizable-e',
16907                             html : '&nbsp;&nbsp;&nbsp'
16908                         }
16909
16910                     ]
16911                 };
16912
16913                 var ctr = _this.el.select('.fc-event-container',true).first();
16914                 var cg = ctr.createChild(cfg);
16915
16916                 var sbox = c.select('.fc-day-content',true).first().getBox();
16917                 var ebox = c.select('.fc-day-content',true).first().getBox();
16918                 //Roo.log(cg);
16919                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16920                 cg.setWidth(ebox.right - sbox.x -2);
16921
16922                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16923                 
16924             }
16925             
16926         });
16927         
16928         
16929         
16930     },
16931     
16932     onEventEnter: function (e, el,event,d) {
16933         this.fireEvent('evententer', this, el, event);
16934     },
16935     
16936     onEventLeave: function (e, el,event,d) {
16937         this.fireEvent('eventleave', this, el, event);
16938     },
16939     
16940     onEventClick: function (e, el,event,d) {
16941         this.fireEvent('eventclick', this, el, event);
16942     },
16943     
16944     onMonthChange: function () {
16945         this.store.load();
16946     },
16947     
16948     onMoreEventClick: function(e, el, more)
16949     {
16950         var _this = this;
16951         
16952         this.calpopover.placement = 'right';
16953         this.calpopover.setTitle('More');
16954         
16955         this.calpopover.setContent('');
16956         
16957         var ctr = this.calpopover.el.select('.popover-content', true).first();
16958         
16959         Roo.each(more, function(m){
16960             var cfg = {
16961                 cls : 'fc-event-hori fc-event-draggable',
16962                 html : m.title
16963             };
16964             var cg = ctr.createChild(cfg);
16965             
16966             cg.on('click', _this.onEventClick, _this, m);
16967         });
16968         
16969         this.calpopover.show(el);
16970         
16971         
16972     },
16973     
16974     onLoad: function () 
16975     {   
16976         this.calevents = [];
16977         var cal = this;
16978         
16979         if(this.store.getCount() > 0){
16980             this.store.data.each(function(d){
16981                cal.addItem({
16982                     id : d.data.id,
16983                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16984                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16985                     time : d.data.start_time,
16986                     title : d.data.title,
16987                     description : d.data.description,
16988                     venue : d.data.venue
16989                 });
16990             });
16991         }
16992         
16993         this.renderEvents();
16994         
16995         if(this.calevents.length && this.loadMask){
16996             this.maskEl.hide();
16997         }
16998     },
16999     
17000     onBeforeLoad: function()
17001     {
17002         this.clearEvents();
17003         if(this.loadMask){
17004             this.maskEl.show();
17005         }
17006     }
17007 });
17008
17009  
17010  /*
17011  * - LGPL
17012  *
17013  * element
17014  * 
17015  */
17016
17017 /**
17018  * @class Roo.bootstrap.Popover
17019  * @extends Roo.bootstrap.Component
17020  * Bootstrap Popover class
17021  * @cfg {String} html contents of the popover   (or false to use children..)
17022  * @cfg {String} title of popover (or false to hide)
17023  * @cfg {String} placement how it is placed
17024  * @cfg {String} trigger click || hover (or false to trigger manually)
17025  * @cfg {String} over what (parent or false to trigger manually.)
17026  * @cfg {Number} delay - delay before showing
17027  
17028  * @constructor
17029  * Create a new Popover
17030  * @param {Object} config The config object
17031  */
17032
17033 Roo.bootstrap.Popover = function(config){
17034     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17035     
17036     this.addEvents({
17037         // raw events
17038          /**
17039          * @event show
17040          * After the popover show
17041          * 
17042          * @param {Roo.bootstrap.Popover} this
17043          */
17044         "show" : true,
17045         /**
17046          * @event hide
17047          * After the popover hide
17048          * 
17049          * @param {Roo.bootstrap.Popover} this
17050          */
17051         "hide" : true
17052     });
17053 };
17054
17055 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17056     
17057     title: 'Fill in a title',
17058     html: false,
17059     
17060     placement : 'right',
17061     trigger : 'hover', // hover
17062     
17063     delay : 0,
17064     
17065     over: 'parent',
17066     
17067     can_build_overlaid : false,
17068     
17069     getChildContainer : function()
17070     {
17071         return this.el.select('.popover-content',true).first();
17072     },
17073     
17074     getAutoCreate : function(){
17075          
17076         var cfg = {
17077            cls : 'popover roo-dynamic',
17078            style: 'display:block',
17079            cn : [
17080                 {
17081                     cls : 'arrow'
17082                 },
17083                 {
17084                     cls : 'popover-inner',
17085                     cn : [
17086                         {
17087                             tag: 'h3',
17088                             cls: 'popover-title',
17089                             html : this.title
17090                         },
17091                         {
17092                             cls : 'popover-content',
17093                             html : this.html
17094                         }
17095                     ]
17096                     
17097                 }
17098            ]
17099         };
17100         
17101         return cfg;
17102     },
17103     setTitle: function(str)
17104     {
17105         this.title = str;
17106         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17107     },
17108     setContent: function(str)
17109     {
17110         this.html = str;
17111         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17112     },
17113     // as it get's added to the bottom of the page.
17114     onRender : function(ct, position)
17115     {
17116         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17117         if(!this.el){
17118             var cfg = Roo.apply({},  this.getAutoCreate());
17119             cfg.id = Roo.id();
17120             
17121             if (this.cls) {
17122                 cfg.cls += ' ' + this.cls;
17123             }
17124             if (this.style) {
17125                 cfg.style = this.style;
17126             }
17127             //Roo.log("adding to ");
17128             this.el = Roo.get(document.body).createChild(cfg, position);
17129 //            Roo.log(this.el);
17130         }
17131         this.initEvents();
17132     },
17133     
17134     initEvents : function()
17135     {
17136         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17137         this.el.enableDisplayMode('block');
17138         this.el.hide();
17139         if (this.over === false) {
17140             return; 
17141         }
17142         if (this.triggers === false) {
17143             return;
17144         }
17145         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17146         var triggers = this.trigger ? this.trigger.split(' ') : [];
17147         Roo.each(triggers, function(trigger) {
17148         
17149             if (trigger == 'click') {
17150                 on_el.on('click', this.toggle, this);
17151             } else if (trigger != 'manual') {
17152                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17153                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17154       
17155                 on_el.on(eventIn  ,this.enter, this);
17156                 on_el.on(eventOut, this.leave, this);
17157             }
17158         }, this);
17159         
17160     },
17161     
17162     
17163     // private
17164     timeout : null,
17165     hoverState : null,
17166     
17167     toggle : function () {
17168         this.hoverState == 'in' ? this.leave() : this.enter();
17169     },
17170     
17171     enter : function () {
17172         
17173         clearTimeout(this.timeout);
17174     
17175         this.hoverState = 'in';
17176     
17177         if (!this.delay || !this.delay.show) {
17178             this.show();
17179             return;
17180         }
17181         var _t = this;
17182         this.timeout = setTimeout(function () {
17183             if (_t.hoverState == 'in') {
17184                 _t.show();
17185             }
17186         }, this.delay.show)
17187     },
17188     
17189     leave : function() {
17190         clearTimeout(this.timeout);
17191     
17192         this.hoverState = 'out';
17193     
17194         if (!this.delay || !this.delay.hide) {
17195             this.hide();
17196             return;
17197         }
17198         var _t = this;
17199         this.timeout = setTimeout(function () {
17200             if (_t.hoverState == 'out') {
17201                 _t.hide();
17202             }
17203         }, this.delay.hide)
17204     },
17205     
17206     show : function (on_el)
17207     {
17208         if (!on_el) {
17209             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17210         }
17211         
17212         // set content.
17213         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17214         if (this.html !== false) {
17215             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17216         }
17217         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17218         if (!this.title.length) {
17219             this.el.select('.popover-title',true).hide();
17220         }
17221         
17222         var placement = typeof this.placement == 'function' ?
17223             this.placement.call(this, this.el, on_el) :
17224             this.placement;
17225             
17226         var autoToken = /\s?auto?\s?/i;
17227         var autoPlace = autoToken.test(placement);
17228         if (autoPlace) {
17229             placement = placement.replace(autoToken, '') || 'top';
17230         }
17231         
17232         //this.el.detach()
17233         //this.el.setXY([0,0]);
17234         this.el.show();
17235         this.el.dom.style.display='block';
17236         this.el.addClass(placement);
17237         
17238         //this.el.appendTo(on_el);
17239         
17240         var p = this.getPosition();
17241         var box = this.el.getBox();
17242         
17243         if (autoPlace) {
17244             // fixme..
17245         }
17246         var align = Roo.bootstrap.Popover.alignment[placement];
17247         this.el.alignTo(on_el, align[0],align[1]);
17248         //var arrow = this.el.select('.arrow',true).first();
17249         //arrow.set(align[2], 
17250         
17251         this.el.addClass('in');
17252         
17253         
17254         if (this.el.hasClass('fade')) {
17255             // fade it?
17256         }
17257         
17258         this.hoverState = 'in';
17259         
17260         this.fireEvent('show', this);
17261         
17262     },
17263     hide : function()
17264     {
17265         this.el.setXY([0,0]);
17266         this.el.removeClass('in');
17267         this.el.hide();
17268         this.hoverState = null;
17269         
17270         this.fireEvent('hide', this);
17271     }
17272     
17273 });
17274
17275 Roo.bootstrap.Popover.alignment = {
17276     'left' : ['r-l', [-10,0], 'right'],
17277     'right' : ['l-r', [10,0], 'left'],
17278     'bottom' : ['t-b', [0,10], 'top'],
17279     'top' : [ 'b-t', [0,-10], 'bottom']
17280 };
17281
17282  /*
17283  * - LGPL
17284  *
17285  * Progress
17286  * 
17287  */
17288
17289 /**
17290  * @class Roo.bootstrap.Progress
17291  * @extends Roo.bootstrap.Component
17292  * Bootstrap Progress class
17293  * @cfg {Boolean} striped striped of the progress bar
17294  * @cfg {Boolean} active animated of the progress bar
17295  * 
17296  * 
17297  * @constructor
17298  * Create a new Progress
17299  * @param {Object} config The config object
17300  */
17301
17302 Roo.bootstrap.Progress = function(config){
17303     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17304 };
17305
17306 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17307     
17308     striped : false,
17309     active: false,
17310     
17311     getAutoCreate : function(){
17312         var cfg = {
17313             tag: 'div',
17314             cls: 'progress'
17315         };
17316         
17317         
17318         if(this.striped){
17319             cfg.cls += ' progress-striped';
17320         }
17321       
17322         if(this.active){
17323             cfg.cls += ' active';
17324         }
17325         
17326         
17327         return cfg;
17328     }
17329    
17330 });
17331
17332  
17333
17334  /*
17335  * - LGPL
17336  *
17337  * ProgressBar
17338  * 
17339  */
17340
17341 /**
17342  * @class Roo.bootstrap.ProgressBar
17343  * @extends Roo.bootstrap.Component
17344  * Bootstrap ProgressBar class
17345  * @cfg {Number} aria_valuenow aria-value now
17346  * @cfg {Number} aria_valuemin aria-value min
17347  * @cfg {Number} aria_valuemax aria-value max
17348  * @cfg {String} label label for the progress bar
17349  * @cfg {String} panel (success | info | warning | danger )
17350  * @cfg {String} role role of the progress bar
17351  * @cfg {String} sr_only text
17352  * 
17353  * 
17354  * @constructor
17355  * Create a new ProgressBar
17356  * @param {Object} config The config object
17357  */
17358
17359 Roo.bootstrap.ProgressBar = function(config){
17360     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17361 };
17362
17363 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17364     
17365     aria_valuenow : 0,
17366     aria_valuemin : 0,
17367     aria_valuemax : 100,
17368     label : false,
17369     panel : false,
17370     role : false,
17371     sr_only: false,
17372     
17373     getAutoCreate : function()
17374     {
17375         
17376         var cfg = {
17377             tag: 'div',
17378             cls: 'progress-bar',
17379             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17380         };
17381         
17382         if(this.sr_only){
17383             cfg.cn = {
17384                 tag: 'span',
17385                 cls: 'sr-only',
17386                 html: this.sr_only
17387             }
17388         }
17389         
17390         if(this.role){
17391             cfg.role = this.role;
17392         }
17393         
17394         if(this.aria_valuenow){
17395             cfg['aria-valuenow'] = this.aria_valuenow;
17396         }
17397         
17398         if(this.aria_valuemin){
17399             cfg['aria-valuemin'] = this.aria_valuemin;
17400         }
17401         
17402         if(this.aria_valuemax){
17403             cfg['aria-valuemax'] = this.aria_valuemax;
17404         }
17405         
17406         if(this.label && !this.sr_only){
17407             cfg.html = this.label;
17408         }
17409         
17410         if(this.panel){
17411             cfg.cls += ' progress-bar-' + this.panel;
17412         }
17413         
17414         return cfg;
17415     },
17416     
17417     update : function(aria_valuenow)
17418     {
17419         this.aria_valuenow = aria_valuenow;
17420         
17421         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17422     }
17423    
17424 });
17425
17426  
17427
17428  /*
17429  * - LGPL
17430  *
17431  * column
17432  * 
17433  */
17434
17435 /**
17436  * @class Roo.bootstrap.TabGroup
17437  * @extends Roo.bootstrap.Column
17438  * Bootstrap Column class
17439  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17440  * @cfg {Boolean} carousel true to make the group behave like a carousel
17441  * @cfg {Boolean} bullets show bullets for the panels
17442  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17443  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17444  * @cfg {Boolean} showarrow (true|false) show arrow default true
17445  * 
17446  * @constructor
17447  * Create a new TabGroup
17448  * @param {Object} config The config object
17449  */
17450
17451 Roo.bootstrap.TabGroup = function(config){
17452     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17453     if (!this.navId) {
17454         this.navId = Roo.id();
17455     }
17456     this.tabs = [];
17457     Roo.bootstrap.TabGroup.register(this);
17458     
17459 };
17460
17461 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17462     
17463     carousel : false,
17464     transition : false,
17465     bullets : 0,
17466     timer : 0,
17467     autoslide : false,
17468     slideFn : false,
17469     slideOnTouch : false,
17470     showarrow : true,
17471     
17472     getAutoCreate : function()
17473     {
17474         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17475         
17476         cfg.cls += ' tab-content';
17477         
17478         if (this.carousel) {
17479             cfg.cls += ' carousel slide';
17480             
17481             cfg.cn = [{
17482                cls : 'carousel-inner',
17483                cn : []
17484             }];
17485         
17486             if(this.bullets  && !Roo.isTouch){
17487                 
17488                 var bullets = {
17489                     cls : 'carousel-bullets',
17490                     cn : []
17491                 };
17492                
17493                 if(this.bullets_cls){
17494                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17495                 }
17496                 
17497                 bullets.cn.push({
17498                     cls : 'clear'
17499                 });
17500                 
17501                 cfg.cn[0].cn.push(bullets);
17502             }
17503             
17504             if(this.showarrow){
17505                 cfg.cn[0].cn.push({
17506                     tag : 'div',
17507                     class : 'carousel-arrow',
17508                     cn : [
17509                         {
17510                             tag : 'div',
17511                             class : 'carousel-prev',
17512                             cn : [
17513                                 {
17514                                     tag : 'i',
17515                                     class : 'fa fa-chevron-left'
17516                                 }
17517                             ]
17518                         },
17519                         {
17520                             tag : 'div',
17521                             class : 'carousel-next',
17522                             cn : [
17523                                 {
17524                                     tag : 'i',
17525                                     class : 'fa fa-chevron-right'
17526                                 }
17527                             ]
17528                         }
17529                     ]
17530                 });
17531             }
17532             
17533         }
17534         
17535         return cfg;
17536     },
17537     
17538     initEvents:  function()
17539     {
17540 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17541 //            this.el.on("touchstart", this.onTouchStart, this);
17542 //        }
17543         
17544         if(this.autoslide){
17545             var _this = this;
17546             
17547             this.slideFn = window.setInterval(function() {
17548                 _this.showPanelNext();
17549             }, this.timer);
17550         }
17551         
17552         if(this.showarrow){
17553             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17554             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17555         }
17556         
17557         
17558     },
17559     
17560 //    onTouchStart : function(e, el, o)
17561 //    {
17562 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17563 //            return;
17564 //        }
17565 //        
17566 //        this.showPanelNext();
17567 //    },
17568     
17569     
17570     getChildContainer : function()
17571     {
17572         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17573     },
17574     
17575     /**
17576     * register a Navigation item
17577     * @param {Roo.bootstrap.NavItem} the navitem to add
17578     */
17579     register : function(item)
17580     {
17581         this.tabs.push( item);
17582         item.navId = this.navId; // not really needed..
17583         this.addBullet();
17584     
17585     },
17586     
17587     getActivePanel : function()
17588     {
17589         var r = false;
17590         Roo.each(this.tabs, function(t) {
17591             if (t.active) {
17592                 r = t;
17593                 return false;
17594             }
17595             return null;
17596         });
17597         return r;
17598         
17599     },
17600     getPanelByName : function(n)
17601     {
17602         var r = false;
17603         Roo.each(this.tabs, function(t) {
17604             if (t.tabId == n) {
17605                 r = t;
17606                 return false;
17607             }
17608             return null;
17609         });
17610         return r;
17611     },
17612     indexOfPanel : function(p)
17613     {
17614         var r = false;
17615         Roo.each(this.tabs, function(t,i) {
17616             if (t.tabId == p.tabId) {
17617                 r = i;
17618                 return false;
17619             }
17620             return null;
17621         });
17622         return r;
17623     },
17624     /**
17625      * show a specific panel
17626      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17627      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17628      */
17629     showPanel : function (pan)
17630     {
17631         if(this.transition || typeof(pan) == 'undefined'){
17632             Roo.log("waiting for the transitionend");
17633             return;
17634         }
17635         
17636         if (typeof(pan) == 'number') {
17637             pan = this.tabs[pan];
17638         }
17639         
17640         if (typeof(pan) == 'string') {
17641             pan = this.getPanelByName(pan);
17642         }
17643         
17644         var cur = this.getActivePanel();
17645         
17646         if(!pan || !cur){
17647             Roo.log('pan or acitve pan is undefined');
17648             return false;
17649         }
17650         
17651         if (pan.tabId == this.getActivePanel().tabId) {
17652             return true;
17653         }
17654         
17655         if (false === cur.fireEvent('beforedeactivate')) {
17656             return false;
17657         }
17658         
17659         if(this.bullets > 0 && !Roo.isTouch){
17660             this.setActiveBullet(this.indexOfPanel(pan));
17661         }
17662         
17663         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17664             
17665             this.transition = true;
17666             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17667             var lr = dir == 'next' ? 'left' : 'right';
17668             pan.el.addClass(dir); // or prev
17669             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17670             cur.el.addClass(lr); // or right
17671             pan.el.addClass(lr);
17672             
17673             var _this = this;
17674             cur.el.on('transitionend', function() {
17675                 Roo.log("trans end?");
17676                 
17677                 pan.el.removeClass([lr,dir]);
17678                 pan.setActive(true);
17679                 
17680                 cur.el.removeClass([lr]);
17681                 cur.setActive(false);
17682                 
17683                 _this.transition = false;
17684                 
17685             }, this, { single:  true } );
17686             
17687             return true;
17688         }
17689         
17690         cur.setActive(false);
17691         pan.setActive(true);
17692         
17693         return true;
17694         
17695     },
17696     showPanelNext : function()
17697     {
17698         var i = this.indexOfPanel(this.getActivePanel());
17699         
17700         if (i >= this.tabs.length - 1 && !this.autoslide) {
17701             return;
17702         }
17703         
17704         if (i >= this.tabs.length - 1 && this.autoslide) {
17705             i = -1;
17706         }
17707         
17708         this.showPanel(this.tabs[i+1]);
17709     },
17710     
17711     showPanelPrev : function()
17712     {
17713         var i = this.indexOfPanel(this.getActivePanel());
17714         
17715         if (i  < 1 && !this.autoslide) {
17716             return;
17717         }
17718         
17719         if (i < 1 && this.autoslide) {
17720             i = this.tabs.length;
17721         }
17722         
17723         this.showPanel(this.tabs[i-1]);
17724     },
17725     
17726     
17727     addBullet: function()
17728     {
17729         if(!this.bullets || Roo.isTouch){
17730             return;
17731         }
17732         var ctr = this.el.select('.carousel-bullets',true).first();
17733         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17734         var bullet = ctr.createChild({
17735             cls : 'bullet bullet-' + i
17736         },ctr.dom.lastChild);
17737         
17738         
17739         var _this = this;
17740         
17741         bullet.on('click', (function(e, el, o, ii, t){
17742
17743             e.preventDefault();
17744
17745             this.showPanel(ii);
17746
17747             if(this.autoslide && this.slideFn){
17748                 clearInterval(this.slideFn);
17749                 this.slideFn = window.setInterval(function() {
17750                     _this.showPanelNext();
17751                 }, this.timer);
17752             }
17753
17754         }).createDelegate(this, [i, bullet], true));
17755                 
17756         
17757     },
17758      
17759     setActiveBullet : function(i)
17760     {
17761         if(Roo.isTouch){
17762             return;
17763         }
17764         
17765         Roo.each(this.el.select('.bullet', true).elements, function(el){
17766             el.removeClass('selected');
17767         });
17768
17769         var bullet = this.el.select('.bullet-' + i, true).first();
17770         
17771         if(!bullet){
17772             return;
17773         }
17774         
17775         bullet.addClass('selected');
17776     }
17777     
17778     
17779   
17780 });
17781
17782  
17783
17784  
17785  
17786 Roo.apply(Roo.bootstrap.TabGroup, {
17787     
17788     groups: {},
17789      /**
17790     * register a Navigation Group
17791     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17792     */
17793     register : function(navgrp)
17794     {
17795         this.groups[navgrp.navId] = navgrp;
17796         
17797     },
17798     /**
17799     * fetch a Navigation Group based on the navigation ID
17800     * if one does not exist , it will get created.
17801     * @param {string} the navgroup to add
17802     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17803     */
17804     get: function(navId) {
17805         if (typeof(this.groups[navId]) == 'undefined') {
17806             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17807         }
17808         return this.groups[navId] ;
17809     }
17810     
17811     
17812     
17813 });
17814
17815  /*
17816  * - LGPL
17817  *
17818  * TabPanel
17819  * 
17820  */
17821
17822 /**
17823  * @class Roo.bootstrap.TabPanel
17824  * @extends Roo.bootstrap.Component
17825  * Bootstrap TabPanel class
17826  * @cfg {Boolean} active panel active
17827  * @cfg {String} html panel content
17828  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17829  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17830  * @cfg {String} href click to link..
17831  * 
17832  * 
17833  * @constructor
17834  * Create a new TabPanel
17835  * @param {Object} config The config object
17836  */
17837
17838 Roo.bootstrap.TabPanel = function(config){
17839     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17840     this.addEvents({
17841         /**
17842              * @event changed
17843              * Fires when the active status changes
17844              * @param {Roo.bootstrap.TabPanel} this
17845              * @param {Boolean} state the new state
17846             
17847          */
17848         'changed': true,
17849         /**
17850              * @event beforedeactivate
17851              * Fires before a tab is de-activated - can be used to do validation on a form.
17852              * @param {Roo.bootstrap.TabPanel} this
17853              * @return {Boolean} false if there is an error
17854             
17855          */
17856         'beforedeactivate': true
17857      });
17858     
17859     this.tabId = this.tabId || Roo.id();
17860   
17861 };
17862
17863 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17864     
17865     active: false,
17866     html: false,
17867     tabId: false,
17868     navId : false,
17869     href : '',
17870     
17871     getAutoCreate : function(){
17872         var cfg = {
17873             tag: 'div',
17874             // item is needed for carousel - not sure if it has any effect otherwise
17875             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17876             html: this.html || ''
17877         };
17878         
17879         if(this.active){
17880             cfg.cls += ' active';
17881         }
17882         
17883         if(this.tabId){
17884             cfg.tabId = this.tabId;
17885         }
17886         
17887         
17888         return cfg;
17889     },
17890     
17891     initEvents:  function()
17892     {
17893         var p = this.parent();
17894         
17895         this.navId = this.navId || p.navId;
17896         
17897         if (typeof(this.navId) != 'undefined') {
17898             // not really needed.. but just in case.. parent should be a NavGroup.
17899             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17900             
17901             tg.register(this);
17902             
17903             var i = tg.tabs.length - 1;
17904             
17905             if(this.active && tg.bullets > 0 && i < tg.bullets){
17906                 tg.setActiveBullet(i);
17907             }
17908         }
17909         
17910         this.el.on('click', this.onClick, this);
17911         
17912         if(Roo.isTouch){
17913             this.el.on("touchstart", this.onTouchStart, this);
17914             this.el.on("touchmove", this.onTouchMove, this);
17915             this.el.on("touchend", this.onTouchEnd, this);
17916         }
17917         
17918     },
17919     
17920     onRender : function(ct, position)
17921     {
17922         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17923     },
17924     
17925     setActive : function(state)
17926     {
17927         Roo.log("panel - set active " + this.tabId + "=" + state);
17928         
17929         this.active = state;
17930         if (!state) {
17931             this.el.removeClass('active');
17932             
17933         } else  if (!this.el.hasClass('active')) {
17934             this.el.addClass('active');
17935         }
17936         
17937         this.fireEvent('changed', this, state);
17938     },
17939     
17940     onClick : function(e)
17941     {
17942         e.preventDefault();
17943         
17944         if(!this.href.length){
17945             return;
17946         }
17947         
17948         window.location.href = this.href;
17949     },
17950     
17951     startX : 0,
17952     startY : 0,
17953     endX : 0,
17954     endY : 0,
17955     swiping : false,
17956     
17957     onTouchStart : function(e)
17958     {
17959         this.swiping = false;
17960         
17961         this.startX = e.browserEvent.touches[0].clientX;
17962         this.startY = e.browserEvent.touches[0].clientY;
17963     },
17964     
17965     onTouchMove : function(e)
17966     {
17967         this.swiping = true;
17968         
17969         this.endX = e.browserEvent.touches[0].clientX;
17970         this.endY = e.browserEvent.touches[0].clientY;
17971     },
17972     
17973     onTouchEnd : function(e)
17974     {
17975         if(!this.swiping){
17976             this.onClick(e);
17977             return;
17978         }
17979         
17980         var tabGroup = this.parent();
17981         
17982         if(this.endX > this.startX){ // swiping right
17983             tabGroup.showPanelPrev();
17984             return;
17985         }
17986         
17987         if(this.startX > this.endX){ // swiping left
17988             tabGroup.showPanelNext();
17989             return;
17990         }
17991     }
17992     
17993     
17994 });
17995  
17996
17997  
17998
17999  /*
18000  * - LGPL
18001  *
18002  * DateField
18003  * 
18004  */
18005
18006 /**
18007  * @class Roo.bootstrap.DateField
18008  * @extends Roo.bootstrap.Input
18009  * Bootstrap DateField class
18010  * @cfg {Number} weekStart default 0
18011  * @cfg {String} viewMode default empty, (months|years)
18012  * @cfg {String} minViewMode default empty, (months|years)
18013  * @cfg {Number} startDate default -Infinity
18014  * @cfg {Number} endDate default Infinity
18015  * @cfg {Boolean} todayHighlight default false
18016  * @cfg {Boolean} todayBtn default false
18017  * @cfg {Boolean} calendarWeeks default false
18018  * @cfg {Object} daysOfWeekDisabled default empty
18019  * @cfg {Boolean} singleMode default false (true | false)
18020  * 
18021  * @cfg {Boolean} keyboardNavigation default true
18022  * @cfg {String} language default en
18023  * 
18024  * @constructor
18025  * Create a new DateField
18026  * @param {Object} config The config object
18027  */
18028
18029 Roo.bootstrap.DateField = function(config){
18030     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18031      this.addEvents({
18032             /**
18033              * @event show
18034              * Fires when this field show.
18035              * @param {Roo.bootstrap.DateField} this
18036              * @param {Mixed} date The date value
18037              */
18038             show : true,
18039             /**
18040              * @event show
18041              * Fires when this field hide.
18042              * @param {Roo.bootstrap.DateField} this
18043              * @param {Mixed} date The date value
18044              */
18045             hide : true,
18046             /**
18047              * @event select
18048              * Fires when select a date.
18049              * @param {Roo.bootstrap.DateField} this
18050              * @param {Mixed} date The date value
18051              */
18052             select : true,
18053             /**
18054              * @event beforeselect
18055              * Fires when before select a date.
18056              * @param {Roo.bootstrap.DateField} this
18057              * @param {Mixed} date The date value
18058              */
18059             beforeselect : true
18060         });
18061 };
18062
18063 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18064     
18065     /**
18066      * @cfg {String} format
18067      * The default date format string which can be overriden for localization support.  The format must be
18068      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18069      */
18070     format : "m/d/y",
18071     /**
18072      * @cfg {String} altFormats
18073      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18074      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18075      */
18076     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18077     
18078     weekStart : 0,
18079     
18080     viewMode : '',
18081     
18082     minViewMode : '',
18083     
18084     todayHighlight : false,
18085     
18086     todayBtn: false,
18087     
18088     language: 'en',
18089     
18090     keyboardNavigation: true,
18091     
18092     calendarWeeks: false,
18093     
18094     startDate: -Infinity,
18095     
18096     endDate: Infinity,
18097     
18098     daysOfWeekDisabled: [],
18099     
18100     _events: [],
18101     
18102     singleMode : false,
18103     
18104     UTCDate: function()
18105     {
18106         return new Date(Date.UTC.apply(Date, arguments));
18107     },
18108     
18109     UTCToday: function()
18110     {
18111         var today = new Date();
18112         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18113     },
18114     
18115     getDate: function() {
18116             var d = this.getUTCDate();
18117             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18118     },
18119     
18120     getUTCDate: function() {
18121             return this.date;
18122     },
18123     
18124     setDate: function(d) {
18125             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18126     },
18127     
18128     setUTCDate: function(d) {
18129             this.date = d;
18130             this.setValue(this.formatDate(this.date));
18131     },
18132         
18133     onRender: function(ct, position)
18134     {
18135         
18136         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18137         
18138         this.language = this.language || 'en';
18139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18140         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18141         
18142         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18143         this.format = this.format || 'm/d/y';
18144         this.isInline = false;
18145         this.isInput = true;
18146         this.component = this.el.select('.add-on', true).first() || false;
18147         this.component = (this.component && this.component.length === 0) ? false : this.component;
18148         this.hasInput = this.component && this.inputEl().length;
18149         
18150         if (typeof(this.minViewMode === 'string')) {
18151             switch (this.minViewMode) {
18152                 case 'months':
18153                     this.minViewMode = 1;
18154                     break;
18155                 case 'years':
18156                     this.minViewMode = 2;
18157                     break;
18158                 default:
18159                     this.minViewMode = 0;
18160                     break;
18161             }
18162         }
18163         
18164         if (typeof(this.viewMode === 'string')) {
18165             switch (this.viewMode) {
18166                 case 'months':
18167                     this.viewMode = 1;
18168                     break;
18169                 case 'years':
18170                     this.viewMode = 2;
18171                     break;
18172                 default:
18173                     this.viewMode = 0;
18174                     break;
18175             }
18176         }
18177                 
18178         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18179         
18180 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18181         
18182         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18183         
18184         this.picker().on('mousedown', this.onMousedown, this);
18185         this.picker().on('click', this.onClick, this);
18186         
18187         this.picker().addClass('datepicker-dropdown');
18188         
18189         this.startViewMode = this.viewMode;
18190         
18191         if(this.singleMode){
18192             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18193                 v.setVisibilityMode(Roo.Element.DISPLAY);
18194                 v.hide();
18195             });
18196             
18197             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18198                 v.setStyle('width', '189px');
18199             });
18200         }
18201         
18202         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18203             if(!this.calendarWeeks){
18204                 v.remove();
18205                 return;
18206             }
18207             
18208             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18209             v.attr('colspan', function(i, val){
18210                 return parseInt(val) + 1;
18211             });
18212         });
18213                         
18214         
18215         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18216         
18217         this.setStartDate(this.startDate);
18218         this.setEndDate(this.endDate);
18219         
18220         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18221         
18222         this.fillDow();
18223         this.fillMonths();
18224         this.update();
18225         this.showMode();
18226         
18227         if(this.isInline) {
18228             this.show();
18229         }
18230     },
18231     
18232     picker : function()
18233     {
18234         return this.pickerEl;
18235 //        return this.el.select('.datepicker', true).first();
18236     },
18237     
18238     fillDow: function()
18239     {
18240         var dowCnt = this.weekStart;
18241         
18242         var dow = {
18243             tag: 'tr',
18244             cn: [
18245                 
18246             ]
18247         };
18248         
18249         if(this.calendarWeeks){
18250             dow.cn.push({
18251                 tag: 'th',
18252                 cls: 'cw',
18253                 html: '&nbsp;'
18254             })
18255         }
18256         
18257         while (dowCnt < this.weekStart + 7) {
18258             dow.cn.push({
18259                 tag: 'th',
18260                 cls: 'dow',
18261                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18262             });
18263         }
18264         
18265         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18266     },
18267     
18268     fillMonths: function()
18269     {    
18270         var i = 0;
18271         var months = this.picker().select('>.datepicker-months td', true).first();
18272         
18273         months.dom.innerHTML = '';
18274         
18275         while (i < 12) {
18276             var month = {
18277                 tag: 'span',
18278                 cls: 'month',
18279                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18280             };
18281             
18282             months.createChild(month);
18283         }
18284         
18285     },
18286     
18287     update: function()
18288     {
18289         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;
18290         
18291         if (this.date < this.startDate) {
18292             this.viewDate = new Date(this.startDate);
18293         } else if (this.date > this.endDate) {
18294             this.viewDate = new Date(this.endDate);
18295         } else {
18296             this.viewDate = new Date(this.date);
18297         }
18298         
18299         this.fill();
18300     },
18301     
18302     fill: function() 
18303     {
18304         var d = new Date(this.viewDate),
18305                 year = d.getUTCFullYear(),
18306                 month = d.getUTCMonth(),
18307                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18308                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18309                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18310                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18311                 currentDate = this.date && this.date.valueOf(),
18312                 today = this.UTCToday();
18313         
18314         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18315         
18316 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18317         
18318 //        this.picker.select('>tfoot th.today').
18319 //                                              .text(dates[this.language].today)
18320 //                                              .toggle(this.todayBtn !== false);
18321     
18322         this.updateNavArrows();
18323         this.fillMonths();
18324                                                 
18325         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18326         
18327         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18328          
18329         prevMonth.setUTCDate(day);
18330         
18331         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18332         
18333         var nextMonth = new Date(prevMonth);
18334         
18335         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18336         
18337         nextMonth = nextMonth.valueOf();
18338         
18339         var fillMonths = false;
18340         
18341         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18342         
18343         while(prevMonth.valueOf() < nextMonth) {
18344             var clsName = '';
18345             
18346             if (prevMonth.getUTCDay() === this.weekStart) {
18347                 if(fillMonths){
18348                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18349                 }
18350                     
18351                 fillMonths = {
18352                     tag: 'tr',
18353                     cn: []
18354                 };
18355                 
18356                 if(this.calendarWeeks){
18357                     // ISO 8601: First week contains first thursday.
18358                     // ISO also states week starts on Monday, but we can be more abstract here.
18359                     var
18360                     // Start of current week: based on weekstart/current date
18361                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18362                     // Thursday of this week
18363                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18364                     // First Thursday of year, year from thursday
18365                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18366                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18367                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18368                     
18369                     fillMonths.cn.push({
18370                         tag: 'td',
18371                         cls: 'cw',
18372                         html: calWeek
18373                     });
18374                 }
18375             }
18376             
18377             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18378                 clsName += ' old';
18379             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18380                 clsName += ' new';
18381             }
18382             if (this.todayHighlight &&
18383                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18384                 prevMonth.getUTCMonth() == today.getMonth() &&
18385                 prevMonth.getUTCDate() == today.getDate()) {
18386                 clsName += ' today';
18387             }
18388             
18389             if (currentDate && prevMonth.valueOf() === currentDate) {
18390                 clsName += ' active';
18391             }
18392             
18393             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18394                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18395                     clsName += ' disabled';
18396             }
18397             
18398             fillMonths.cn.push({
18399                 tag: 'td',
18400                 cls: 'day ' + clsName,
18401                 html: prevMonth.getDate()
18402             });
18403             
18404             prevMonth.setDate(prevMonth.getDate()+1);
18405         }
18406           
18407         var currentYear = this.date && this.date.getUTCFullYear();
18408         var currentMonth = this.date && this.date.getUTCMonth();
18409         
18410         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18411         
18412         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18413             v.removeClass('active');
18414             
18415             if(currentYear === year && k === currentMonth){
18416                 v.addClass('active');
18417             }
18418             
18419             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18420                 v.addClass('disabled');
18421             }
18422             
18423         });
18424         
18425         
18426         year = parseInt(year/10, 10) * 10;
18427         
18428         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18429         
18430         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18431         
18432         year -= 1;
18433         for (var i = -1; i < 11; i++) {
18434             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18435                 tag: 'span',
18436                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18437                 html: year
18438             });
18439             
18440             year += 1;
18441         }
18442     },
18443     
18444     showMode: function(dir) 
18445     {
18446         if (dir) {
18447             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18448         }
18449         
18450         Roo.each(this.picker().select('>div',true).elements, function(v){
18451             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18452             v.hide();
18453         });
18454         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18455     },
18456     
18457     place: function()
18458     {
18459         if(this.isInline) {
18460             return;
18461         }
18462         
18463         this.picker().removeClass(['bottom', 'top']);
18464         
18465         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18466             /*
18467              * place to the top of element!
18468              *
18469              */
18470             
18471             this.picker().addClass('top');
18472             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18473             
18474             return;
18475         }
18476         
18477         this.picker().addClass('bottom');
18478         
18479         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18480     },
18481     
18482     parseDate : function(value)
18483     {
18484         if(!value || value instanceof Date){
18485             return value;
18486         }
18487         var v = Date.parseDate(value, this.format);
18488         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18489             v = Date.parseDate(value, 'Y-m-d');
18490         }
18491         if(!v && this.altFormats){
18492             if(!this.altFormatsArray){
18493                 this.altFormatsArray = this.altFormats.split("|");
18494             }
18495             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18496                 v = Date.parseDate(value, this.altFormatsArray[i]);
18497             }
18498         }
18499         return v;
18500     },
18501     
18502     formatDate : function(date, fmt)
18503     {   
18504         return (!date || !(date instanceof Date)) ?
18505         date : date.dateFormat(fmt || this.format);
18506     },
18507     
18508     onFocus : function()
18509     {
18510         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18511         this.show();
18512     },
18513     
18514     onBlur : function()
18515     {
18516         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18517         
18518         var d = this.inputEl().getValue();
18519         
18520         this.setValue(d);
18521                 
18522         this.hide();
18523     },
18524     
18525     show : function()
18526     {
18527         this.picker().show();
18528         this.update();
18529         this.place();
18530         
18531         this.fireEvent('show', this, this.date);
18532     },
18533     
18534     hide : function()
18535     {
18536         if(this.isInline) {
18537             return;
18538         }
18539         this.picker().hide();
18540         this.viewMode = this.startViewMode;
18541         this.showMode();
18542         
18543         this.fireEvent('hide', this, this.date);
18544         
18545     },
18546     
18547     onMousedown: function(e)
18548     {
18549         e.stopPropagation();
18550         e.preventDefault();
18551     },
18552     
18553     keyup: function(e)
18554     {
18555         Roo.bootstrap.DateField.superclass.keyup.call(this);
18556         this.update();
18557     },
18558
18559     setValue: function(v)
18560     {
18561         if(this.fireEvent('beforeselect', this, v) !== false){
18562             var d = new Date(this.parseDate(v) ).clearTime();
18563         
18564             if(isNaN(d.getTime())){
18565                 this.date = this.viewDate = '';
18566                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18567                 return;
18568             }
18569
18570             v = this.formatDate(d);
18571
18572             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18573
18574             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18575
18576             this.update();
18577
18578             this.fireEvent('select', this, this.date);
18579         }
18580     },
18581     
18582     getValue: function()
18583     {
18584         return this.formatDate(this.date);
18585     },
18586     
18587     fireKey: function(e)
18588     {
18589         if (!this.picker().isVisible()){
18590             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18591                 this.show();
18592             }
18593             return;
18594         }
18595         
18596         var dateChanged = false,
18597         dir, day, month,
18598         newDate, newViewDate;
18599         
18600         switch(e.keyCode){
18601             case 27: // escape
18602                 this.hide();
18603                 e.preventDefault();
18604                 break;
18605             case 37: // left
18606             case 39: // right
18607                 if (!this.keyboardNavigation) {
18608                     break;
18609                 }
18610                 dir = e.keyCode == 37 ? -1 : 1;
18611                 
18612                 if (e.ctrlKey){
18613                     newDate = this.moveYear(this.date, dir);
18614                     newViewDate = this.moveYear(this.viewDate, dir);
18615                 } else if (e.shiftKey){
18616                     newDate = this.moveMonth(this.date, dir);
18617                     newViewDate = this.moveMonth(this.viewDate, dir);
18618                 } else {
18619                     newDate = new Date(this.date);
18620                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18621                     newViewDate = new Date(this.viewDate);
18622                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18623                 }
18624                 if (this.dateWithinRange(newDate)){
18625                     this.date = newDate;
18626                     this.viewDate = newViewDate;
18627                     this.setValue(this.formatDate(this.date));
18628 //                    this.update();
18629                     e.preventDefault();
18630                     dateChanged = true;
18631                 }
18632                 break;
18633             case 38: // up
18634             case 40: // down
18635                 if (!this.keyboardNavigation) {
18636                     break;
18637                 }
18638                 dir = e.keyCode == 38 ? -1 : 1;
18639                 if (e.ctrlKey){
18640                     newDate = this.moveYear(this.date, dir);
18641                     newViewDate = this.moveYear(this.viewDate, dir);
18642                 } else if (e.shiftKey){
18643                     newDate = this.moveMonth(this.date, dir);
18644                     newViewDate = this.moveMonth(this.viewDate, dir);
18645                 } else {
18646                     newDate = new Date(this.date);
18647                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18648                     newViewDate = new Date(this.viewDate);
18649                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18650                 }
18651                 if (this.dateWithinRange(newDate)){
18652                     this.date = newDate;
18653                     this.viewDate = newViewDate;
18654                     this.setValue(this.formatDate(this.date));
18655 //                    this.update();
18656                     e.preventDefault();
18657                     dateChanged = true;
18658                 }
18659                 break;
18660             case 13: // enter
18661                 this.setValue(this.formatDate(this.date));
18662                 this.hide();
18663                 e.preventDefault();
18664                 break;
18665             case 9: // tab
18666                 this.setValue(this.formatDate(this.date));
18667                 this.hide();
18668                 break;
18669             case 16: // shift
18670             case 17: // ctrl
18671             case 18: // alt
18672                 break;
18673             default :
18674                 this.hide();
18675                 
18676         }
18677     },
18678     
18679     
18680     onClick: function(e) 
18681     {
18682         e.stopPropagation();
18683         e.preventDefault();
18684         
18685         var target = e.getTarget();
18686         
18687         if(target.nodeName.toLowerCase() === 'i'){
18688             target = Roo.get(target).dom.parentNode;
18689         }
18690         
18691         var nodeName = target.nodeName;
18692         var className = target.className;
18693         var html = target.innerHTML;
18694         //Roo.log(nodeName);
18695         
18696         switch(nodeName.toLowerCase()) {
18697             case 'th':
18698                 switch(className) {
18699                     case 'switch':
18700                         this.showMode(1);
18701                         break;
18702                     case 'prev':
18703                     case 'next':
18704                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18705                         switch(this.viewMode){
18706                                 case 0:
18707                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18708                                         break;
18709                                 case 1:
18710                                 case 2:
18711                                         this.viewDate = this.moveYear(this.viewDate, dir);
18712                                         break;
18713                         }
18714                         this.fill();
18715                         break;
18716                     case 'today':
18717                         var date = new Date();
18718                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18719 //                        this.fill()
18720                         this.setValue(this.formatDate(this.date));
18721                         
18722                         this.hide();
18723                         break;
18724                 }
18725                 break;
18726             case 'span':
18727                 if (className.indexOf('disabled') < 0) {
18728                     this.viewDate.setUTCDate(1);
18729                     if (className.indexOf('month') > -1) {
18730                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18731                     } else {
18732                         var year = parseInt(html, 10) || 0;
18733                         this.viewDate.setUTCFullYear(year);
18734                         
18735                     }
18736                     
18737                     if(this.singleMode){
18738                         this.setValue(this.formatDate(this.viewDate));
18739                         this.hide();
18740                         return;
18741                     }
18742                     
18743                     this.showMode(-1);
18744                     this.fill();
18745                 }
18746                 break;
18747                 
18748             case 'td':
18749                 //Roo.log(className);
18750                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18751                     var day = parseInt(html, 10) || 1;
18752                     var year = this.viewDate.getUTCFullYear(),
18753                         month = this.viewDate.getUTCMonth();
18754
18755                     if (className.indexOf('old') > -1) {
18756                         if(month === 0 ){
18757                             month = 11;
18758                             year -= 1;
18759                         }else{
18760                             month -= 1;
18761                         }
18762                     } else if (className.indexOf('new') > -1) {
18763                         if (month == 11) {
18764                             month = 0;
18765                             year += 1;
18766                         } else {
18767                             month += 1;
18768                         }
18769                     }
18770                     //Roo.log([year,month,day]);
18771                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18772                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18773 //                    this.fill();
18774                     //Roo.log(this.formatDate(this.date));
18775                     this.setValue(this.formatDate(this.date));
18776                     this.hide();
18777                 }
18778                 break;
18779         }
18780     },
18781     
18782     setStartDate: function(startDate)
18783     {
18784         this.startDate = startDate || -Infinity;
18785         if (this.startDate !== -Infinity) {
18786             this.startDate = this.parseDate(this.startDate);
18787         }
18788         this.update();
18789         this.updateNavArrows();
18790     },
18791
18792     setEndDate: function(endDate)
18793     {
18794         this.endDate = endDate || Infinity;
18795         if (this.endDate !== Infinity) {
18796             this.endDate = this.parseDate(this.endDate);
18797         }
18798         this.update();
18799         this.updateNavArrows();
18800     },
18801     
18802     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18803     {
18804         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18805         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18806             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18807         }
18808         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18809             return parseInt(d, 10);
18810         });
18811         this.update();
18812         this.updateNavArrows();
18813     },
18814     
18815     updateNavArrows: function() 
18816     {
18817         if(this.singleMode){
18818             return;
18819         }
18820         
18821         var d = new Date(this.viewDate),
18822         year = d.getUTCFullYear(),
18823         month = d.getUTCMonth();
18824         
18825         Roo.each(this.picker().select('.prev', true).elements, function(v){
18826             v.show();
18827             switch (this.viewMode) {
18828                 case 0:
18829
18830                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18831                         v.hide();
18832                     }
18833                     break;
18834                 case 1:
18835                 case 2:
18836                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18837                         v.hide();
18838                     }
18839                     break;
18840             }
18841         });
18842         
18843         Roo.each(this.picker().select('.next', true).elements, function(v){
18844             v.show();
18845             switch (this.viewMode) {
18846                 case 0:
18847
18848                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18849                         v.hide();
18850                     }
18851                     break;
18852                 case 1:
18853                 case 2:
18854                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18855                         v.hide();
18856                     }
18857                     break;
18858             }
18859         })
18860     },
18861     
18862     moveMonth: function(date, dir)
18863     {
18864         if (!dir) {
18865             return date;
18866         }
18867         var new_date = new Date(date.valueOf()),
18868         day = new_date.getUTCDate(),
18869         month = new_date.getUTCMonth(),
18870         mag = Math.abs(dir),
18871         new_month, test;
18872         dir = dir > 0 ? 1 : -1;
18873         if (mag == 1){
18874             test = dir == -1
18875             // If going back one month, make sure month is not current month
18876             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18877             ? function(){
18878                 return new_date.getUTCMonth() == month;
18879             }
18880             // If going forward one month, make sure month is as expected
18881             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18882             : function(){
18883                 return new_date.getUTCMonth() != new_month;
18884             };
18885             new_month = month + dir;
18886             new_date.setUTCMonth(new_month);
18887             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18888             if (new_month < 0 || new_month > 11) {
18889                 new_month = (new_month + 12) % 12;
18890             }
18891         } else {
18892             // For magnitudes >1, move one month at a time...
18893             for (var i=0; i<mag; i++) {
18894                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18895                 new_date = this.moveMonth(new_date, dir);
18896             }
18897             // ...then reset the day, keeping it in the new month
18898             new_month = new_date.getUTCMonth();
18899             new_date.setUTCDate(day);
18900             test = function(){
18901                 return new_month != new_date.getUTCMonth();
18902             };
18903         }
18904         // Common date-resetting loop -- if date is beyond end of month, make it
18905         // end of month
18906         while (test()){
18907             new_date.setUTCDate(--day);
18908             new_date.setUTCMonth(new_month);
18909         }
18910         return new_date;
18911     },
18912
18913     moveYear: function(date, dir)
18914     {
18915         return this.moveMonth(date, dir*12);
18916     },
18917
18918     dateWithinRange: function(date)
18919     {
18920         return date >= this.startDate && date <= this.endDate;
18921     },
18922
18923     
18924     remove: function() 
18925     {
18926         this.picker().remove();
18927     },
18928     
18929     validateValue : function(value)
18930     {
18931         if(value.length < 1)  {
18932             if(this.allowBlank){
18933                 return true;
18934             }
18935             return false;
18936         }
18937         
18938         if(value.length < this.minLength){
18939             return false;
18940         }
18941         if(value.length > this.maxLength){
18942             return false;
18943         }
18944         if(this.vtype){
18945             var vt = Roo.form.VTypes;
18946             if(!vt[this.vtype](value, this)){
18947                 return false;
18948             }
18949         }
18950         if(typeof this.validator == "function"){
18951             var msg = this.validator(value);
18952             if(msg !== true){
18953                 return false;
18954             }
18955         }
18956         
18957         if(this.regex && !this.regex.test(value)){
18958             return false;
18959         }
18960         
18961         if(typeof(this.parseDate(value)) == 'undefined'){
18962             return false;
18963         }
18964         
18965         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18966             return false;
18967         }      
18968         
18969         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18970             return false;
18971         } 
18972         
18973         
18974         return true;
18975     }
18976    
18977 });
18978
18979 Roo.apply(Roo.bootstrap.DateField,  {
18980     
18981     head : {
18982         tag: 'thead',
18983         cn: [
18984         {
18985             tag: 'tr',
18986             cn: [
18987             {
18988                 tag: 'th',
18989                 cls: 'prev',
18990                 html: '<i class="fa fa-arrow-left"/>'
18991             },
18992             {
18993                 tag: 'th',
18994                 cls: 'switch',
18995                 colspan: '5'
18996             },
18997             {
18998                 tag: 'th',
18999                 cls: 'next',
19000                 html: '<i class="fa fa-arrow-right"/>'
19001             }
19002
19003             ]
19004         }
19005         ]
19006     },
19007     
19008     content : {
19009         tag: 'tbody',
19010         cn: [
19011         {
19012             tag: 'tr',
19013             cn: [
19014             {
19015                 tag: 'td',
19016                 colspan: '7'
19017             }
19018             ]
19019         }
19020         ]
19021     },
19022     
19023     footer : {
19024         tag: 'tfoot',
19025         cn: [
19026         {
19027             tag: 'tr',
19028             cn: [
19029             {
19030                 tag: 'th',
19031                 colspan: '7',
19032                 cls: 'today'
19033             }
19034                     
19035             ]
19036         }
19037         ]
19038     },
19039     
19040     dates:{
19041         en: {
19042             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19043             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19044             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19045             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19046             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19047             today: "Today"
19048         }
19049     },
19050     
19051     modes: [
19052     {
19053         clsName: 'days',
19054         navFnc: 'Month',
19055         navStep: 1
19056     },
19057     {
19058         clsName: 'months',
19059         navFnc: 'FullYear',
19060         navStep: 1
19061     },
19062     {
19063         clsName: 'years',
19064         navFnc: 'FullYear',
19065         navStep: 10
19066     }]
19067 });
19068
19069 Roo.apply(Roo.bootstrap.DateField,  {
19070   
19071     template : {
19072         tag: 'div',
19073         cls: 'datepicker dropdown-menu roo-dynamic',
19074         cn: [
19075         {
19076             tag: 'div',
19077             cls: 'datepicker-days',
19078             cn: [
19079             {
19080                 tag: 'table',
19081                 cls: 'table-condensed',
19082                 cn:[
19083                 Roo.bootstrap.DateField.head,
19084                 {
19085                     tag: 'tbody'
19086                 },
19087                 Roo.bootstrap.DateField.footer
19088                 ]
19089             }
19090             ]
19091         },
19092         {
19093             tag: 'div',
19094             cls: 'datepicker-months',
19095             cn: [
19096             {
19097                 tag: 'table',
19098                 cls: 'table-condensed',
19099                 cn:[
19100                 Roo.bootstrap.DateField.head,
19101                 Roo.bootstrap.DateField.content,
19102                 Roo.bootstrap.DateField.footer
19103                 ]
19104             }
19105             ]
19106         },
19107         {
19108             tag: 'div',
19109             cls: 'datepicker-years',
19110             cn: [
19111             {
19112                 tag: 'table',
19113                 cls: 'table-condensed',
19114                 cn:[
19115                 Roo.bootstrap.DateField.head,
19116                 Roo.bootstrap.DateField.content,
19117                 Roo.bootstrap.DateField.footer
19118                 ]
19119             }
19120             ]
19121         }
19122         ]
19123     }
19124 });
19125
19126  
19127
19128  /*
19129  * - LGPL
19130  *
19131  * TimeField
19132  * 
19133  */
19134
19135 /**
19136  * @class Roo.bootstrap.TimeField
19137  * @extends Roo.bootstrap.Input
19138  * Bootstrap DateField class
19139  * 
19140  * 
19141  * @constructor
19142  * Create a new TimeField
19143  * @param {Object} config The config object
19144  */
19145
19146 Roo.bootstrap.TimeField = function(config){
19147     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19148     this.addEvents({
19149             /**
19150              * @event show
19151              * Fires when this field show.
19152              * @param {Roo.bootstrap.DateField} thisthis
19153              * @param {Mixed} date The date value
19154              */
19155             show : true,
19156             /**
19157              * @event show
19158              * Fires when this field hide.
19159              * @param {Roo.bootstrap.DateField} this
19160              * @param {Mixed} date The date value
19161              */
19162             hide : true,
19163             /**
19164              * @event select
19165              * Fires when select a date.
19166              * @param {Roo.bootstrap.DateField} this
19167              * @param {Mixed} date The date value
19168              */
19169             select : true
19170         });
19171 };
19172
19173 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19174     
19175     /**
19176      * @cfg {String} format
19177      * The default time format string which can be overriden for localization support.  The format must be
19178      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19179      */
19180     format : "H:i",
19181        
19182     onRender: function(ct, position)
19183     {
19184         
19185         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19186                 
19187         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19188         
19189         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19190         
19191         this.pop = this.picker().select('>.datepicker-time',true).first();
19192         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19193         
19194         this.picker().on('mousedown', this.onMousedown, this);
19195         this.picker().on('click', this.onClick, this);
19196         
19197         this.picker().addClass('datepicker-dropdown');
19198     
19199         this.fillTime();
19200         this.update();
19201             
19202         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19203         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19204         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19205         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19206         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19207         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19208
19209     },
19210     
19211     fireKey: function(e){
19212         if (!this.picker().isVisible()){
19213             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19214                 this.show();
19215             }
19216             return;
19217         }
19218
19219         e.preventDefault();
19220         
19221         switch(e.keyCode){
19222             case 27: // escape
19223                 this.hide();
19224                 break;
19225             case 37: // left
19226             case 39: // right
19227                 this.onTogglePeriod();
19228                 break;
19229             case 38: // up
19230                 this.onIncrementMinutes();
19231                 break;
19232             case 40: // down
19233                 this.onDecrementMinutes();
19234                 break;
19235             case 13: // enter
19236             case 9: // tab
19237                 this.setTime();
19238                 break;
19239         }
19240     },
19241     
19242     onClick: function(e) {
19243         e.stopPropagation();
19244         e.preventDefault();
19245     },
19246     
19247     picker : function()
19248     {
19249         return this.el.select('.datepicker', true).first();
19250     },
19251     
19252     fillTime: function()
19253     {    
19254         var time = this.pop.select('tbody', true).first();
19255         
19256         time.dom.innerHTML = '';
19257         
19258         time.createChild({
19259             tag: 'tr',
19260             cn: [
19261                 {
19262                     tag: 'td',
19263                     cn: [
19264                         {
19265                             tag: 'a',
19266                             href: '#',
19267                             cls: 'btn',
19268                             cn: [
19269                                 {
19270                                     tag: 'span',
19271                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19272                                 }
19273                             ]
19274                         } 
19275                     ]
19276                 },
19277                 {
19278                     tag: 'td',
19279                     cls: 'separator'
19280                 },
19281                 {
19282                     tag: 'td',
19283                     cn: [
19284                         {
19285                             tag: 'a',
19286                             href: '#',
19287                             cls: 'btn',
19288                             cn: [
19289                                 {
19290                                     tag: 'span',
19291                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19292                                 }
19293                             ]
19294                         }
19295                     ]
19296                 },
19297                 {
19298                     tag: 'td',
19299                     cls: 'separator'
19300                 }
19301             ]
19302         });
19303         
19304         time.createChild({
19305             tag: 'tr',
19306             cn: [
19307                 {
19308                     tag: 'td',
19309                     cn: [
19310                         {
19311                             tag: 'span',
19312                             cls: 'timepicker-hour',
19313                             html: '00'
19314                         }  
19315                     ]
19316                 },
19317                 {
19318                     tag: 'td',
19319                     cls: 'separator',
19320                     html: ':'
19321                 },
19322                 {
19323                     tag: 'td',
19324                     cn: [
19325                         {
19326                             tag: 'span',
19327                             cls: 'timepicker-minute',
19328                             html: '00'
19329                         }  
19330                     ]
19331                 },
19332                 {
19333                     tag: 'td',
19334                     cls: 'separator'
19335                 },
19336                 {
19337                     tag: 'td',
19338                     cn: [
19339                         {
19340                             tag: 'button',
19341                             type: 'button',
19342                             cls: 'btn btn-primary period',
19343                             html: 'AM'
19344                             
19345                         }
19346                     ]
19347                 }
19348             ]
19349         });
19350         
19351         time.createChild({
19352             tag: 'tr',
19353             cn: [
19354                 {
19355                     tag: 'td',
19356                     cn: [
19357                         {
19358                             tag: 'a',
19359                             href: '#',
19360                             cls: 'btn',
19361                             cn: [
19362                                 {
19363                                     tag: 'span',
19364                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19365                                 }
19366                             ]
19367                         }
19368                     ]
19369                 },
19370                 {
19371                     tag: 'td',
19372                     cls: 'separator'
19373                 },
19374                 {
19375                     tag: 'td',
19376                     cn: [
19377                         {
19378                             tag: 'a',
19379                             href: '#',
19380                             cls: 'btn',
19381                             cn: [
19382                                 {
19383                                     tag: 'span',
19384                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19385                                 }
19386                             ]
19387                         }
19388                     ]
19389                 },
19390                 {
19391                     tag: 'td',
19392                     cls: 'separator'
19393                 }
19394             ]
19395         });
19396         
19397     },
19398     
19399     update: function()
19400     {
19401         
19402         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19403         
19404         this.fill();
19405     },
19406     
19407     fill: function() 
19408     {
19409         var hours = this.time.getHours();
19410         var minutes = this.time.getMinutes();
19411         var period = 'AM';
19412         
19413         if(hours > 11){
19414             period = 'PM';
19415         }
19416         
19417         if(hours == 0){
19418             hours = 12;
19419         }
19420         
19421         
19422         if(hours > 12){
19423             hours = hours - 12;
19424         }
19425         
19426         if(hours < 10){
19427             hours = '0' + hours;
19428         }
19429         
19430         if(minutes < 10){
19431             minutes = '0' + minutes;
19432         }
19433         
19434         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19435         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19436         this.pop.select('button', true).first().dom.innerHTML = period;
19437         
19438     },
19439     
19440     place: function()
19441     {   
19442         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19443         
19444         var cls = ['bottom'];
19445         
19446         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19447             cls.pop();
19448             cls.push('top');
19449         }
19450         
19451         cls.push('right');
19452         
19453         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19454             cls.pop();
19455             cls.push('left');
19456         }
19457         
19458         this.picker().addClass(cls.join('-'));
19459         
19460         var _this = this;
19461         
19462         Roo.each(cls, function(c){
19463             if(c == 'bottom'){
19464                 _this.picker().setTop(_this.inputEl().getHeight());
19465                 return;
19466             }
19467             if(c == 'top'){
19468                 _this.picker().setTop(0 - _this.picker().getHeight());
19469                 return;
19470             }
19471             
19472             if(c == 'left'){
19473                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19474                 return;
19475             }
19476             if(c == 'right'){
19477                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19478                 return;
19479             }
19480         });
19481         
19482     },
19483   
19484     onFocus : function()
19485     {
19486         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19487         this.show();
19488     },
19489     
19490     onBlur : function()
19491     {
19492         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19493         this.hide();
19494     },
19495     
19496     show : function()
19497     {
19498         this.picker().show();
19499         this.pop.show();
19500         this.update();
19501         this.place();
19502         
19503         this.fireEvent('show', this, this.date);
19504     },
19505     
19506     hide : function()
19507     {
19508         this.picker().hide();
19509         this.pop.hide();
19510         
19511         this.fireEvent('hide', this, this.date);
19512     },
19513     
19514     setTime : function()
19515     {
19516         this.hide();
19517         this.setValue(this.time.format(this.format));
19518         
19519         this.fireEvent('select', this, this.date);
19520         
19521         
19522     },
19523     
19524     onMousedown: function(e){
19525         e.stopPropagation();
19526         e.preventDefault();
19527     },
19528     
19529     onIncrementHours: function()
19530     {
19531         Roo.log('onIncrementHours');
19532         this.time = this.time.add(Date.HOUR, 1);
19533         this.update();
19534         
19535     },
19536     
19537     onDecrementHours: function()
19538     {
19539         Roo.log('onDecrementHours');
19540         this.time = this.time.add(Date.HOUR, -1);
19541         this.update();
19542     },
19543     
19544     onIncrementMinutes: function()
19545     {
19546         Roo.log('onIncrementMinutes');
19547         this.time = this.time.add(Date.MINUTE, 1);
19548         this.update();
19549     },
19550     
19551     onDecrementMinutes: function()
19552     {
19553         Roo.log('onDecrementMinutes');
19554         this.time = this.time.add(Date.MINUTE, -1);
19555         this.update();
19556     },
19557     
19558     onTogglePeriod: function()
19559     {
19560         Roo.log('onTogglePeriod');
19561         this.time = this.time.add(Date.HOUR, 12);
19562         this.update();
19563     }
19564     
19565    
19566 });
19567
19568 Roo.apply(Roo.bootstrap.TimeField,  {
19569     
19570     content : {
19571         tag: 'tbody',
19572         cn: [
19573             {
19574                 tag: 'tr',
19575                 cn: [
19576                 {
19577                     tag: 'td',
19578                     colspan: '7'
19579                 }
19580                 ]
19581             }
19582         ]
19583     },
19584     
19585     footer : {
19586         tag: 'tfoot',
19587         cn: [
19588             {
19589                 tag: 'tr',
19590                 cn: [
19591                 {
19592                     tag: 'th',
19593                     colspan: '7',
19594                     cls: '',
19595                     cn: [
19596                         {
19597                             tag: 'button',
19598                             cls: 'btn btn-info ok',
19599                             html: 'OK'
19600                         }
19601                     ]
19602                 }
19603
19604                 ]
19605             }
19606         ]
19607     }
19608 });
19609
19610 Roo.apply(Roo.bootstrap.TimeField,  {
19611   
19612     template : {
19613         tag: 'div',
19614         cls: 'datepicker dropdown-menu',
19615         cn: [
19616             {
19617                 tag: 'div',
19618                 cls: 'datepicker-time',
19619                 cn: [
19620                 {
19621                     tag: 'table',
19622                     cls: 'table-condensed',
19623                     cn:[
19624                     Roo.bootstrap.TimeField.content,
19625                     Roo.bootstrap.TimeField.footer
19626                     ]
19627                 }
19628                 ]
19629             }
19630         ]
19631     }
19632 });
19633
19634  
19635
19636  /*
19637  * - LGPL
19638  *
19639  * MonthField
19640  * 
19641  */
19642
19643 /**
19644  * @class Roo.bootstrap.MonthField
19645  * @extends Roo.bootstrap.Input
19646  * Bootstrap MonthField class
19647  * 
19648  * @cfg {String} language default en
19649  * 
19650  * @constructor
19651  * Create a new MonthField
19652  * @param {Object} config The config object
19653  */
19654
19655 Roo.bootstrap.MonthField = function(config){
19656     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19657     
19658     this.addEvents({
19659         /**
19660          * @event show
19661          * Fires when this field show.
19662          * @param {Roo.bootstrap.MonthField} this
19663          * @param {Mixed} date The date value
19664          */
19665         show : true,
19666         /**
19667          * @event show
19668          * Fires when this field hide.
19669          * @param {Roo.bootstrap.MonthField} this
19670          * @param {Mixed} date The date value
19671          */
19672         hide : true,
19673         /**
19674          * @event select
19675          * Fires when select a date.
19676          * @param {Roo.bootstrap.MonthField} this
19677          * @param {String} oldvalue The old value
19678          * @param {String} newvalue The new value
19679          */
19680         select : true
19681     });
19682 };
19683
19684 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19685     
19686     onRender: function(ct, position)
19687     {
19688         
19689         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19690         
19691         this.language = this.language || 'en';
19692         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19693         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19694         
19695         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19696         this.isInline = false;
19697         this.isInput = true;
19698         this.component = this.el.select('.add-on', true).first() || false;
19699         this.component = (this.component && this.component.length === 0) ? false : this.component;
19700         this.hasInput = this.component && this.inputEL().length;
19701         
19702         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19703         
19704         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19705         
19706         this.picker().on('mousedown', this.onMousedown, this);
19707         this.picker().on('click', this.onClick, this);
19708         
19709         this.picker().addClass('datepicker-dropdown');
19710         
19711         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19712             v.setStyle('width', '189px');
19713         });
19714         
19715         this.fillMonths();
19716         
19717         this.update();
19718         
19719         if(this.isInline) {
19720             this.show();
19721         }
19722         
19723     },
19724     
19725     setValue: function(v, suppressEvent)
19726     {   
19727         var o = this.getValue();
19728         
19729         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19730         
19731         this.update();
19732
19733         if(suppressEvent !== true){
19734             this.fireEvent('select', this, o, v);
19735         }
19736         
19737     },
19738     
19739     getValue: function()
19740     {
19741         return this.value;
19742     },
19743     
19744     onClick: function(e) 
19745     {
19746         e.stopPropagation();
19747         e.preventDefault();
19748         
19749         var target = e.getTarget();
19750         
19751         if(target.nodeName.toLowerCase() === 'i'){
19752             target = Roo.get(target).dom.parentNode;
19753         }
19754         
19755         var nodeName = target.nodeName;
19756         var className = target.className;
19757         var html = target.innerHTML;
19758         
19759         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19760             return;
19761         }
19762         
19763         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19764         
19765         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19766         
19767         this.hide();
19768                         
19769     },
19770     
19771     picker : function()
19772     {
19773         return this.pickerEl;
19774     },
19775     
19776     fillMonths: function()
19777     {    
19778         var i = 0;
19779         var months = this.picker().select('>.datepicker-months td', true).first();
19780         
19781         months.dom.innerHTML = '';
19782         
19783         while (i < 12) {
19784             var month = {
19785                 tag: 'span',
19786                 cls: 'month',
19787                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19788             };
19789             
19790             months.createChild(month);
19791         }
19792         
19793     },
19794     
19795     update: function()
19796     {
19797         var _this = this;
19798         
19799         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19800             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19801         }
19802         
19803         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19804             e.removeClass('active');
19805             
19806             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19807                 e.addClass('active');
19808             }
19809         })
19810     },
19811     
19812     place: function()
19813     {
19814         if(this.isInline) {
19815             return;
19816         }
19817         
19818         this.picker().removeClass(['bottom', 'top']);
19819         
19820         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19821             /*
19822              * place to the top of element!
19823              *
19824              */
19825             
19826             this.picker().addClass('top');
19827             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19828             
19829             return;
19830         }
19831         
19832         this.picker().addClass('bottom');
19833         
19834         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19835     },
19836     
19837     onFocus : function()
19838     {
19839         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19840         this.show();
19841     },
19842     
19843     onBlur : function()
19844     {
19845         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19846         
19847         var d = this.inputEl().getValue();
19848         
19849         this.setValue(d);
19850                 
19851         this.hide();
19852     },
19853     
19854     show : function()
19855     {
19856         this.picker().show();
19857         this.picker().select('>.datepicker-months', true).first().show();
19858         this.update();
19859         this.place();
19860         
19861         this.fireEvent('show', this, this.date);
19862     },
19863     
19864     hide : function()
19865     {
19866         if(this.isInline) {
19867             return;
19868         }
19869         this.picker().hide();
19870         this.fireEvent('hide', this, this.date);
19871         
19872     },
19873     
19874     onMousedown: function(e)
19875     {
19876         e.stopPropagation();
19877         e.preventDefault();
19878     },
19879     
19880     keyup: function(e)
19881     {
19882         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19883         this.update();
19884     },
19885
19886     fireKey: function(e)
19887     {
19888         if (!this.picker().isVisible()){
19889             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19890                 this.show();
19891             }
19892             return;
19893         }
19894         
19895         var dir;
19896         
19897         switch(e.keyCode){
19898             case 27: // escape
19899                 this.hide();
19900                 e.preventDefault();
19901                 break;
19902             case 37: // left
19903             case 39: // right
19904                 dir = e.keyCode == 37 ? -1 : 1;
19905                 
19906                 this.vIndex = this.vIndex + dir;
19907                 
19908                 if(this.vIndex < 0){
19909                     this.vIndex = 0;
19910                 }
19911                 
19912                 if(this.vIndex > 11){
19913                     this.vIndex = 11;
19914                 }
19915                 
19916                 if(isNaN(this.vIndex)){
19917                     this.vIndex = 0;
19918                 }
19919                 
19920                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19921                 
19922                 break;
19923             case 38: // up
19924             case 40: // down
19925                 
19926                 dir = e.keyCode == 38 ? -1 : 1;
19927                 
19928                 this.vIndex = this.vIndex + dir * 4;
19929                 
19930                 if(this.vIndex < 0){
19931                     this.vIndex = 0;
19932                 }
19933                 
19934                 if(this.vIndex > 11){
19935                     this.vIndex = 11;
19936                 }
19937                 
19938                 if(isNaN(this.vIndex)){
19939                     this.vIndex = 0;
19940                 }
19941                 
19942                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19943                 break;
19944                 
19945             case 13: // enter
19946                 
19947                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19948                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19949                 }
19950                 
19951                 this.hide();
19952                 e.preventDefault();
19953                 break;
19954             case 9: // tab
19955                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19956                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19957                 }
19958                 this.hide();
19959                 break;
19960             case 16: // shift
19961             case 17: // ctrl
19962             case 18: // alt
19963                 break;
19964             default :
19965                 this.hide();
19966                 
19967         }
19968     },
19969     
19970     remove: function() 
19971     {
19972         this.picker().remove();
19973     }
19974    
19975 });
19976
19977 Roo.apply(Roo.bootstrap.MonthField,  {
19978     
19979     content : {
19980         tag: 'tbody',
19981         cn: [
19982         {
19983             tag: 'tr',
19984             cn: [
19985             {
19986                 tag: 'td',
19987                 colspan: '7'
19988             }
19989             ]
19990         }
19991         ]
19992     },
19993     
19994     dates:{
19995         en: {
19996             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19997             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19998         }
19999     }
20000 });
20001
20002 Roo.apply(Roo.bootstrap.MonthField,  {
20003   
20004     template : {
20005         tag: 'div',
20006         cls: 'datepicker dropdown-menu roo-dynamic',
20007         cn: [
20008             {
20009                 tag: 'div',
20010                 cls: 'datepicker-months',
20011                 cn: [
20012                 {
20013                     tag: 'table',
20014                     cls: 'table-condensed',
20015                     cn:[
20016                         Roo.bootstrap.DateField.content
20017                     ]
20018                 }
20019                 ]
20020             }
20021         ]
20022     }
20023 });
20024
20025  
20026
20027  
20028  /*
20029  * - LGPL
20030  *
20031  * CheckBox
20032  * 
20033  */
20034
20035 /**
20036  * @class Roo.bootstrap.CheckBox
20037  * @extends Roo.bootstrap.Input
20038  * Bootstrap CheckBox class
20039  * 
20040  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20041  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20042  * @cfg {String} boxLabel The text that appears beside the checkbox
20043  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20044  * @cfg {Boolean} checked initnal the element
20045  * @cfg {Boolean} inline inline the element (default false)
20046  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20047  * 
20048  * @constructor
20049  * Create a new CheckBox
20050  * @param {Object} config The config object
20051  */
20052
20053 Roo.bootstrap.CheckBox = function(config){
20054     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20055    
20056     this.addEvents({
20057         /**
20058         * @event check
20059         * Fires when the element is checked or unchecked.
20060         * @param {Roo.bootstrap.CheckBox} this This input
20061         * @param {Boolean} checked The new checked value
20062         */
20063        check : true
20064     });
20065     
20066 };
20067
20068 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20069   
20070     inputType: 'checkbox',
20071     inputValue: 1,
20072     valueOff: 0,
20073     boxLabel: false,
20074     checked: false,
20075     weight : false,
20076     inline: false,
20077     
20078     getAutoCreate : function()
20079     {
20080         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20081         
20082         var id = Roo.id();
20083         
20084         var cfg = {};
20085         
20086         cfg.cls = 'form-group ' + this.inputType; //input-group
20087         
20088         if(this.inline){
20089             cfg.cls += ' ' + this.inputType + '-inline';
20090         }
20091         
20092         var input =  {
20093             tag: 'input',
20094             id : id,
20095             type : this.inputType,
20096             value : this.inputValue,
20097             cls : 'roo-' + this.inputType, //'form-box',
20098             placeholder : this.placeholder || ''
20099             
20100         };
20101         
20102         if(this.inputType != 'radio'){
20103             var hidden =  {
20104                 tag: 'input',
20105                 type : 'hidden',
20106                 cls : 'roo-hidden-value',
20107                 value : this.checked ? this.valueOff : this.inputValue
20108             };
20109         }
20110         
20111             
20112         if (this.weight) { // Validity check?
20113             cfg.cls += " " + this.inputType + "-" + this.weight;
20114         }
20115         
20116         if (this.disabled) {
20117             input.disabled=true;
20118         }
20119         
20120         if(this.checked){
20121             input.checked = this.checked;
20122             
20123         }
20124         
20125         
20126         if (this.name) {
20127             
20128             input.name = this.name;
20129             
20130             if(this.inputType != 'radio'){
20131                 hidden.name = this.name;
20132                 input.name = '_hidden_' + this.name;
20133             }
20134         }
20135         
20136         if (this.size) {
20137             input.cls += ' input-' + this.size;
20138         }
20139         
20140         var settings=this;
20141         
20142         ['xs','sm','md','lg'].map(function(size){
20143             if (settings[size]) {
20144                 cfg.cls += ' col-' + size + '-' + settings[size];
20145             }
20146         });
20147         
20148         var inputblock = input;
20149          
20150         if (this.before || this.after) {
20151             
20152             inputblock = {
20153                 cls : 'input-group',
20154                 cn :  [] 
20155             };
20156             
20157             if (this.before) {
20158                 inputblock.cn.push({
20159                     tag :'span',
20160                     cls : 'input-group-addon',
20161                     html : this.before
20162                 });
20163             }
20164             
20165             inputblock.cn.push(input);
20166             
20167             if(this.inputType != 'radio'){
20168                 inputblock.cn.push(hidden);
20169             }
20170             
20171             if (this.after) {
20172                 inputblock.cn.push({
20173                     tag :'span',
20174                     cls : 'input-group-addon',
20175                     html : this.after
20176                 });
20177             }
20178             
20179         }
20180         
20181         if (align ==='left' && this.fieldLabel.length) {
20182 //                Roo.log("left and has label");
20183             cfg.cn = [
20184                 {
20185                     tag: 'label',
20186                     'for' :  id,
20187                     cls : 'control-label',
20188                     html : this.fieldLabel
20189
20190                 },
20191                 {
20192                     cls : "", 
20193                     cn: [
20194                         inputblock
20195                     ]
20196                 }
20197             ];
20198             
20199             if(this.labelWidth > 12){
20200                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20201             }
20202             
20203             if(this.labelWidth < 13 && this.labelmd == 0){
20204                 this.labelmd = this.labelWidth;
20205             }
20206             
20207             if(this.labellg > 0){
20208                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20209                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20210             }
20211             
20212             if(this.labelmd > 0){
20213                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20214                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20215             }
20216             
20217             if(this.labelsm > 0){
20218                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20219                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20220             }
20221             
20222             if(this.labelxs > 0){
20223                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20224                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20225             }
20226             
20227         } else if ( this.fieldLabel.length) {
20228 //                Roo.log(" label");
20229                 cfg.cn = [
20230                    
20231                     {
20232                         tag: this.boxLabel ? 'span' : 'label',
20233                         'for': id,
20234                         cls: 'control-label box-input-label',
20235                         //cls : 'input-group-addon',
20236                         html : this.fieldLabel
20237                         
20238                     },
20239                     
20240                     inputblock
20241                     
20242                 ];
20243
20244         } else {
20245             
20246 //                Roo.log(" no label && no align");
20247                 cfg.cn = [  inputblock ] ;
20248                 
20249                 
20250         }
20251         
20252         if(this.boxLabel){
20253              var boxLabelCfg = {
20254                 tag: 'label',
20255                 //'for': id, // box label is handled by onclick - so no for...
20256                 cls: 'box-label',
20257                 html: this.boxLabel
20258             };
20259             
20260             if(this.tooltip){
20261                 boxLabelCfg.tooltip = this.tooltip;
20262             }
20263              
20264             cfg.cn.push(boxLabelCfg);
20265         }
20266         
20267         if(this.inputType != 'radio'){
20268             cfg.cn.push(hidden);
20269         }
20270         
20271         return cfg;
20272         
20273     },
20274     
20275     /**
20276      * return the real input element.
20277      */
20278     inputEl: function ()
20279     {
20280         return this.el.select('input.roo-' + this.inputType,true).first();
20281     },
20282     hiddenEl: function ()
20283     {
20284         return this.el.select('input.roo-hidden-value',true).first();
20285     },
20286     
20287     labelEl: function()
20288     {
20289         return this.el.select('label.control-label',true).first();
20290     },
20291     /* depricated... */
20292     
20293     label: function()
20294     {
20295         return this.labelEl();
20296     },
20297     
20298     boxLabelEl: function()
20299     {
20300         return this.el.select('label.box-label',true).first();
20301     },
20302     
20303     initEvents : function()
20304     {
20305 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20306         
20307         this.inputEl().on('click', this.onClick,  this);
20308         
20309         if (this.boxLabel) { 
20310             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20311         }
20312         
20313         this.startValue = this.getValue();
20314         
20315         if(this.groupId){
20316             Roo.bootstrap.CheckBox.register(this);
20317         }
20318     },
20319     
20320     onClick : function()
20321     {   
20322         this.setChecked(!this.checked);
20323     },
20324     
20325     setChecked : function(state,suppressEvent)
20326     {
20327         this.startValue = this.getValue();
20328
20329         if(this.inputType == 'radio'){
20330             
20331             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20332                 e.dom.checked = false;
20333             });
20334             
20335             this.inputEl().dom.checked = true;
20336             
20337             this.inputEl().dom.value = this.inputValue;
20338             
20339             if(suppressEvent !== true){
20340                 this.fireEvent('check', this, true);
20341             }
20342             
20343             this.validate();
20344             
20345             return;
20346         }
20347         
20348         this.checked = state;
20349         
20350         this.inputEl().dom.checked = state;
20351         
20352         
20353         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20354         
20355         if(suppressEvent !== true){
20356             this.fireEvent('check', this, state);
20357         }
20358         
20359         this.validate();
20360     },
20361     
20362     getValue : function()
20363     {
20364         if(this.inputType == 'radio'){
20365             return this.getGroupValue();
20366         }
20367         
20368         return this.hiddenEl().dom.value;
20369         
20370     },
20371     
20372     getGroupValue : function()
20373     {
20374         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20375             return '';
20376         }
20377         
20378         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20379     },
20380     
20381     setValue : function(v,suppressEvent)
20382     {
20383         if(this.inputType == 'radio'){
20384             this.setGroupValue(v, suppressEvent);
20385             return;
20386         }
20387         
20388         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20389         
20390         this.validate();
20391     },
20392     
20393     setGroupValue : function(v, suppressEvent)
20394     {
20395         this.startValue = this.getValue();
20396         
20397         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20398             e.dom.checked = false;
20399             
20400             if(e.dom.value == v){
20401                 e.dom.checked = true;
20402             }
20403         });
20404         
20405         if(suppressEvent !== true){
20406             this.fireEvent('check', this, true);
20407         }
20408
20409         this.validate();
20410         
20411         return;
20412     },
20413     
20414     validate : function()
20415     {
20416         if(
20417                 this.disabled || 
20418                 (this.inputType == 'radio' && this.validateRadio()) ||
20419                 (this.inputType == 'checkbox' && this.validateCheckbox())
20420         ){
20421             this.markValid();
20422             return true;
20423         }
20424         
20425         this.markInvalid();
20426         return false;
20427     },
20428     
20429     validateRadio : function()
20430     {
20431         if(this.allowBlank){
20432             return true;
20433         }
20434         
20435         var valid = false;
20436         
20437         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438             if(!e.dom.checked){
20439                 return;
20440             }
20441             
20442             valid = true;
20443             
20444             return false;
20445         });
20446         
20447         return valid;
20448     },
20449     
20450     validateCheckbox : function()
20451     {
20452         if(!this.groupId){
20453             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20454             //return (this.getValue() == this.inputValue) ? true : false;
20455         }
20456         
20457         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20458         
20459         if(!group){
20460             return false;
20461         }
20462         
20463         var r = false;
20464         
20465         for(var i in group){
20466             if(r){
20467                 break;
20468             }
20469             
20470             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20471         }
20472         
20473         return r;
20474     },
20475     
20476     /**
20477      * Mark this field as valid
20478      */
20479     markValid : function()
20480     {
20481         var _this = this;
20482         
20483         this.fireEvent('valid', this);
20484         
20485         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20486         
20487         if(this.groupId){
20488             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20489         }
20490         
20491         if(label){
20492             label.markValid();
20493         }
20494
20495         if(this.inputType == 'radio'){
20496             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20497                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20498                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20499             });
20500             
20501             return;
20502         }
20503
20504         if(!this.groupId){
20505             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20506             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20507             return;
20508         }
20509         
20510         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20511         
20512         if(!group){
20513             return;
20514         }
20515         
20516         for(var i in group){
20517             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20518             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20519         }
20520     },
20521     
20522      /**
20523      * Mark this field as invalid
20524      * @param {String} msg The validation message
20525      */
20526     markInvalid : function(msg)
20527     {
20528         if(this.allowBlank){
20529             return;
20530         }
20531         
20532         var _this = this;
20533         
20534         this.fireEvent('invalid', this, msg);
20535         
20536         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20537         
20538         if(this.groupId){
20539             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20540         }
20541         
20542         if(label){
20543             label.markInvalid();
20544         }
20545             
20546         if(this.inputType == 'radio'){
20547             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20548                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20549                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20550             });
20551             
20552             return;
20553         }
20554         
20555         if(!this.groupId){
20556             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20557             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20558             return;
20559         }
20560         
20561         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20562         
20563         if(!group){
20564             return;
20565         }
20566         
20567         for(var i in group){
20568             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20569             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20570         }
20571         
20572     },
20573     
20574     clearInvalid : function()
20575     {
20576         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20577         
20578         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20579         
20580         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20581         
20582         if (label) {
20583             label.iconEl.removeClass(label.validClass);
20584             label.iconEl.removeClass(label.invalidClass);
20585         }
20586     },
20587     
20588     disable : function()
20589     {
20590         if(this.inputType != 'radio'){
20591             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20592             return;
20593         }
20594         
20595         var _this = this;
20596         
20597         if(this.rendered){
20598             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20599                 _this.getActionEl().addClass(this.disabledClass);
20600                 e.dom.disabled = true;
20601             });
20602         }
20603         
20604         this.disabled = true;
20605         this.fireEvent("disable", this);
20606         return this;
20607     },
20608
20609     enable : function()
20610     {
20611         if(this.inputType != 'radio'){
20612             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20613             return;
20614         }
20615         
20616         var _this = this;
20617         
20618         if(this.rendered){
20619             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20620                 _this.getActionEl().removeClass(this.disabledClass);
20621                 e.dom.disabled = false;
20622             });
20623         }
20624         
20625         this.disabled = false;
20626         this.fireEvent("enable", this);
20627         return this;
20628     }
20629
20630 });
20631
20632 Roo.apply(Roo.bootstrap.CheckBox, {
20633     
20634     groups: {},
20635     
20636      /**
20637     * register a CheckBox Group
20638     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20639     */
20640     register : function(checkbox)
20641     {
20642         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20643             this.groups[checkbox.groupId] = {};
20644         }
20645         
20646         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20647             return;
20648         }
20649         
20650         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20651         
20652     },
20653     /**
20654     * fetch a CheckBox Group based on the group ID
20655     * @param {string} the group ID
20656     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20657     */
20658     get: function(groupId) {
20659         if (typeof(this.groups[groupId]) == 'undefined') {
20660             return false;
20661         }
20662         
20663         return this.groups[groupId] ;
20664     }
20665     
20666     
20667 });
20668 /*
20669  * - LGPL
20670  *
20671  * RadioItem
20672  * 
20673  */
20674
20675 /**
20676  * @class Roo.bootstrap.Radio
20677  * @extends Roo.bootstrap.Component
20678  * Bootstrap Radio class
20679  * @cfg {String} boxLabel - the label associated
20680  * @cfg {String} value - the value of radio
20681  * 
20682  * @constructor
20683  * Create a new Radio
20684  * @param {Object} config The config object
20685  */
20686 Roo.bootstrap.Radio = function(config){
20687     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20688     
20689 };
20690
20691 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20692     
20693     boxLabel : '',
20694     
20695     value : '',
20696     
20697     getAutoCreate : function()
20698     {
20699         var cfg = {
20700             tag : 'div',
20701             cls : 'form-group radio',
20702             cn : [
20703                 {
20704                     tag : 'label',
20705                     cls : 'box-label',
20706                     html : this.boxLabel
20707                 }
20708             ]
20709         };
20710         
20711         return cfg;
20712     },
20713     
20714     initEvents : function() 
20715     {
20716         this.parent().register(this);
20717         
20718         this.el.on('click', this.onClick, this);
20719         
20720     },
20721     
20722     onClick : function()
20723     {
20724         this.setChecked(true);
20725     },
20726     
20727     setChecked : function(state, suppressEvent)
20728     {
20729         this.parent().setValue(this.value, suppressEvent);
20730         
20731     }
20732     
20733 });
20734  
20735
20736  /*
20737  * - LGPL
20738  *
20739  * Input
20740  * 
20741  */
20742
20743 /**
20744  * @class Roo.bootstrap.SecurePass
20745  * @extends Roo.bootstrap.Input
20746  * Bootstrap SecurePass class
20747  *
20748  * 
20749  * @constructor
20750  * Create a new SecurePass
20751  * @param {Object} config The config object
20752  */
20753  
20754 Roo.bootstrap.SecurePass = function (config) {
20755     // these go here, so the translation tool can replace them..
20756     this.errors = {
20757         PwdEmpty: "Please type a password, and then retype it to confirm.",
20758         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20759         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20760         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20761         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20762         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20763         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20764         TooWeak: "Your password is Too Weak."
20765     },
20766     this.meterLabel = "Password strength:";
20767     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20768     this.meterClass = [
20769         "roo-password-meter-tooweak", 
20770         "roo-password-meter-weak", 
20771         "roo-password-meter-medium", 
20772         "roo-password-meter-strong", 
20773         "roo-password-meter-grey"
20774     ];
20775     
20776     this.errors = {};
20777     
20778     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20779 }
20780
20781 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20782     /**
20783      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20784      * {
20785      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20786      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20787      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20788      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20789      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20790      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20791      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20792      * })
20793      */
20794     // private
20795     
20796     meterWidth: 300,
20797     errorMsg :'',    
20798     errors: false,
20799     imageRoot: '/',
20800     /**
20801      * @cfg {String/Object} Label for the strength meter (defaults to
20802      * 'Password strength:')
20803      */
20804     // private
20805     meterLabel: '',
20806     /**
20807      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20808      * ['Weak', 'Medium', 'Strong'])
20809      */
20810     // private    
20811     pwdStrengths: false,    
20812     // private
20813     strength: 0,
20814     // private
20815     _lastPwd: null,
20816     // private
20817     kCapitalLetter: 0,
20818     kSmallLetter: 1,
20819     kDigit: 2,
20820     kPunctuation: 3,
20821     
20822     insecure: false,
20823     // private
20824     initEvents: function ()
20825     {
20826         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20827
20828         if (this.el.is('input[type=password]') && Roo.isSafari) {
20829             this.el.on('keydown', this.SafariOnKeyDown, this);
20830         }
20831
20832         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20833     },
20834     // private
20835     onRender: function (ct, position)
20836     {
20837         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20838         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20839         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20840
20841         this.trigger.createChild({
20842                    cn: [
20843                     {
20844                     //id: 'PwdMeter',
20845                     tag: 'div',
20846                     cls: 'roo-password-meter-grey col-xs-12',
20847                     style: {
20848                         //width: 0,
20849                         //width: this.meterWidth + 'px'                                                
20850                         }
20851                     },
20852                     {                            
20853                          cls: 'roo-password-meter-text'                          
20854                     }
20855                 ]            
20856         });
20857
20858          
20859         if (this.hideTrigger) {
20860             this.trigger.setDisplayed(false);
20861         }
20862         this.setSize(this.width || '', this.height || '');
20863     },
20864     // private
20865     onDestroy: function ()
20866     {
20867         if (this.trigger) {
20868             this.trigger.removeAllListeners();
20869             this.trigger.remove();
20870         }
20871         if (this.wrap) {
20872             this.wrap.remove();
20873         }
20874         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20875     },
20876     // private
20877     checkStrength: function ()
20878     {
20879         var pwd = this.inputEl().getValue();
20880         if (pwd == this._lastPwd) {
20881             return;
20882         }
20883
20884         var strength;
20885         if (this.ClientSideStrongPassword(pwd)) {
20886             strength = 3;
20887         } else if (this.ClientSideMediumPassword(pwd)) {
20888             strength = 2;
20889         } else if (this.ClientSideWeakPassword(pwd)) {
20890             strength = 1;
20891         } else {
20892             strength = 0;
20893         }
20894         
20895         Roo.log('strength1: ' + strength);
20896         
20897         //var pm = this.trigger.child('div/div/div').dom;
20898         var pm = this.trigger.child('div/div');
20899         pm.removeClass(this.meterClass);
20900         pm.addClass(this.meterClass[strength]);
20901                 
20902         
20903         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20904                 
20905         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20906         
20907         this._lastPwd = pwd;
20908     },
20909     reset: function ()
20910     {
20911         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20912         
20913         this._lastPwd = '';
20914         
20915         var pm = this.trigger.child('div/div');
20916         pm.removeClass(this.meterClass);
20917         pm.addClass('roo-password-meter-grey');        
20918         
20919         
20920         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20921         
20922         pt.innerHTML = '';
20923         this.inputEl().dom.type='password';
20924     },
20925     // private
20926     validateValue: function (value)
20927     {
20928         
20929         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20930             return false;
20931         }
20932         if (value.length == 0) {
20933             if (this.allowBlank) {
20934                 this.clearInvalid();
20935                 return true;
20936             }
20937
20938             this.markInvalid(this.errors.PwdEmpty);
20939             this.errorMsg = this.errors.PwdEmpty;
20940             return false;
20941         }
20942         
20943         if(this.insecure){
20944             return true;
20945         }
20946         
20947         if ('[\x21-\x7e]*'.match(value)) {
20948             this.markInvalid(this.errors.PwdBadChar);
20949             this.errorMsg = this.errors.PwdBadChar;
20950             return false;
20951         }
20952         if (value.length < 6) {
20953             this.markInvalid(this.errors.PwdShort);
20954             this.errorMsg = this.errors.PwdShort;
20955             return false;
20956         }
20957         if (value.length > 16) {
20958             this.markInvalid(this.errors.PwdLong);
20959             this.errorMsg = this.errors.PwdLong;
20960             return false;
20961         }
20962         var strength;
20963         if (this.ClientSideStrongPassword(value)) {
20964             strength = 3;
20965         } else if (this.ClientSideMediumPassword(value)) {
20966             strength = 2;
20967         } else if (this.ClientSideWeakPassword(value)) {
20968             strength = 1;
20969         } else {
20970             strength = 0;
20971         }
20972
20973         
20974         if (strength < 2) {
20975             //this.markInvalid(this.errors.TooWeak);
20976             this.errorMsg = this.errors.TooWeak;
20977             //return false;
20978         }
20979         
20980         
20981         console.log('strength2: ' + strength);
20982         
20983         //var pm = this.trigger.child('div/div/div').dom;
20984         
20985         var pm = this.trigger.child('div/div');
20986         pm.removeClass(this.meterClass);
20987         pm.addClass(this.meterClass[strength]);
20988                 
20989         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20990                 
20991         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20992         
20993         this.errorMsg = ''; 
20994         return true;
20995     },
20996     // private
20997     CharacterSetChecks: function (type)
20998     {
20999         this.type = type;
21000         this.fResult = false;
21001     },
21002     // private
21003     isctype: function (character, type)
21004     {
21005         switch (type) {  
21006             case this.kCapitalLetter:
21007                 if (character >= 'A' && character <= 'Z') {
21008                     return true;
21009                 }
21010                 break;
21011             
21012             case this.kSmallLetter:
21013                 if (character >= 'a' && character <= 'z') {
21014                     return true;
21015                 }
21016                 break;
21017             
21018             case this.kDigit:
21019                 if (character >= '0' && character <= '9') {
21020                     return true;
21021                 }
21022                 break;
21023             
21024             case this.kPunctuation:
21025                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21026                     return true;
21027                 }
21028                 break;
21029             
21030             default:
21031                 return false;
21032         }
21033
21034     },
21035     // private
21036     IsLongEnough: function (pwd, size)
21037     {
21038         return !(pwd == null || isNaN(size) || pwd.length < size);
21039     },
21040     // private
21041     SpansEnoughCharacterSets: function (word, nb)
21042     {
21043         if (!this.IsLongEnough(word, nb))
21044         {
21045             return false;
21046         }
21047
21048         var characterSetChecks = new Array(
21049             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21050             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21051         );
21052         
21053         for (var index = 0; index < word.length; ++index) {
21054             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21055                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21056                     characterSetChecks[nCharSet].fResult = true;
21057                     break;
21058                 }
21059             }
21060         }
21061
21062         var nCharSets = 0;
21063         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21064             if (characterSetChecks[nCharSet].fResult) {
21065                 ++nCharSets;
21066             }
21067         }
21068
21069         if (nCharSets < nb) {
21070             return false;
21071         }
21072         return true;
21073     },
21074     // private
21075     ClientSideStrongPassword: function (pwd)
21076     {
21077         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21078     },
21079     // private
21080     ClientSideMediumPassword: function (pwd)
21081     {
21082         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21083     },
21084     // private
21085     ClientSideWeakPassword: function (pwd)
21086     {
21087         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21088     }
21089           
21090 })//<script type="text/javascript">
21091
21092 /*
21093  * Based  Ext JS Library 1.1.1
21094  * Copyright(c) 2006-2007, Ext JS, LLC.
21095  * LGPL
21096  *
21097  */
21098  
21099 /**
21100  * @class Roo.HtmlEditorCore
21101  * @extends Roo.Component
21102  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21103  *
21104  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21105  */
21106
21107 Roo.HtmlEditorCore = function(config){
21108     
21109     
21110     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21111     
21112     
21113     this.addEvents({
21114         /**
21115          * @event initialize
21116          * Fires when the editor is fully initialized (including the iframe)
21117          * @param {Roo.HtmlEditorCore} this
21118          */
21119         initialize: true,
21120         /**
21121          * @event activate
21122          * Fires when the editor is first receives the focus. Any insertion must wait
21123          * until after this event.
21124          * @param {Roo.HtmlEditorCore} this
21125          */
21126         activate: true,
21127          /**
21128          * @event beforesync
21129          * Fires before the textarea is updated with content from the editor iframe. Return false
21130          * to cancel the sync.
21131          * @param {Roo.HtmlEditorCore} this
21132          * @param {String} html
21133          */
21134         beforesync: true,
21135          /**
21136          * @event beforepush
21137          * Fires before the iframe editor is updated with content from the textarea. Return false
21138          * to cancel the push.
21139          * @param {Roo.HtmlEditorCore} this
21140          * @param {String} html
21141          */
21142         beforepush: true,
21143          /**
21144          * @event sync
21145          * Fires when the textarea is updated with content from the editor iframe.
21146          * @param {Roo.HtmlEditorCore} this
21147          * @param {String} html
21148          */
21149         sync: true,
21150          /**
21151          * @event push
21152          * Fires when the iframe editor is updated with content from the textarea.
21153          * @param {Roo.HtmlEditorCore} this
21154          * @param {String} html
21155          */
21156         push: true,
21157         
21158         /**
21159          * @event editorevent
21160          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21161          * @param {Roo.HtmlEditorCore} this
21162          */
21163         editorevent: true
21164         
21165     });
21166     
21167     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21168     
21169     // defaults : white / black...
21170     this.applyBlacklists();
21171     
21172     
21173     
21174 };
21175
21176
21177 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21178
21179
21180      /**
21181      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21182      */
21183     
21184     owner : false,
21185     
21186      /**
21187      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21188      *                        Roo.resizable.
21189      */
21190     resizable : false,
21191      /**
21192      * @cfg {Number} height (in pixels)
21193      */   
21194     height: 300,
21195    /**
21196      * @cfg {Number} width (in pixels)
21197      */   
21198     width: 500,
21199     
21200     /**
21201      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21202      * 
21203      */
21204     stylesheets: false,
21205     
21206     // id of frame..
21207     frameId: false,
21208     
21209     // private properties
21210     validationEvent : false,
21211     deferHeight: true,
21212     initialized : false,
21213     activated : false,
21214     sourceEditMode : false,
21215     onFocus : Roo.emptyFn,
21216     iframePad:3,
21217     hideMode:'offsets',
21218     
21219     clearUp: true,
21220     
21221     // blacklist + whitelisted elements..
21222     black: false,
21223     white: false,
21224      
21225     
21226
21227     /**
21228      * Protected method that will not generally be called directly. It
21229      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21230      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21231      */
21232     getDocMarkup : function(){
21233         // body styles..
21234         var st = '';
21235         
21236         // inherit styels from page...?? 
21237         if (this.stylesheets === false) {
21238             
21239             Roo.get(document.head).select('style').each(function(node) {
21240                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21241             });
21242             
21243             Roo.get(document.head).select('link').each(function(node) { 
21244                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21245             });
21246             
21247         } else if (!this.stylesheets.length) {
21248                 // simple..
21249                 st = '<style type="text/css">' +
21250                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21251                    '</style>';
21252         } else { 
21253             
21254         }
21255         
21256         st +=  '<style type="text/css">' +
21257             'IMG { cursor: pointer } ' +
21258         '</style>';
21259
21260         
21261         return '<html><head>' + st  +
21262             //<style type="text/css">' +
21263             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21264             //'</style>' +
21265             ' </head><body class="roo-htmleditor-body"></body></html>';
21266     },
21267
21268     // private
21269     onRender : function(ct, position)
21270     {
21271         var _t = this;
21272         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21273         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21274         
21275         
21276         this.el.dom.style.border = '0 none';
21277         this.el.dom.setAttribute('tabIndex', -1);
21278         this.el.addClass('x-hidden hide');
21279         
21280         
21281         
21282         if(Roo.isIE){ // fix IE 1px bogus margin
21283             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21284         }
21285        
21286         
21287         this.frameId = Roo.id();
21288         
21289          
21290         
21291         var iframe = this.owner.wrap.createChild({
21292             tag: 'iframe',
21293             cls: 'form-control', // bootstrap..
21294             id: this.frameId,
21295             name: this.frameId,
21296             frameBorder : 'no',
21297             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21298         }, this.el
21299         );
21300         
21301         
21302         this.iframe = iframe.dom;
21303
21304          this.assignDocWin();
21305         
21306         this.doc.designMode = 'on';
21307        
21308         this.doc.open();
21309         this.doc.write(this.getDocMarkup());
21310         this.doc.close();
21311
21312         
21313         var task = { // must defer to wait for browser to be ready
21314             run : function(){
21315                 //console.log("run task?" + this.doc.readyState);
21316                 this.assignDocWin();
21317                 if(this.doc.body || this.doc.readyState == 'complete'){
21318                     try {
21319                         this.doc.designMode="on";
21320                     } catch (e) {
21321                         return;
21322                     }
21323                     Roo.TaskMgr.stop(task);
21324                     this.initEditor.defer(10, this);
21325                 }
21326             },
21327             interval : 10,
21328             duration: 10000,
21329             scope: this
21330         };
21331         Roo.TaskMgr.start(task);
21332
21333     },
21334
21335     // private
21336     onResize : function(w, h)
21337     {
21338          Roo.log('resize: ' +w + ',' + h );
21339         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21340         if(!this.iframe){
21341             return;
21342         }
21343         if(typeof w == 'number'){
21344             
21345             this.iframe.style.width = w + 'px';
21346         }
21347         if(typeof h == 'number'){
21348             
21349             this.iframe.style.height = h + 'px';
21350             if(this.doc){
21351                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21352             }
21353         }
21354         
21355     },
21356
21357     /**
21358      * Toggles the editor between standard and source edit mode.
21359      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21360      */
21361     toggleSourceEdit : function(sourceEditMode){
21362         
21363         this.sourceEditMode = sourceEditMode === true;
21364         
21365         if(this.sourceEditMode){
21366  
21367             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21368             
21369         }else{
21370             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21371             //this.iframe.className = '';
21372             this.deferFocus();
21373         }
21374         //this.setSize(this.owner.wrap.getSize());
21375         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21376     },
21377
21378     
21379   
21380
21381     /**
21382      * Protected method that will not generally be called directly. If you need/want
21383      * custom HTML cleanup, this is the method you should override.
21384      * @param {String} html The HTML to be cleaned
21385      * return {String} The cleaned HTML
21386      */
21387     cleanHtml : function(html){
21388         html = String(html);
21389         if(html.length > 5){
21390             if(Roo.isSafari){ // strip safari nonsense
21391                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21392             }
21393         }
21394         if(html == '&nbsp;'){
21395             html = '';
21396         }
21397         return html;
21398     },
21399
21400     /**
21401      * HTML Editor -> Textarea
21402      * Protected method that will not generally be called directly. Syncs the contents
21403      * of the editor iframe with the textarea.
21404      */
21405     syncValue : function(){
21406         if(this.initialized){
21407             var bd = (this.doc.body || this.doc.documentElement);
21408             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21409             var html = bd.innerHTML;
21410             if(Roo.isSafari){
21411                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21412                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21413                 if(m && m[1]){
21414                     html = '<div style="'+m[0]+'">' + html + '</div>';
21415                 }
21416             }
21417             html = this.cleanHtml(html);
21418             // fix up the special chars.. normaly like back quotes in word...
21419             // however we do not want to do this with chinese..
21420             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21421                 var cc = b.charCodeAt();
21422                 if (
21423                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21424                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21425                     (cc >= 0xf900 && cc < 0xfb00 )
21426                 ) {
21427                         return b;
21428                 }
21429                 return "&#"+cc+";" 
21430             });
21431             if(this.owner.fireEvent('beforesync', this, html) !== false){
21432                 this.el.dom.value = html;
21433                 this.owner.fireEvent('sync', this, html);
21434             }
21435         }
21436     },
21437
21438     /**
21439      * Protected method that will not generally be called directly. Pushes the value of the textarea
21440      * into the iframe editor.
21441      */
21442     pushValue : function(){
21443         if(this.initialized){
21444             var v = this.el.dom.value.trim();
21445             
21446 //            if(v.length < 1){
21447 //                v = '&#160;';
21448 //            }
21449             
21450             if(this.owner.fireEvent('beforepush', this, v) !== false){
21451                 var d = (this.doc.body || this.doc.documentElement);
21452                 d.innerHTML = v;
21453                 this.cleanUpPaste();
21454                 this.el.dom.value = d.innerHTML;
21455                 this.owner.fireEvent('push', this, v);
21456             }
21457         }
21458     },
21459
21460     // private
21461     deferFocus : function(){
21462         this.focus.defer(10, this);
21463     },
21464
21465     // doc'ed in Field
21466     focus : function(){
21467         if(this.win && !this.sourceEditMode){
21468             this.win.focus();
21469         }else{
21470             this.el.focus();
21471         }
21472     },
21473     
21474     assignDocWin: function()
21475     {
21476         var iframe = this.iframe;
21477         
21478          if(Roo.isIE){
21479             this.doc = iframe.contentWindow.document;
21480             this.win = iframe.contentWindow;
21481         } else {
21482 //            if (!Roo.get(this.frameId)) {
21483 //                return;
21484 //            }
21485 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21486 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21487             
21488             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21489                 return;
21490             }
21491             
21492             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21493             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21494         }
21495     },
21496     
21497     // private
21498     initEditor : function(){
21499         //console.log("INIT EDITOR");
21500         this.assignDocWin();
21501         
21502         
21503         
21504         this.doc.designMode="on";
21505         this.doc.open();
21506         this.doc.write(this.getDocMarkup());
21507         this.doc.close();
21508         
21509         var dbody = (this.doc.body || this.doc.documentElement);
21510         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21511         // this copies styles from the containing element into thsi one..
21512         // not sure why we need all of this..
21513         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21514         
21515         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21516         //ss['background-attachment'] = 'fixed'; // w3c
21517         dbody.bgProperties = 'fixed'; // ie
21518         //Roo.DomHelper.applyStyles(dbody, ss);
21519         Roo.EventManager.on(this.doc, {
21520             //'mousedown': this.onEditorEvent,
21521             'mouseup': this.onEditorEvent,
21522             'dblclick': this.onEditorEvent,
21523             'click': this.onEditorEvent,
21524             'keyup': this.onEditorEvent,
21525             buffer:100,
21526             scope: this
21527         });
21528         if(Roo.isGecko){
21529             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21530         }
21531         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21532             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21533         }
21534         this.initialized = true;
21535
21536         this.owner.fireEvent('initialize', this);
21537         this.pushValue();
21538     },
21539
21540     // private
21541     onDestroy : function(){
21542         
21543         
21544         
21545         if(this.rendered){
21546             
21547             //for (var i =0; i < this.toolbars.length;i++) {
21548             //    // fixme - ask toolbars for heights?
21549             //    this.toolbars[i].onDestroy();
21550            // }
21551             
21552             //this.wrap.dom.innerHTML = '';
21553             //this.wrap.remove();
21554         }
21555     },
21556
21557     // private
21558     onFirstFocus : function(){
21559         
21560         this.assignDocWin();
21561         
21562         
21563         this.activated = true;
21564          
21565     
21566         if(Roo.isGecko){ // prevent silly gecko errors
21567             this.win.focus();
21568             var s = this.win.getSelection();
21569             if(!s.focusNode || s.focusNode.nodeType != 3){
21570                 var r = s.getRangeAt(0);
21571                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21572                 r.collapse(true);
21573                 this.deferFocus();
21574             }
21575             try{
21576                 this.execCmd('useCSS', true);
21577                 this.execCmd('styleWithCSS', false);
21578             }catch(e){}
21579         }
21580         this.owner.fireEvent('activate', this);
21581     },
21582
21583     // private
21584     adjustFont: function(btn){
21585         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21586         //if(Roo.isSafari){ // safari
21587         //    adjust *= 2;
21588        // }
21589         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21590         if(Roo.isSafari){ // safari
21591             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21592             v =  (v < 10) ? 10 : v;
21593             v =  (v > 48) ? 48 : v;
21594             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21595             
21596         }
21597         
21598         
21599         v = Math.max(1, v+adjust);
21600         
21601         this.execCmd('FontSize', v  );
21602     },
21603
21604     onEditorEvent : function(e)
21605     {
21606         this.owner.fireEvent('editorevent', this, e);
21607       //  this.updateToolbar();
21608         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21609     },
21610
21611     insertTag : function(tg)
21612     {
21613         // could be a bit smarter... -> wrap the current selected tRoo..
21614         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21615             
21616             range = this.createRange(this.getSelection());
21617             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21618             wrappingNode.appendChild(range.extractContents());
21619             range.insertNode(wrappingNode);
21620
21621             return;
21622             
21623             
21624             
21625         }
21626         this.execCmd("formatblock",   tg);
21627         
21628     },
21629     
21630     insertText : function(txt)
21631     {
21632         
21633         
21634         var range = this.createRange();
21635         range.deleteContents();
21636                //alert(Sender.getAttribute('label'));
21637                
21638         range.insertNode(this.doc.createTextNode(txt));
21639     } ,
21640     
21641      
21642
21643     /**
21644      * Executes a Midas editor command on the editor document and performs necessary focus and
21645      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21646      * @param {String} cmd The Midas command
21647      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21648      */
21649     relayCmd : function(cmd, value){
21650         this.win.focus();
21651         this.execCmd(cmd, value);
21652         this.owner.fireEvent('editorevent', this);
21653         //this.updateToolbar();
21654         this.owner.deferFocus();
21655     },
21656
21657     /**
21658      * Executes a Midas editor command directly on the editor document.
21659      * For visual commands, you should use {@link #relayCmd} instead.
21660      * <b>This should only be called after the editor is initialized.</b>
21661      * @param {String} cmd The Midas command
21662      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21663      */
21664     execCmd : function(cmd, value){
21665         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21666         this.syncValue();
21667     },
21668  
21669  
21670    
21671     /**
21672      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21673      * to insert tRoo.
21674      * @param {String} text | dom node.. 
21675      */
21676     insertAtCursor : function(text)
21677     {
21678         
21679         if(!this.activated){
21680             return;
21681         }
21682         /*
21683         if(Roo.isIE){
21684             this.win.focus();
21685             var r = this.doc.selection.createRange();
21686             if(r){
21687                 r.collapse(true);
21688                 r.pasteHTML(text);
21689                 this.syncValue();
21690                 this.deferFocus();
21691             
21692             }
21693             return;
21694         }
21695         */
21696         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21697             this.win.focus();
21698             
21699             
21700             // from jquery ui (MIT licenced)
21701             var range, node;
21702             var win = this.win;
21703             
21704             if (win.getSelection && win.getSelection().getRangeAt) {
21705                 range = win.getSelection().getRangeAt(0);
21706                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21707                 range.insertNode(node);
21708             } else if (win.document.selection && win.document.selection.createRange) {
21709                 // no firefox support
21710                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21711                 win.document.selection.createRange().pasteHTML(txt);
21712             } else {
21713                 // no firefox support
21714                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21715                 this.execCmd('InsertHTML', txt);
21716             } 
21717             
21718             this.syncValue();
21719             
21720             this.deferFocus();
21721         }
21722     },
21723  // private
21724     mozKeyPress : function(e){
21725         if(e.ctrlKey){
21726             var c = e.getCharCode(), cmd;
21727           
21728             if(c > 0){
21729                 c = String.fromCharCode(c).toLowerCase();
21730                 switch(c){
21731                     case 'b':
21732                         cmd = 'bold';
21733                         break;
21734                     case 'i':
21735                         cmd = 'italic';
21736                         break;
21737                     
21738                     case 'u':
21739                         cmd = 'underline';
21740                         break;
21741                     
21742                     case 'v':
21743                         this.cleanUpPaste.defer(100, this);
21744                         return;
21745                         
21746                 }
21747                 if(cmd){
21748                     this.win.focus();
21749                     this.execCmd(cmd);
21750                     this.deferFocus();
21751                     e.preventDefault();
21752                 }
21753                 
21754             }
21755         }
21756     },
21757
21758     // private
21759     fixKeys : function(){ // load time branching for fastest keydown performance
21760         if(Roo.isIE){
21761             return function(e){
21762                 var k = e.getKey(), r;
21763                 if(k == e.TAB){
21764                     e.stopEvent();
21765                     r = this.doc.selection.createRange();
21766                     if(r){
21767                         r.collapse(true);
21768                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21769                         this.deferFocus();
21770                     }
21771                     return;
21772                 }
21773                 
21774                 if(k == e.ENTER){
21775                     r = this.doc.selection.createRange();
21776                     if(r){
21777                         var target = r.parentElement();
21778                         if(!target || target.tagName.toLowerCase() != 'li'){
21779                             e.stopEvent();
21780                             r.pasteHTML('<br />');
21781                             r.collapse(false);
21782                             r.select();
21783                         }
21784                     }
21785                 }
21786                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21787                     this.cleanUpPaste.defer(100, this);
21788                     return;
21789                 }
21790                 
21791                 
21792             };
21793         }else if(Roo.isOpera){
21794             return function(e){
21795                 var k = e.getKey();
21796                 if(k == e.TAB){
21797                     e.stopEvent();
21798                     this.win.focus();
21799                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21800                     this.deferFocus();
21801                 }
21802                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21803                     this.cleanUpPaste.defer(100, this);
21804                     return;
21805                 }
21806                 
21807             };
21808         }else if(Roo.isSafari){
21809             return function(e){
21810                 var k = e.getKey();
21811                 
21812                 if(k == e.TAB){
21813                     e.stopEvent();
21814                     this.execCmd('InsertText','\t');
21815                     this.deferFocus();
21816                     return;
21817                 }
21818                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21819                     this.cleanUpPaste.defer(100, this);
21820                     return;
21821                 }
21822                 
21823              };
21824         }
21825     }(),
21826     
21827     getAllAncestors: function()
21828     {
21829         var p = this.getSelectedNode();
21830         var a = [];
21831         if (!p) {
21832             a.push(p); // push blank onto stack..
21833             p = this.getParentElement();
21834         }
21835         
21836         
21837         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21838             a.push(p);
21839             p = p.parentNode;
21840         }
21841         a.push(this.doc.body);
21842         return a;
21843     },
21844     lastSel : false,
21845     lastSelNode : false,
21846     
21847     
21848     getSelection : function() 
21849     {
21850         this.assignDocWin();
21851         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21852     },
21853     
21854     getSelectedNode: function() 
21855     {
21856         // this may only work on Gecko!!!
21857         
21858         // should we cache this!!!!
21859         
21860         
21861         
21862          
21863         var range = this.createRange(this.getSelection()).cloneRange();
21864         
21865         if (Roo.isIE) {
21866             var parent = range.parentElement();
21867             while (true) {
21868                 var testRange = range.duplicate();
21869                 testRange.moveToElementText(parent);
21870                 if (testRange.inRange(range)) {
21871                     break;
21872                 }
21873                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21874                     break;
21875                 }
21876                 parent = parent.parentElement;
21877             }
21878             return parent;
21879         }
21880         
21881         // is ancestor a text element.
21882         var ac =  range.commonAncestorContainer;
21883         if (ac.nodeType == 3) {
21884             ac = ac.parentNode;
21885         }
21886         
21887         var ar = ac.childNodes;
21888          
21889         var nodes = [];
21890         var other_nodes = [];
21891         var has_other_nodes = false;
21892         for (var i=0;i<ar.length;i++) {
21893             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21894                 continue;
21895             }
21896             // fullly contained node.
21897             
21898             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21899                 nodes.push(ar[i]);
21900                 continue;
21901             }
21902             
21903             // probably selected..
21904             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21905                 other_nodes.push(ar[i]);
21906                 continue;
21907             }
21908             // outer..
21909             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21910                 continue;
21911             }
21912             
21913             
21914             has_other_nodes = true;
21915         }
21916         if (!nodes.length && other_nodes.length) {
21917             nodes= other_nodes;
21918         }
21919         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21920             return false;
21921         }
21922         
21923         return nodes[0];
21924     },
21925     createRange: function(sel)
21926     {
21927         // this has strange effects when using with 
21928         // top toolbar - not sure if it's a great idea.
21929         //this.editor.contentWindow.focus();
21930         if (typeof sel != "undefined") {
21931             try {
21932                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21933             } catch(e) {
21934                 return this.doc.createRange();
21935             }
21936         } else {
21937             return this.doc.createRange();
21938         }
21939     },
21940     getParentElement: function()
21941     {
21942         
21943         this.assignDocWin();
21944         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21945         
21946         var range = this.createRange(sel);
21947          
21948         try {
21949             var p = range.commonAncestorContainer;
21950             while (p.nodeType == 3) { // text node
21951                 p = p.parentNode;
21952             }
21953             return p;
21954         } catch (e) {
21955             return null;
21956         }
21957     
21958     },
21959     /***
21960      *
21961      * Range intersection.. the hard stuff...
21962      *  '-1' = before
21963      *  '0' = hits..
21964      *  '1' = after.
21965      *         [ -- selected range --- ]
21966      *   [fail]                        [fail]
21967      *
21968      *    basically..
21969      *      if end is before start or  hits it. fail.
21970      *      if start is after end or hits it fail.
21971      *
21972      *   if either hits (but other is outside. - then it's not 
21973      *   
21974      *    
21975      **/
21976     
21977     
21978     // @see http://www.thismuchiknow.co.uk/?p=64.
21979     rangeIntersectsNode : function(range, node)
21980     {
21981         var nodeRange = node.ownerDocument.createRange();
21982         try {
21983             nodeRange.selectNode(node);
21984         } catch (e) {
21985             nodeRange.selectNodeContents(node);
21986         }
21987     
21988         var rangeStartRange = range.cloneRange();
21989         rangeStartRange.collapse(true);
21990     
21991         var rangeEndRange = range.cloneRange();
21992         rangeEndRange.collapse(false);
21993     
21994         var nodeStartRange = nodeRange.cloneRange();
21995         nodeStartRange.collapse(true);
21996     
21997         var nodeEndRange = nodeRange.cloneRange();
21998         nodeEndRange.collapse(false);
21999     
22000         return rangeStartRange.compareBoundaryPoints(
22001                  Range.START_TO_START, nodeEndRange) == -1 &&
22002                rangeEndRange.compareBoundaryPoints(
22003                  Range.START_TO_START, nodeStartRange) == 1;
22004         
22005          
22006     },
22007     rangeCompareNode : function(range, node)
22008     {
22009         var nodeRange = node.ownerDocument.createRange();
22010         try {
22011             nodeRange.selectNode(node);
22012         } catch (e) {
22013             nodeRange.selectNodeContents(node);
22014         }
22015         
22016         
22017         range.collapse(true);
22018     
22019         nodeRange.collapse(true);
22020      
22021         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22022         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22023          
22024         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22025         
22026         var nodeIsBefore   =  ss == 1;
22027         var nodeIsAfter    = ee == -1;
22028         
22029         if (nodeIsBefore && nodeIsAfter) {
22030             return 0; // outer
22031         }
22032         if (!nodeIsBefore && nodeIsAfter) {
22033             return 1; //right trailed.
22034         }
22035         
22036         if (nodeIsBefore && !nodeIsAfter) {
22037             return 2;  // left trailed.
22038         }
22039         // fully contined.
22040         return 3;
22041     },
22042
22043     // private? - in a new class?
22044     cleanUpPaste :  function()
22045     {
22046         // cleans up the whole document..
22047         Roo.log('cleanuppaste');
22048         
22049         this.cleanUpChildren(this.doc.body);
22050         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22051         if (clean != this.doc.body.innerHTML) {
22052             this.doc.body.innerHTML = clean;
22053         }
22054         
22055     },
22056     
22057     cleanWordChars : function(input) {// change the chars to hex code
22058         var he = Roo.HtmlEditorCore;
22059         
22060         var output = input;
22061         Roo.each(he.swapCodes, function(sw) { 
22062             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22063             
22064             output = output.replace(swapper, sw[1]);
22065         });
22066         
22067         return output;
22068     },
22069     
22070     
22071     cleanUpChildren : function (n)
22072     {
22073         if (!n.childNodes.length) {
22074             return;
22075         }
22076         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22077            this.cleanUpChild(n.childNodes[i]);
22078         }
22079     },
22080     
22081     
22082         
22083     
22084     cleanUpChild : function (node)
22085     {
22086         var ed = this;
22087         //console.log(node);
22088         if (node.nodeName == "#text") {
22089             // clean up silly Windows -- stuff?
22090             return; 
22091         }
22092         if (node.nodeName == "#comment") {
22093             node.parentNode.removeChild(node);
22094             // clean up silly Windows -- stuff?
22095             return; 
22096         }
22097         var lcname = node.tagName.toLowerCase();
22098         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22099         // whitelist of tags..
22100         
22101         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22102             // remove node.
22103             node.parentNode.removeChild(node);
22104             return;
22105             
22106         }
22107         
22108         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22109         
22110         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22111         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22112         
22113         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22114         //    remove_keep_children = true;
22115         //}
22116         
22117         if (remove_keep_children) {
22118             this.cleanUpChildren(node);
22119             // inserts everything just before this node...
22120             while (node.childNodes.length) {
22121                 var cn = node.childNodes[0];
22122                 node.removeChild(cn);
22123                 node.parentNode.insertBefore(cn, node);
22124             }
22125             node.parentNode.removeChild(node);
22126             return;
22127         }
22128         
22129         if (!node.attributes || !node.attributes.length) {
22130             this.cleanUpChildren(node);
22131             return;
22132         }
22133         
22134         function cleanAttr(n,v)
22135         {
22136             
22137             if (v.match(/^\./) || v.match(/^\//)) {
22138                 return;
22139             }
22140             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22141                 return;
22142             }
22143             if (v.match(/^#/)) {
22144                 return;
22145             }
22146 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22147             node.removeAttribute(n);
22148             
22149         }
22150         
22151         var cwhite = this.cwhite;
22152         var cblack = this.cblack;
22153             
22154         function cleanStyle(n,v)
22155         {
22156             if (v.match(/expression/)) { //XSS?? should we even bother..
22157                 node.removeAttribute(n);
22158                 return;
22159             }
22160             
22161             var parts = v.split(/;/);
22162             var clean = [];
22163             
22164             Roo.each(parts, function(p) {
22165                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22166                 if (!p.length) {
22167                     return true;
22168                 }
22169                 var l = p.split(':').shift().replace(/\s+/g,'');
22170                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22171                 
22172                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22173 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22174                     //node.removeAttribute(n);
22175                     return true;
22176                 }
22177                 //Roo.log()
22178                 // only allow 'c whitelisted system attributes'
22179                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22181                     //node.removeAttribute(n);
22182                     return true;
22183                 }
22184                 
22185                 
22186                  
22187                 
22188                 clean.push(p);
22189                 return true;
22190             });
22191             if (clean.length) { 
22192                 node.setAttribute(n, clean.join(';'));
22193             } else {
22194                 node.removeAttribute(n);
22195             }
22196             
22197         }
22198         
22199         
22200         for (var i = node.attributes.length-1; i > -1 ; i--) {
22201             var a = node.attributes[i];
22202             //console.log(a);
22203             
22204             if (a.name.toLowerCase().substr(0,2)=='on')  {
22205                 node.removeAttribute(a.name);
22206                 continue;
22207             }
22208             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22209                 node.removeAttribute(a.name);
22210                 continue;
22211             }
22212             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22213                 cleanAttr(a.name,a.value); // fixme..
22214                 continue;
22215             }
22216             if (a.name == 'style') {
22217                 cleanStyle(a.name,a.value);
22218                 continue;
22219             }
22220             /// clean up MS crap..
22221             // tecnically this should be a list of valid class'es..
22222             
22223             
22224             if (a.name == 'class') {
22225                 if (a.value.match(/^Mso/)) {
22226                     node.className = '';
22227                 }
22228                 
22229                 if (a.value.match(/^body$/)) {
22230                     node.className = '';
22231                 }
22232                 continue;
22233             }
22234             
22235             // style cleanup!?
22236             // class cleanup?
22237             
22238         }
22239         
22240         
22241         this.cleanUpChildren(node);
22242         
22243         
22244     },
22245     
22246     /**
22247      * Clean up MS wordisms...
22248      */
22249     cleanWord : function(node)
22250     {
22251         
22252         
22253         if (!node) {
22254             this.cleanWord(this.doc.body);
22255             return;
22256         }
22257         if (node.nodeName == "#text") {
22258             // clean up silly Windows -- stuff?
22259             return; 
22260         }
22261         if (node.nodeName == "#comment") {
22262             node.parentNode.removeChild(node);
22263             // clean up silly Windows -- stuff?
22264             return; 
22265         }
22266         
22267         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22268             node.parentNode.removeChild(node);
22269             return;
22270         }
22271         
22272         // remove - but keep children..
22273         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22274             while (node.childNodes.length) {
22275                 var cn = node.childNodes[0];
22276                 node.removeChild(cn);
22277                 node.parentNode.insertBefore(cn, node);
22278             }
22279             node.parentNode.removeChild(node);
22280             this.iterateChildren(node, this.cleanWord);
22281             return;
22282         }
22283         // clean styles
22284         if (node.className.length) {
22285             
22286             var cn = node.className.split(/\W+/);
22287             var cna = [];
22288             Roo.each(cn, function(cls) {
22289                 if (cls.match(/Mso[a-zA-Z]+/)) {
22290                     return;
22291                 }
22292                 cna.push(cls);
22293             });
22294             node.className = cna.length ? cna.join(' ') : '';
22295             if (!cna.length) {
22296                 node.removeAttribute("class");
22297             }
22298         }
22299         
22300         if (node.hasAttribute("lang")) {
22301             node.removeAttribute("lang");
22302         }
22303         
22304         if (node.hasAttribute("style")) {
22305             
22306             var styles = node.getAttribute("style").split(";");
22307             var nstyle = [];
22308             Roo.each(styles, function(s) {
22309                 if (!s.match(/:/)) {
22310                     return;
22311                 }
22312                 var kv = s.split(":");
22313                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22314                     return;
22315                 }
22316                 // what ever is left... we allow.
22317                 nstyle.push(s);
22318             });
22319             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22320             if (!nstyle.length) {
22321                 node.removeAttribute('style');
22322             }
22323         }
22324         this.iterateChildren(node, this.cleanWord);
22325         
22326         
22327         
22328     },
22329     /**
22330      * iterateChildren of a Node, calling fn each time, using this as the scole..
22331      * @param {DomNode} node node to iterate children of.
22332      * @param {Function} fn method of this class to call on each item.
22333      */
22334     iterateChildren : function(node, fn)
22335     {
22336         if (!node.childNodes.length) {
22337                 return;
22338         }
22339         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22340            fn.call(this, node.childNodes[i])
22341         }
22342     },
22343     
22344     
22345     /**
22346      * cleanTableWidths.
22347      *
22348      * Quite often pasting from word etc.. results in tables with column and widths.
22349      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22350      *
22351      */
22352     cleanTableWidths : function(node)
22353     {
22354          
22355          
22356         if (!node) {
22357             this.cleanTableWidths(this.doc.body);
22358             return;
22359         }
22360         
22361         // ignore list...
22362         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22363             return; 
22364         }
22365         Roo.log(node.tagName);
22366         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22367             this.iterateChildren(node, this.cleanTableWidths);
22368             return;
22369         }
22370         if (node.hasAttribute('width')) {
22371             node.removeAttribute('width');
22372         }
22373         
22374          
22375         if (node.hasAttribute("style")) {
22376             // pretty basic...
22377             
22378             var styles = node.getAttribute("style").split(";");
22379             var nstyle = [];
22380             Roo.each(styles, function(s) {
22381                 if (!s.match(/:/)) {
22382                     return;
22383                 }
22384                 var kv = s.split(":");
22385                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22386                     return;
22387                 }
22388                 // what ever is left... we allow.
22389                 nstyle.push(s);
22390             });
22391             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22392             if (!nstyle.length) {
22393                 node.removeAttribute('style');
22394             }
22395         }
22396         
22397         this.iterateChildren(node, this.cleanTableWidths);
22398         
22399         
22400     },
22401     
22402     
22403     
22404     
22405     domToHTML : function(currentElement, depth, nopadtext) {
22406         
22407         depth = depth || 0;
22408         nopadtext = nopadtext || false;
22409     
22410         if (!currentElement) {
22411             return this.domToHTML(this.doc.body);
22412         }
22413         
22414         //Roo.log(currentElement);
22415         var j;
22416         var allText = false;
22417         var nodeName = currentElement.nodeName;
22418         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22419         
22420         if  (nodeName == '#text') {
22421             
22422             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22423         }
22424         
22425         
22426         var ret = '';
22427         if (nodeName != 'BODY') {
22428              
22429             var i = 0;
22430             // Prints the node tagName, such as <A>, <IMG>, etc
22431             if (tagName) {
22432                 var attr = [];
22433                 for(i = 0; i < currentElement.attributes.length;i++) {
22434                     // quoting?
22435                     var aname = currentElement.attributes.item(i).name;
22436                     if (!currentElement.attributes.item(i).value.length) {
22437                         continue;
22438                     }
22439                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22440                 }
22441                 
22442                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22443             } 
22444             else {
22445                 
22446                 // eack
22447             }
22448         } else {
22449             tagName = false;
22450         }
22451         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22452             return ret;
22453         }
22454         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22455             nopadtext = true;
22456         }
22457         
22458         
22459         // Traverse the tree
22460         i = 0;
22461         var currentElementChild = currentElement.childNodes.item(i);
22462         var allText = true;
22463         var innerHTML  = '';
22464         lastnode = '';
22465         while (currentElementChild) {
22466             // Formatting code (indent the tree so it looks nice on the screen)
22467             var nopad = nopadtext;
22468             if (lastnode == 'SPAN') {
22469                 nopad  = true;
22470             }
22471             // text
22472             if  (currentElementChild.nodeName == '#text') {
22473                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22474                 toadd = nopadtext ? toadd : toadd.trim();
22475                 if (!nopad && toadd.length > 80) {
22476                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22477                 }
22478                 innerHTML  += toadd;
22479                 
22480                 i++;
22481                 currentElementChild = currentElement.childNodes.item(i);
22482                 lastNode = '';
22483                 continue;
22484             }
22485             allText = false;
22486             
22487             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22488                 
22489             // Recursively traverse the tree structure of the child node
22490             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22491             lastnode = currentElementChild.nodeName;
22492             i++;
22493             currentElementChild=currentElement.childNodes.item(i);
22494         }
22495         
22496         ret += innerHTML;
22497         
22498         if (!allText) {
22499                 // The remaining code is mostly for formatting the tree
22500             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22501         }
22502         
22503         
22504         if (tagName) {
22505             ret+= "</"+tagName+">";
22506         }
22507         return ret;
22508         
22509     },
22510         
22511     applyBlacklists : function()
22512     {
22513         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22514         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22515         
22516         this.white = [];
22517         this.black = [];
22518         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22519             if (b.indexOf(tag) > -1) {
22520                 return;
22521             }
22522             this.white.push(tag);
22523             
22524         }, this);
22525         
22526         Roo.each(w, function(tag) {
22527             if (b.indexOf(tag) > -1) {
22528                 return;
22529             }
22530             if (this.white.indexOf(tag) > -1) {
22531                 return;
22532             }
22533             this.white.push(tag);
22534             
22535         }, this);
22536         
22537         
22538         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22539             if (w.indexOf(tag) > -1) {
22540                 return;
22541             }
22542             this.black.push(tag);
22543             
22544         }, this);
22545         
22546         Roo.each(b, function(tag) {
22547             if (w.indexOf(tag) > -1) {
22548                 return;
22549             }
22550             if (this.black.indexOf(tag) > -1) {
22551                 return;
22552             }
22553             this.black.push(tag);
22554             
22555         }, this);
22556         
22557         
22558         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22559         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22560         
22561         this.cwhite = [];
22562         this.cblack = [];
22563         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22564             if (b.indexOf(tag) > -1) {
22565                 return;
22566             }
22567             this.cwhite.push(tag);
22568             
22569         }, this);
22570         
22571         Roo.each(w, function(tag) {
22572             if (b.indexOf(tag) > -1) {
22573                 return;
22574             }
22575             if (this.cwhite.indexOf(tag) > -1) {
22576                 return;
22577             }
22578             this.cwhite.push(tag);
22579             
22580         }, this);
22581         
22582         
22583         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22584             if (w.indexOf(tag) > -1) {
22585                 return;
22586             }
22587             this.cblack.push(tag);
22588             
22589         }, this);
22590         
22591         Roo.each(b, function(tag) {
22592             if (w.indexOf(tag) > -1) {
22593                 return;
22594             }
22595             if (this.cblack.indexOf(tag) > -1) {
22596                 return;
22597             }
22598             this.cblack.push(tag);
22599             
22600         }, this);
22601     },
22602     
22603     setStylesheets : function(stylesheets)
22604     {
22605         if(typeof(stylesheets) == 'string'){
22606             Roo.get(this.iframe.contentDocument.head).createChild({
22607                 tag : 'link',
22608                 rel : 'stylesheet',
22609                 type : 'text/css',
22610                 href : stylesheets
22611             });
22612             
22613             return;
22614         }
22615         var _this = this;
22616      
22617         Roo.each(stylesheets, function(s) {
22618             if(!s.length){
22619                 return;
22620             }
22621             
22622             Roo.get(_this.iframe.contentDocument.head).createChild({
22623                 tag : 'link',
22624                 rel : 'stylesheet',
22625                 type : 'text/css',
22626                 href : s
22627             });
22628         });
22629
22630         
22631     },
22632     
22633     removeStylesheets : function()
22634     {
22635         var _this = this;
22636         
22637         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22638             s.remove();
22639         });
22640     }
22641     
22642     // hide stuff that is not compatible
22643     /**
22644      * @event blur
22645      * @hide
22646      */
22647     /**
22648      * @event change
22649      * @hide
22650      */
22651     /**
22652      * @event focus
22653      * @hide
22654      */
22655     /**
22656      * @event specialkey
22657      * @hide
22658      */
22659     /**
22660      * @cfg {String} fieldClass @hide
22661      */
22662     /**
22663      * @cfg {String} focusClass @hide
22664      */
22665     /**
22666      * @cfg {String} autoCreate @hide
22667      */
22668     /**
22669      * @cfg {String} inputType @hide
22670      */
22671     /**
22672      * @cfg {String} invalidClass @hide
22673      */
22674     /**
22675      * @cfg {String} invalidText @hide
22676      */
22677     /**
22678      * @cfg {String} msgFx @hide
22679      */
22680     /**
22681      * @cfg {String} validateOnBlur @hide
22682      */
22683 });
22684
22685 Roo.HtmlEditorCore.white = [
22686         'area', 'br', 'img', 'input', 'hr', 'wbr',
22687         
22688        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22689        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22690        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22691        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22692        'table',   'ul',         'xmp', 
22693        
22694        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22695       'thead',   'tr', 
22696      
22697       'dir', 'menu', 'ol', 'ul', 'dl',
22698        
22699       'embed',  'object'
22700 ];
22701
22702
22703 Roo.HtmlEditorCore.black = [
22704     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22705         'applet', // 
22706         'base',   'basefont', 'bgsound', 'blink',  'body', 
22707         'frame',  'frameset', 'head',    'html',   'ilayer', 
22708         'iframe', 'layer',  'link',     'meta',    'object',   
22709         'script', 'style' ,'title',  'xml' // clean later..
22710 ];
22711 Roo.HtmlEditorCore.clean = [
22712     'script', 'style', 'title', 'xml'
22713 ];
22714 Roo.HtmlEditorCore.remove = [
22715     'font'
22716 ];
22717 // attributes..
22718
22719 Roo.HtmlEditorCore.ablack = [
22720     'on'
22721 ];
22722     
22723 Roo.HtmlEditorCore.aclean = [ 
22724     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22725 ];
22726
22727 // protocols..
22728 Roo.HtmlEditorCore.pwhite= [
22729         'http',  'https',  'mailto'
22730 ];
22731
22732 // white listed style attributes.
22733 Roo.HtmlEditorCore.cwhite= [
22734       //  'text-align', /// default is to allow most things..
22735       
22736          
22737 //        'font-size'//??
22738 ];
22739
22740 // black listed style attributes.
22741 Roo.HtmlEditorCore.cblack= [
22742       //  'font-size' -- this can be set by the project 
22743 ];
22744
22745
22746 Roo.HtmlEditorCore.swapCodes   =[ 
22747     [    8211, "--" ], 
22748     [    8212, "--" ], 
22749     [    8216,  "'" ],  
22750     [    8217, "'" ],  
22751     [    8220, '"' ],  
22752     [    8221, '"' ],  
22753     [    8226, "*" ],  
22754     [    8230, "..." ]
22755 ]; 
22756
22757     /*
22758  * - LGPL
22759  *
22760  * HtmlEditor
22761  * 
22762  */
22763
22764 /**
22765  * @class Roo.bootstrap.HtmlEditor
22766  * @extends Roo.bootstrap.TextArea
22767  * Bootstrap HtmlEditor class
22768
22769  * @constructor
22770  * Create a new HtmlEditor
22771  * @param {Object} config The config object
22772  */
22773
22774 Roo.bootstrap.HtmlEditor = function(config){
22775     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22776     if (!this.toolbars) {
22777         this.toolbars = [];
22778     }
22779     
22780     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22781     this.addEvents({
22782             /**
22783              * @event initialize
22784              * Fires when the editor is fully initialized (including the iframe)
22785              * @param {HtmlEditor} this
22786              */
22787             initialize: true,
22788             /**
22789              * @event activate
22790              * Fires when the editor is first receives the focus. Any insertion must wait
22791              * until after this event.
22792              * @param {HtmlEditor} this
22793              */
22794             activate: true,
22795              /**
22796              * @event beforesync
22797              * Fires before the textarea is updated with content from the editor iframe. Return false
22798              * to cancel the sync.
22799              * @param {HtmlEditor} this
22800              * @param {String} html
22801              */
22802             beforesync: true,
22803              /**
22804              * @event beforepush
22805              * Fires before the iframe editor is updated with content from the textarea. Return false
22806              * to cancel the push.
22807              * @param {HtmlEditor} this
22808              * @param {String} html
22809              */
22810             beforepush: true,
22811              /**
22812              * @event sync
22813              * Fires when the textarea is updated with content from the editor iframe.
22814              * @param {HtmlEditor} this
22815              * @param {String} html
22816              */
22817             sync: true,
22818              /**
22819              * @event push
22820              * Fires when the iframe editor is updated with content from the textarea.
22821              * @param {HtmlEditor} this
22822              * @param {String} html
22823              */
22824             push: true,
22825              /**
22826              * @event editmodechange
22827              * Fires when the editor switches edit modes
22828              * @param {HtmlEditor} this
22829              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22830              */
22831             editmodechange: true,
22832             /**
22833              * @event editorevent
22834              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22835              * @param {HtmlEditor} this
22836              */
22837             editorevent: true,
22838             /**
22839              * @event firstfocus
22840              * Fires when on first focus - needed by toolbars..
22841              * @param {HtmlEditor} this
22842              */
22843             firstfocus: true,
22844             /**
22845              * @event autosave
22846              * Auto save the htmlEditor value as a file into Events
22847              * @param {HtmlEditor} this
22848              */
22849             autosave: true,
22850             /**
22851              * @event savedpreview
22852              * preview the saved version of htmlEditor
22853              * @param {HtmlEditor} this
22854              */
22855             savedpreview: true
22856         });
22857 };
22858
22859
22860 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22861     
22862     
22863       /**
22864      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22865      */
22866     toolbars : false,
22867     
22868      /**
22869     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22870     */
22871     btns : [],
22872    
22873      /**
22874      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22875      *                        Roo.resizable.
22876      */
22877     resizable : false,
22878      /**
22879      * @cfg {Number} height (in pixels)
22880      */   
22881     height: 300,
22882    /**
22883      * @cfg {Number} width (in pixels)
22884      */   
22885     width: false,
22886     
22887     /**
22888      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22889      * 
22890      */
22891     stylesheets: false,
22892     
22893     // id of frame..
22894     frameId: false,
22895     
22896     // private properties
22897     validationEvent : false,
22898     deferHeight: true,
22899     initialized : false,
22900     activated : false,
22901     
22902     onFocus : Roo.emptyFn,
22903     iframePad:3,
22904     hideMode:'offsets',
22905     
22906     tbContainer : false,
22907     
22908     toolbarContainer :function() {
22909         return this.wrap.select('.x-html-editor-tb',true).first();
22910     },
22911
22912     /**
22913      * Protected method that will not generally be called directly. It
22914      * is called when the editor creates its toolbar. Override this method if you need to
22915      * add custom toolbar buttons.
22916      * @param {HtmlEditor} editor
22917      */
22918     createToolbar : function(){
22919         Roo.log('renewing');
22920         Roo.log("create toolbars");
22921         
22922         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22923         this.toolbars[0].render(this.toolbarContainer());
22924         
22925         return;
22926         
22927 //        if (!editor.toolbars || !editor.toolbars.length) {
22928 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22929 //        }
22930 //        
22931 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22932 //            editor.toolbars[i] = Roo.factory(
22933 //                    typeof(editor.toolbars[i]) == 'string' ?
22934 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22935 //                Roo.bootstrap.HtmlEditor);
22936 //            editor.toolbars[i].init(editor);
22937 //        }
22938     },
22939
22940      
22941     // private
22942     onRender : function(ct, position)
22943     {
22944        // Roo.log("Call onRender: " + this.xtype);
22945         var _t = this;
22946         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22947       
22948         this.wrap = this.inputEl().wrap({
22949             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22950         });
22951         
22952         this.editorcore.onRender(ct, position);
22953          
22954         if (this.resizable) {
22955             this.resizeEl = new Roo.Resizable(this.wrap, {
22956                 pinned : true,
22957                 wrap: true,
22958                 dynamic : true,
22959                 minHeight : this.height,
22960                 height: this.height,
22961                 handles : this.resizable,
22962                 width: this.width,
22963                 listeners : {
22964                     resize : function(r, w, h) {
22965                         _t.onResize(w,h); // -something
22966                     }
22967                 }
22968             });
22969             
22970         }
22971         this.createToolbar(this);
22972        
22973         
22974         if(!this.width && this.resizable){
22975             this.setSize(this.wrap.getSize());
22976         }
22977         if (this.resizeEl) {
22978             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22979             // should trigger onReize..
22980         }
22981         
22982     },
22983
22984     // private
22985     onResize : function(w, h)
22986     {
22987         Roo.log('resize: ' +w + ',' + h );
22988         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22989         var ew = false;
22990         var eh = false;
22991         
22992         if(this.inputEl() ){
22993             if(typeof w == 'number'){
22994                 var aw = w - this.wrap.getFrameWidth('lr');
22995                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22996                 ew = aw;
22997             }
22998             if(typeof h == 'number'){
22999                  var tbh = -11;  // fixme it needs to tool bar size!
23000                 for (var i =0; i < this.toolbars.length;i++) {
23001                     // fixme - ask toolbars for heights?
23002                     tbh += this.toolbars[i].el.getHeight();
23003                     //if (this.toolbars[i].footer) {
23004                     //    tbh += this.toolbars[i].footer.el.getHeight();
23005                     //}
23006                 }
23007               
23008                 
23009                 
23010                 
23011                 
23012                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23013                 ah -= 5; // knock a few pixes off for look..
23014                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23015                 var eh = ah;
23016             }
23017         }
23018         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23019         this.editorcore.onResize(ew,eh);
23020         
23021     },
23022
23023     /**
23024      * Toggles the editor between standard and source edit mode.
23025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23026      */
23027     toggleSourceEdit : function(sourceEditMode)
23028     {
23029         this.editorcore.toggleSourceEdit(sourceEditMode);
23030         
23031         if(this.editorcore.sourceEditMode){
23032             Roo.log('editor - showing textarea');
23033             
23034 //            Roo.log('in');
23035 //            Roo.log(this.syncValue());
23036             this.syncValue();
23037             this.inputEl().removeClass(['hide', 'x-hidden']);
23038             this.inputEl().dom.removeAttribute('tabIndex');
23039             this.inputEl().focus();
23040         }else{
23041             Roo.log('editor - hiding textarea');
23042 //            Roo.log('out')
23043 //            Roo.log(this.pushValue()); 
23044             this.pushValue();
23045             
23046             this.inputEl().addClass(['hide', 'x-hidden']);
23047             this.inputEl().dom.setAttribute('tabIndex', -1);
23048             //this.deferFocus();
23049         }
23050          
23051         if(this.resizable){
23052             this.setSize(this.wrap.getSize());
23053         }
23054         
23055         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23056     },
23057  
23058     // private (for BoxComponent)
23059     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23060
23061     // private (for BoxComponent)
23062     getResizeEl : function(){
23063         return this.wrap;
23064     },
23065
23066     // private (for BoxComponent)
23067     getPositionEl : function(){
23068         return this.wrap;
23069     },
23070
23071     // private
23072     initEvents : function(){
23073         this.originalValue = this.getValue();
23074     },
23075
23076 //    /**
23077 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23078 //     * @method
23079 //     */
23080 //    markInvalid : Roo.emptyFn,
23081 //    /**
23082 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23083 //     * @method
23084 //     */
23085 //    clearInvalid : Roo.emptyFn,
23086
23087     setValue : function(v){
23088         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23089         this.editorcore.pushValue();
23090     },
23091
23092      
23093     // private
23094     deferFocus : function(){
23095         this.focus.defer(10, this);
23096     },
23097
23098     // doc'ed in Field
23099     focus : function(){
23100         this.editorcore.focus();
23101         
23102     },
23103       
23104
23105     // private
23106     onDestroy : function(){
23107         
23108         
23109         
23110         if(this.rendered){
23111             
23112             for (var i =0; i < this.toolbars.length;i++) {
23113                 // fixme - ask toolbars for heights?
23114                 this.toolbars[i].onDestroy();
23115             }
23116             
23117             this.wrap.dom.innerHTML = '';
23118             this.wrap.remove();
23119         }
23120     },
23121
23122     // private
23123     onFirstFocus : function(){
23124         //Roo.log("onFirstFocus");
23125         this.editorcore.onFirstFocus();
23126          for (var i =0; i < this.toolbars.length;i++) {
23127             this.toolbars[i].onFirstFocus();
23128         }
23129         
23130     },
23131     
23132     // private
23133     syncValue : function()
23134     {   
23135         this.editorcore.syncValue();
23136     },
23137     
23138     pushValue : function()
23139     {   
23140         this.editorcore.pushValue();
23141     }
23142      
23143     
23144     // hide stuff that is not compatible
23145     /**
23146      * @event blur
23147      * @hide
23148      */
23149     /**
23150      * @event change
23151      * @hide
23152      */
23153     /**
23154      * @event focus
23155      * @hide
23156      */
23157     /**
23158      * @event specialkey
23159      * @hide
23160      */
23161     /**
23162      * @cfg {String} fieldClass @hide
23163      */
23164     /**
23165      * @cfg {String} focusClass @hide
23166      */
23167     /**
23168      * @cfg {String} autoCreate @hide
23169      */
23170     /**
23171      * @cfg {String} inputType @hide
23172      */
23173     /**
23174      * @cfg {String} invalidClass @hide
23175      */
23176     /**
23177      * @cfg {String} invalidText @hide
23178      */
23179     /**
23180      * @cfg {String} msgFx @hide
23181      */
23182     /**
23183      * @cfg {String} validateOnBlur @hide
23184      */
23185 });
23186  
23187     
23188    
23189    
23190    
23191       
23192 Roo.namespace('Roo.bootstrap.htmleditor');
23193 /**
23194  * @class Roo.bootstrap.HtmlEditorToolbar1
23195  * Basic Toolbar
23196  * 
23197  * Usage:
23198  *
23199  new Roo.bootstrap.HtmlEditor({
23200     ....
23201     toolbars : [
23202         new Roo.bootstrap.HtmlEditorToolbar1({
23203             disable : { fonts: 1 , format: 1, ..., ... , ...],
23204             btns : [ .... ]
23205         })
23206     }
23207      
23208  * 
23209  * @cfg {Object} disable List of elements to disable..
23210  * @cfg {Array} btns List of additional buttons.
23211  * 
23212  * 
23213  * NEEDS Extra CSS? 
23214  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23215  */
23216  
23217 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23218 {
23219     
23220     Roo.apply(this, config);
23221     
23222     // default disabled, based on 'good practice'..
23223     this.disable = this.disable || {};
23224     Roo.applyIf(this.disable, {
23225         fontSize : true,
23226         colors : true,
23227         specialElements : true
23228     });
23229     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23230     
23231     this.editor = config.editor;
23232     this.editorcore = config.editor.editorcore;
23233     
23234     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23235     
23236     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23237     // dont call parent... till later.
23238 }
23239 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23240      
23241     bar : true,
23242     
23243     editor : false,
23244     editorcore : false,
23245     
23246     
23247     formats : [
23248         "p" ,  
23249         "h1","h2","h3","h4","h5","h6", 
23250         "pre", "code", 
23251         "abbr", "acronym", "address", "cite", "samp", "var",
23252         'div','span'
23253     ],
23254     
23255     onRender : function(ct, position)
23256     {
23257        // Roo.log("Call onRender: " + this.xtype);
23258         
23259        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23260        Roo.log(this.el);
23261        this.el.dom.style.marginBottom = '0';
23262        var _this = this;
23263        var editorcore = this.editorcore;
23264        var editor= this.editor;
23265        
23266        var children = [];
23267        var btn = function(id,cmd , toggle, handler, html){
23268        
23269             var  event = toggle ? 'toggle' : 'click';
23270        
23271             var a = {
23272                 size : 'sm',
23273                 xtype: 'Button',
23274                 xns: Roo.bootstrap,
23275                 glyphicon : id,
23276                 cmd : id || cmd,
23277                 enableToggle:toggle !== false,
23278                 html : html || '',
23279                 pressed : toggle ? false : null,
23280                 listeners : {}
23281             };
23282             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23283                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23284             };
23285             children.push(a);
23286             return a;
23287        }
23288        
23289     //    var cb_box = function...
23290         
23291         var style = {
23292                 xtype: 'Button',
23293                 size : 'sm',
23294                 xns: Roo.bootstrap,
23295                 glyphicon : 'font',
23296                 //html : 'submit'
23297                 menu : {
23298                     xtype: 'Menu',
23299                     xns: Roo.bootstrap,
23300                     items:  []
23301                 }
23302         };
23303         Roo.each(this.formats, function(f) {
23304             style.menu.items.push({
23305                 xtype :'MenuItem',
23306                 xns: Roo.bootstrap,
23307                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23308                 tagname : f,
23309                 listeners : {
23310                     click : function()
23311                     {
23312                         editorcore.insertTag(this.tagname);
23313                         editor.focus();
23314                     }
23315                 }
23316                 
23317             });
23318         });
23319         children.push(style);   
23320         
23321         btn('bold',false,true);
23322         btn('italic',false,true);
23323         btn('align-left', 'justifyleft',true);
23324         btn('align-center', 'justifycenter',true);
23325         btn('align-right' , 'justifyright',true);
23326         btn('link', false, false, function(btn) {
23327             //Roo.log("create link?");
23328             var url = prompt(this.createLinkText, this.defaultLinkValue);
23329             if(url && url != 'http:/'+'/'){
23330                 this.editorcore.relayCmd('createlink', url);
23331             }
23332         }),
23333         btn('list','insertunorderedlist',true);
23334         btn('pencil', false,true, function(btn){
23335                 Roo.log(this);
23336                 this.toggleSourceEdit(btn.pressed);
23337         });
23338         
23339         if (this.editor.btns.length > 0) {
23340             for (var i = 0; i<this.editor.btns.length; i++) {
23341                 children.push(this.editor.btns[i]);
23342             }
23343         }
23344         
23345         /*
23346         var cog = {
23347                 xtype: 'Button',
23348                 size : 'sm',
23349                 xns: Roo.bootstrap,
23350                 glyphicon : 'cog',
23351                 //html : 'submit'
23352                 menu : {
23353                     xtype: 'Menu',
23354                     xns: Roo.bootstrap,
23355                     items:  []
23356                 }
23357         };
23358         
23359         cog.menu.items.push({
23360             xtype :'MenuItem',
23361             xns: Roo.bootstrap,
23362             html : Clean styles,
23363             tagname : f,
23364             listeners : {
23365                 click : function()
23366                 {
23367                     editorcore.insertTag(this.tagname);
23368                     editor.focus();
23369                 }
23370             }
23371             
23372         });
23373        */
23374         
23375          
23376        this.xtype = 'NavSimplebar';
23377         
23378         for(var i=0;i< children.length;i++) {
23379             
23380             this.buttons.add(this.addxtypeChild(children[i]));
23381             
23382         }
23383         
23384         editor.on('editorevent', this.updateToolbar, this);
23385     },
23386     onBtnClick : function(id)
23387     {
23388        this.editorcore.relayCmd(id);
23389        this.editorcore.focus();
23390     },
23391     
23392     /**
23393      * Protected method that will not generally be called directly. It triggers
23394      * a toolbar update by reading the markup state of the current selection in the editor.
23395      */
23396     updateToolbar: function(){
23397
23398         if(!this.editorcore.activated){
23399             this.editor.onFirstFocus(); // is this neeed?
23400             return;
23401         }
23402
23403         var btns = this.buttons; 
23404         var doc = this.editorcore.doc;
23405         btns.get('bold').setActive(doc.queryCommandState('bold'));
23406         btns.get('italic').setActive(doc.queryCommandState('italic'));
23407         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23408         
23409         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23410         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23411         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23412         
23413         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23414         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23415          /*
23416         
23417         var ans = this.editorcore.getAllAncestors();
23418         if (this.formatCombo) {
23419             
23420             
23421             var store = this.formatCombo.store;
23422             this.formatCombo.setValue("");
23423             for (var i =0; i < ans.length;i++) {
23424                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23425                     // select it..
23426                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23427                     break;
23428                 }
23429             }
23430         }
23431         
23432         
23433         
23434         // hides menus... - so this cant be on a menu...
23435         Roo.bootstrap.MenuMgr.hideAll();
23436         */
23437         Roo.bootstrap.MenuMgr.hideAll();
23438         //this.editorsyncValue();
23439     },
23440     onFirstFocus: function() {
23441         this.buttons.each(function(item){
23442            item.enable();
23443         });
23444     },
23445     toggleSourceEdit : function(sourceEditMode){
23446         
23447           
23448         if(sourceEditMode){
23449             Roo.log("disabling buttons");
23450            this.buttons.each( function(item){
23451                 if(item.cmd != 'pencil'){
23452                     item.disable();
23453                 }
23454             });
23455           
23456         }else{
23457             Roo.log("enabling buttons");
23458             if(this.editorcore.initialized){
23459                 this.buttons.each( function(item){
23460                     item.enable();
23461                 });
23462             }
23463             
23464         }
23465         Roo.log("calling toggole on editor");
23466         // tell the editor that it's been pressed..
23467         this.editor.toggleSourceEdit(sourceEditMode);
23468        
23469     }
23470 });
23471
23472
23473
23474
23475
23476 /**
23477  * @class Roo.bootstrap.Table.AbstractSelectionModel
23478  * @extends Roo.util.Observable
23479  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23480  * implemented by descendant classes.  This class should not be directly instantiated.
23481  * @constructor
23482  */
23483 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23484     this.locked = false;
23485     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23486 };
23487
23488
23489 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23490     /** @ignore Called by the grid automatically. Do not call directly. */
23491     init : function(grid){
23492         this.grid = grid;
23493         this.initEvents();
23494     },
23495
23496     /**
23497      * Locks the selections.
23498      */
23499     lock : function(){
23500         this.locked = true;
23501     },
23502
23503     /**
23504      * Unlocks the selections.
23505      */
23506     unlock : function(){
23507         this.locked = false;
23508     },
23509
23510     /**
23511      * Returns true if the selections are locked.
23512      * @return {Boolean}
23513      */
23514     isLocked : function(){
23515         return this.locked;
23516     }
23517 });
23518 /**
23519  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23520  * @class Roo.bootstrap.Table.RowSelectionModel
23521  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23522  * It supports multiple selections and keyboard selection/navigation. 
23523  * @constructor
23524  * @param {Object} config
23525  */
23526
23527 Roo.bootstrap.Table.RowSelectionModel = function(config){
23528     Roo.apply(this, config);
23529     this.selections = new Roo.util.MixedCollection(false, function(o){
23530         return o.id;
23531     });
23532
23533     this.last = false;
23534     this.lastActive = false;
23535
23536     this.addEvents({
23537         /**
23538              * @event selectionchange
23539              * Fires when the selection changes
23540              * @param {SelectionModel} this
23541              */
23542             "selectionchange" : true,
23543         /**
23544              * @event afterselectionchange
23545              * Fires after the selection changes (eg. by key press or clicking)
23546              * @param {SelectionModel} this
23547              */
23548             "afterselectionchange" : true,
23549         /**
23550              * @event beforerowselect
23551              * Fires when a row is selected being selected, return false to cancel.
23552              * @param {SelectionModel} this
23553              * @param {Number} rowIndex The selected index
23554              * @param {Boolean} keepExisting False if other selections will be cleared
23555              */
23556             "beforerowselect" : true,
23557         /**
23558              * @event rowselect
23559              * Fires when a row is selected.
23560              * @param {SelectionModel} this
23561              * @param {Number} rowIndex The selected index
23562              * @param {Roo.data.Record} r The record
23563              */
23564             "rowselect" : true,
23565         /**
23566              * @event rowdeselect
23567              * Fires when a row is deselected.
23568              * @param {SelectionModel} this
23569              * @param {Number} rowIndex The selected index
23570              */
23571         "rowdeselect" : true
23572     });
23573     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23574     this.locked = false;
23575  };
23576
23577 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23578     /**
23579      * @cfg {Boolean} singleSelect
23580      * True to allow selection of only one row at a time (defaults to false)
23581      */
23582     singleSelect : false,
23583
23584     // private
23585     initEvents : function()
23586     {
23587
23588         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23589         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23590         //}else{ // allow click to work like normal
23591          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23592         //}
23593         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23594         this.grid.on("rowclick", this.handleMouseDown, this);
23595         
23596         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23597             "up" : function(e){
23598                 if(!e.shiftKey){
23599                     this.selectPrevious(e.shiftKey);
23600                 }else if(this.last !== false && this.lastActive !== false){
23601                     var last = this.last;
23602                     this.selectRange(this.last,  this.lastActive-1);
23603                     this.grid.getView().focusRow(this.lastActive);
23604                     if(last !== false){
23605                         this.last = last;
23606                     }
23607                 }else{
23608                     this.selectFirstRow();
23609                 }
23610                 this.fireEvent("afterselectionchange", this);
23611             },
23612             "down" : function(e){
23613                 if(!e.shiftKey){
23614                     this.selectNext(e.shiftKey);
23615                 }else if(this.last !== false && this.lastActive !== false){
23616                     var last = this.last;
23617                     this.selectRange(this.last,  this.lastActive+1);
23618                     this.grid.getView().focusRow(this.lastActive);
23619                     if(last !== false){
23620                         this.last = last;
23621                     }
23622                 }else{
23623                     this.selectFirstRow();
23624                 }
23625                 this.fireEvent("afterselectionchange", this);
23626             },
23627             scope: this
23628         });
23629         this.grid.store.on('load', function(){
23630             this.selections.clear();
23631         },this);
23632         /*
23633         var view = this.grid.view;
23634         view.on("refresh", this.onRefresh, this);
23635         view.on("rowupdated", this.onRowUpdated, this);
23636         view.on("rowremoved", this.onRemove, this);
23637         */
23638     },
23639
23640     // private
23641     onRefresh : function()
23642     {
23643         var ds = this.grid.store, i, v = this.grid.view;
23644         var s = this.selections;
23645         s.each(function(r){
23646             if((i = ds.indexOfId(r.id)) != -1){
23647                 v.onRowSelect(i);
23648             }else{
23649                 s.remove(r);
23650             }
23651         });
23652     },
23653
23654     // private
23655     onRemove : function(v, index, r){
23656         this.selections.remove(r);
23657     },
23658
23659     // private
23660     onRowUpdated : function(v, index, r){
23661         if(this.isSelected(r)){
23662             v.onRowSelect(index);
23663         }
23664     },
23665
23666     /**
23667      * Select records.
23668      * @param {Array} records The records to select
23669      * @param {Boolean} keepExisting (optional) True to keep existing selections
23670      */
23671     selectRecords : function(records, keepExisting)
23672     {
23673         if(!keepExisting){
23674             this.clearSelections();
23675         }
23676             var ds = this.grid.store;
23677         for(var i = 0, len = records.length; i < len; i++){
23678             this.selectRow(ds.indexOf(records[i]), true);
23679         }
23680     },
23681
23682     /**
23683      * Gets the number of selected rows.
23684      * @return {Number}
23685      */
23686     getCount : function(){
23687         return this.selections.length;
23688     },
23689
23690     /**
23691      * Selects the first row in the grid.
23692      */
23693     selectFirstRow : function(){
23694         this.selectRow(0);
23695     },
23696
23697     /**
23698      * Select the last row.
23699      * @param {Boolean} keepExisting (optional) True to keep existing selections
23700      */
23701     selectLastRow : function(keepExisting){
23702         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23703         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23704     },
23705
23706     /**
23707      * Selects the row immediately following the last selected row.
23708      * @param {Boolean} keepExisting (optional) True to keep existing selections
23709      */
23710     selectNext : function(keepExisting)
23711     {
23712             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23713             this.selectRow(this.last+1, keepExisting);
23714             this.grid.getView().focusRow(this.last);
23715         }
23716     },
23717
23718     /**
23719      * Selects the row that precedes the last selected row.
23720      * @param {Boolean} keepExisting (optional) True to keep existing selections
23721      */
23722     selectPrevious : function(keepExisting){
23723         if(this.last){
23724             this.selectRow(this.last-1, keepExisting);
23725             this.grid.getView().focusRow(this.last);
23726         }
23727     },
23728
23729     /**
23730      * Returns the selected records
23731      * @return {Array} Array of selected records
23732      */
23733     getSelections : function(){
23734         return [].concat(this.selections.items);
23735     },
23736
23737     /**
23738      * Returns the first selected record.
23739      * @return {Record}
23740      */
23741     getSelected : function(){
23742         return this.selections.itemAt(0);
23743     },
23744
23745
23746     /**
23747      * Clears all selections.
23748      */
23749     clearSelections : function(fast)
23750     {
23751         if(this.locked) {
23752             return;
23753         }
23754         if(fast !== true){
23755                 var ds = this.grid.store;
23756             var s = this.selections;
23757             s.each(function(r){
23758                 this.deselectRow(ds.indexOfId(r.id));
23759             }, this);
23760             s.clear();
23761         }else{
23762             this.selections.clear();
23763         }
23764         this.last = false;
23765     },
23766
23767
23768     /**
23769      * Selects all rows.
23770      */
23771     selectAll : function(){
23772         if(this.locked) {
23773             return;
23774         }
23775         this.selections.clear();
23776         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23777             this.selectRow(i, true);
23778         }
23779     },
23780
23781     /**
23782      * Returns True if there is a selection.
23783      * @return {Boolean}
23784      */
23785     hasSelection : function(){
23786         return this.selections.length > 0;
23787     },
23788
23789     /**
23790      * Returns True if the specified row is selected.
23791      * @param {Number/Record} record The record or index of the record to check
23792      * @return {Boolean}
23793      */
23794     isSelected : function(index){
23795             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23796         return (r && this.selections.key(r.id) ? true : false);
23797     },
23798
23799     /**
23800      * Returns True if the specified record id is selected.
23801      * @param {String} id The id of record to check
23802      * @return {Boolean}
23803      */
23804     isIdSelected : function(id){
23805         return (this.selections.key(id) ? true : false);
23806     },
23807
23808
23809     // private
23810     handleMouseDBClick : function(e, t){
23811         
23812     },
23813     // private
23814     handleMouseDown : function(e, t)
23815     {
23816             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23817         if(this.isLocked() || rowIndex < 0 ){
23818             return;
23819         };
23820         if(e.shiftKey && this.last !== false){
23821             var last = this.last;
23822             this.selectRange(last, rowIndex, e.ctrlKey);
23823             this.last = last; // reset the last
23824             t.focus();
23825     
23826         }else{
23827             var isSelected = this.isSelected(rowIndex);
23828             //Roo.log("select row:" + rowIndex);
23829             if(isSelected){
23830                 this.deselectRow(rowIndex);
23831             } else {
23832                         this.selectRow(rowIndex, true);
23833             }
23834     
23835             /*
23836                 if(e.button !== 0 && isSelected){
23837                 alert('rowIndex 2: ' + rowIndex);
23838                     view.focusRow(rowIndex);
23839                 }else if(e.ctrlKey && isSelected){
23840                     this.deselectRow(rowIndex);
23841                 }else if(!isSelected){
23842                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23843                     view.focusRow(rowIndex);
23844                 }
23845             */
23846         }
23847         this.fireEvent("afterselectionchange", this);
23848     },
23849     // private
23850     handleDragableRowClick :  function(grid, rowIndex, e) 
23851     {
23852         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23853             this.selectRow(rowIndex, false);
23854             grid.view.focusRow(rowIndex);
23855              this.fireEvent("afterselectionchange", this);
23856         }
23857     },
23858     
23859     /**
23860      * Selects multiple rows.
23861      * @param {Array} rows Array of the indexes of the row to select
23862      * @param {Boolean} keepExisting (optional) True to keep existing selections
23863      */
23864     selectRows : function(rows, keepExisting){
23865         if(!keepExisting){
23866             this.clearSelections();
23867         }
23868         for(var i = 0, len = rows.length; i < len; i++){
23869             this.selectRow(rows[i], true);
23870         }
23871     },
23872
23873     /**
23874      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23875      * @param {Number} startRow The index of the first row in the range
23876      * @param {Number} endRow The index of the last row in the range
23877      * @param {Boolean} keepExisting (optional) True to retain existing selections
23878      */
23879     selectRange : function(startRow, endRow, keepExisting){
23880         if(this.locked) {
23881             return;
23882         }
23883         if(!keepExisting){
23884             this.clearSelections();
23885         }
23886         if(startRow <= endRow){
23887             for(var i = startRow; i <= endRow; i++){
23888                 this.selectRow(i, true);
23889             }
23890         }else{
23891             for(var i = startRow; i >= endRow; i--){
23892                 this.selectRow(i, true);
23893             }
23894         }
23895     },
23896
23897     /**
23898      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23899      * @param {Number} startRow The index of the first row in the range
23900      * @param {Number} endRow The index of the last row in the range
23901      */
23902     deselectRange : function(startRow, endRow, preventViewNotify){
23903         if(this.locked) {
23904             return;
23905         }
23906         for(var i = startRow; i <= endRow; i++){
23907             this.deselectRow(i, preventViewNotify);
23908         }
23909     },
23910
23911     /**
23912      * Selects a row.
23913      * @param {Number} row The index of the row to select
23914      * @param {Boolean} keepExisting (optional) True to keep existing selections
23915      */
23916     selectRow : function(index, keepExisting, preventViewNotify)
23917     {
23918             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23919             return;
23920         }
23921         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23922             if(!keepExisting || this.singleSelect){
23923                 this.clearSelections();
23924             }
23925             
23926             var r = this.grid.store.getAt(index);
23927             //console.log('selectRow - record id :' + r.id);
23928             
23929             this.selections.add(r);
23930             this.last = this.lastActive = index;
23931             if(!preventViewNotify){
23932                 var proxy = new Roo.Element(
23933                                 this.grid.getRowDom(index)
23934                 );
23935                 proxy.addClass('bg-info info');
23936             }
23937             this.fireEvent("rowselect", this, index, r);
23938             this.fireEvent("selectionchange", this);
23939         }
23940     },
23941
23942     /**
23943      * Deselects a row.
23944      * @param {Number} row The index of the row to deselect
23945      */
23946     deselectRow : function(index, preventViewNotify)
23947     {
23948         if(this.locked) {
23949             return;
23950         }
23951         if(this.last == index){
23952             this.last = false;
23953         }
23954         if(this.lastActive == index){
23955             this.lastActive = false;
23956         }
23957         
23958         var r = this.grid.store.getAt(index);
23959         if (!r) {
23960             return;
23961         }
23962         
23963         this.selections.remove(r);
23964         //.console.log('deselectRow - record id :' + r.id);
23965         if(!preventViewNotify){
23966         
23967             var proxy = new Roo.Element(
23968                 this.grid.getRowDom(index)
23969             );
23970             proxy.removeClass('bg-info info');
23971         }
23972         this.fireEvent("rowdeselect", this, index);
23973         this.fireEvent("selectionchange", this);
23974     },
23975
23976     // private
23977     restoreLast : function(){
23978         if(this._last){
23979             this.last = this._last;
23980         }
23981     },
23982
23983     // private
23984     acceptsNav : function(row, col, cm){
23985         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23986     },
23987
23988     // private
23989     onEditorKey : function(field, e){
23990         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23991         if(k == e.TAB){
23992             e.stopEvent();
23993             ed.completeEdit();
23994             if(e.shiftKey){
23995                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23996             }else{
23997                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23998             }
23999         }else if(k == e.ENTER && !e.ctrlKey){
24000             e.stopEvent();
24001             ed.completeEdit();
24002             if(e.shiftKey){
24003                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24004             }else{
24005                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24006             }
24007         }else if(k == e.ESC){
24008             ed.cancelEdit();
24009         }
24010         if(newCell){
24011             g.startEditing(newCell[0], newCell[1]);
24012         }
24013     }
24014 });
24015 /*
24016  * Based on:
24017  * Ext JS Library 1.1.1
24018  * Copyright(c) 2006-2007, Ext JS, LLC.
24019  *
24020  * Originally Released Under LGPL - original licence link has changed is not relivant.
24021  *
24022  * Fork - LGPL
24023  * <script type="text/javascript">
24024  */
24025  
24026 /**
24027  * @class Roo.bootstrap.PagingToolbar
24028  * @extends Roo.bootstrap.NavSimplebar
24029  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24030  * @constructor
24031  * Create a new PagingToolbar
24032  * @param {Object} config The config object
24033  * @param {Roo.data.Store} store
24034  */
24035 Roo.bootstrap.PagingToolbar = function(config)
24036 {
24037     // old args format still supported... - xtype is prefered..
24038         // created from xtype...
24039     
24040     this.ds = config.dataSource;
24041     
24042     if (config.store && !this.ds) {
24043         this.store= Roo.factory(config.store, Roo.data);
24044         this.ds = this.store;
24045         this.ds.xmodule = this.xmodule || false;
24046     }
24047     
24048     this.toolbarItems = [];
24049     if (config.items) {
24050         this.toolbarItems = config.items;
24051     }
24052     
24053     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24054     
24055     this.cursor = 0;
24056     
24057     if (this.ds) { 
24058         this.bind(this.ds);
24059     }
24060     
24061     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24062     
24063 };
24064
24065 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24066     /**
24067      * @cfg {Roo.data.Store} dataSource
24068      * The underlying data store providing the paged data
24069      */
24070     /**
24071      * @cfg {String/HTMLElement/Element} container
24072      * container The id or element that will contain the toolbar
24073      */
24074     /**
24075      * @cfg {Boolean} displayInfo
24076      * True to display the displayMsg (defaults to false)
24077      */
24078     /**
24079      * @cfg {Number} pageSize
24080      * The number of records to display per page (defaults to 20)
24081      */
24082     pageSize: 20,
24083     /**
24084      * @cfg {String} displayMsg
24085      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24086      */
24087     displayMsg : 'Displaying {0} - {1} of {2}',
24088     /**
24089      * @cfg {String} emptyMsg
24090      * The message to display when no records are found (defaults to "No data to display")
24091      */
24092     emptyMsg : 'No data to display',
24093     /**
24094      * Customizable piece of the default paging text (defaults to "Page")
24095      * @type String
24096      */
24097     beforePageText : "Page",
24098     /**
24099      * Customizable piece of the default paging text (defaults to "of %0")
24100      * @type String
24101      */
24102     afterPageText : "of {0}",
24103     /**
24104      * Customizable piece of the default paging text (defaults to "First Page")
24105      * @type String
24106      */
24107     firstText : "First Page",
24108     /**
24109      * Customizable piece of the default paging text (defaults to "Previous Page")
24110      * @type String
24111      */
24112     prevText : "Previous Page",
24113     /**
24114      * Customizable piece of the default paging text (defaults to "Next Page")
24115      * @type String
24116      */
24117     nextText : "Next Page",
24118     /**
24119      * Customizable piece of the default paging text (defaults to "Last Page")
24120      * @type String
24121      */
24122     lastText : "Last Page",
24123     /**
24124      * Customizable piece of the default paging text (defaults to "Refresh")
24125      * @type String
24126      */
24127     refreshText : "Refresh",
24128
24129     buttons : false,
24130     // private
24131     onRender : function(ct, position) 
24132     {
24133         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24134         this.navgroup.parentId = this.id;
24135         this.navgroup.onRender(this.el, null);
24136         // add the buttons to the navgroup
24137         
24138         if(this.displayInfo){
24139             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24140             this.displayEl = this.el.select('.x-paging-info', true).first();
24141 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24142 //            this.displayEl = navel.el.select('span',true).first();
24143         }
24144         
24145         var _this = this;
24146         
24147         if(this.buttons){
24148             Roo.each(_this.buttons, function(e){ // this might need to use render????
24149                Roo.factory(e).onRender(_this.el, null);
24150             });
24151         }
24152             
24153         Roo.each(_this.toolbarItems, function(e) {
24154             _this.navgroup.addItem(e);
24155         });
24156         
24157         
24158         this.first = this.navgroup.addItem({
24159             tooltip: this.firstText,
24160             cls: "prev",
24161             icon : 'fa fa-backward',
24162             disabled: true,
24163             preventDefault: true,
24164             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24165         });
24166         
24167         this.prev =  this.navgroup.addItem({
24168             tooltip: this.prevText,
24169             cls: "prev",
24170             icon : 'fa fa-step-backward',
24171             disabled: true,
24172             preventDefault: true,
24173             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24174         });
24175     //this.addSeparator();
24176         
24177         
24178         var field = this.navgroup.addItem( {
24179             tagtype : 'span',
24180             cls : 'x-paging-position',
24181             
24182             html : this.beforePageText  +
24183                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24184                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24185          } ); //?? escaped?
24186         
24187         this.field = field.el.select('input', true).first();
24188         this.field.on("keydown", this.onPagingKeydown, this);
24189         this.field.on("focus", function(){this.dom.select();});
24190     
24191     
24192         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24193         //this.field.setHeight(18);
24194         //this.addSeparator();
24195         this.next = this.navgroup.addItem({
24196             tooltip: this.nextText,
24197             cls: "next",
24198             html : ' <i class="fa fa-step-forward">',
24199             disabled: true,
24200             preventDefault: true,
24201             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24202         });
24203         this.last = this.navgroup.addItem({
24204             tooltip: this.lastText,
24205             icon : 'fa fa-forward',
24206             cls: "next",
24207             disabled: true,
24208             preventDefault: true,
24209             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24210         });
24211     //this.addSeparator();
24212         this.loading = this.navgroup.addItem({
24213             tooltip: this.refreshText,
24214             icon: 'fa fa-refresh',
24215             preventDefault: true,
24216             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24217         });
24218         
24219     },
24220
24221     // private
24222     updateInfo : function(){
24223         if(this.displayEl){
24224             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24225             var msg = count == 0 ?
24226                 this.emptyMsg :
24227                 String.format(
24228                     this.displayMsg,
24229                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24230                 );
24231             this.displayEl.update(msg);
24232         }
24233     },
24234
24235     // private
24236     onLoad : function(ds, r, o)
24237     {
24238         this.cursor = o.params ? o.params.start : 0;
24239         var d = this.getPageData(),
24240             ap = d.activePage,
24241             ps = d.pages;
24242         
24243         
24244         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24245         this.field.dom.value = ap;
24246         this.first.setDisabled(ap == 1);
24247         this.prev.setDisabled(ap == 1);
24248         this.next.setDisabled(ap == ps);
24249         this.last.setDisabled(ap == ps);
24250         this.loading.enable();
24251         this.updateInfo();
24252     },
24253
24254     // private
24255     getPageData : function(){
24256         var total = this.ds.getTotalCount();
24257         return {
24258             total : total,
24259             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24260             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24261         };
24262     },
24263
24264     // private
24265     onLoadError : function(){
24266         this.loading.enable();
24267     },
24268
24269     // private
24270     onPagingKeydown : function(e){
24271         var k = e.getKey();
24272         var d = this.getPageData();
24273         if(k == e.RETURN){
24274             var v = this.field.dom.value, pageNum;
24275             if(!v || isNaN(pageNum = parseInt(v, 10))){
24276                 this.field.dom.value = d.activePage;
24277                 return;
24278             }
24279             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24280             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24281             e.stopEvent();
24282         }
24283         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))
24284         {
24285           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24286           this.field.dom.value = pageNum;
24287           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24288           e.stopEvent();
24289         }
24290         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24291         {
24292           var v = this.field.dom.value, pageNum; 
24293           var increment = (e.shiftKey) ? 10 : 1;
24294           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24295                 increment *= -1;
24296           }
24297           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24298             this.field.dom.value = d.activePage;
24299             return;
24300           }
24301           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24302           {
24303             this.field.dom.value = parseInt(v, 10) + increment;
24304             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24305             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24306           }
24307           e.stopEvent();
24308         }
24309     },
24310
24311     // private
24312     beforeLoad : function(){
24313         if(this.loading){
24314             this.loading.disable();
24315         }
24316     },
24317
24318     // private
24319     onClick : function(which){
24320         
24321         var ds = this.ds;
24322         if (!ds) {
24323             return;
24324         }
24325         
24326         switch(which){
24327             case "first":
24328                 ds.load({params:{start: 0, limit: this.pageSize}});
24329             break;
24330             case "prev":
24331                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24332             break;
24333             case "next":
24334                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24335             break;
24336             case "last":
24337                 var total = ds.getTotalCount();
24338                 var extra = total % this.pageSize;
24339                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24340                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24341             break;
24342             case "refresh":
24343                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24344             break;
24345         }
24346     },
24347
24348     /**
24349      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24350      * @param {Roo.data.Store} store The data store to unbind
24351      */
24352     unbind : function(ds){
24353         ds.un("beforeload", this.beforeLoad, this);
24354         ds.un("load", this.onLoad, this);
24355         ds.un("loadexception", this.onLoadError, this);
24356         ds.un("remove", this.updateInfo, this);
24357         ds.un("add", this.updateInfo, this);
24358         this.ds = undefined;
24359     },
24360
24361     /**
24362      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24363      * @param {Roo.data.Store} store The data store to bind
24364      */
24365     bind : function(ds){
24366         ds.on("beforeload", this.beforeLoad, this);
24367         ds.on("load", this.onLoad, this);
24368         ds.on("loadexception", this.onLoadError, this);
24369         ds.on("remove", this.updateInfo, this);
24370         ds.on("add", this.updateInfo, this);
24371         this.ds = ds;
24372     }
24373 });/*
24374  * - LGPL
24375  *
24376  * element
24377  * 
24378  */
24379
24380 /**
24381  * @class Roo.bootstrap.MessageBar
24382  * @extends Roo.bootstrap.Component
24383  * Bootstrap MessageBar class
24384  * @cfg {String} html contents of the MessageBar
24385  * @cfg {String} weight (info | success | warning | danger) default info
24386  * @cfg {String} beforeClass insert the bar before the given class
24387  * @cfg {Boolean} closable (true | false) default false
24388  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24389  * 
24390  * @constructor
24391  * Create a new Element
24392  * @param {Object} config The config object
24393  */
24394
24395 Roo.bootstrap.MessageBar = function(config){
24396     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24397 };
24398
24399 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24400     
24401     html: '',
24402     weight: 'info',
24403     closable: false,
24404     fixed: false,
24405     beforeClass: 'bootstrap-sticky-wrap',
24406     
24407     getAutoCreate : function(){
24408         
24409         var cfg = {
24410             tag: 'div',
24411             cls: 'alert alert-dismissable alert-' + this.weight,
24412             cn: [
24413                 {
24414                     tag: 'span',
24415                     cls: 'message',
24416                     html: this.html || ''
24417                 }
24418             ]
24419         };
24420         
24421         if(this.fixed){
24422             cfg.cls += ' alert-messages-fixed';
24423         }
24424         
24425         if(this.closable){
24426             cfg.cn.push({
24427                 tag: 'button',
24428                 cls: 'close',
24429                 html: 'x'
24430             });
24431         }
24432         
24433         return cfg;
24434     },
24435     
24436     onRender : function(ct, position)
24437     {
24438         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24439         
24440         if(!this.el){
24441             var cfg = Roo.apply({},  this.getAutoCreate());
24442             cfg.id = Roo.id();
24443             
24444             if (this.cls) {
24445                 cfg.cls += ' ' + this.cls;
24446             }
24447             if (this.style) {
24448                 cfg.style = this.style;
24449             }
24450             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24451             
24452             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24453         }
24454         
24455         this.el.select('>button.close').on('click', this.hide, this);
24456         
24457     },
24458     
24459     show : function()
24460     {
24461         if (!this.rendered) {
24462             this.render();
24463         }
24464         
24465         this.el.show();
24466         
24467         this.fireEvent('show', this);
24468         
24469     },
24470     
24471     hide : function()
24472     {
24473         if (!this.rendered) {
24474             this.render();
24475         }
24476         
24477         this.el.hide();
24478         
24479         this.fireEvent('hide', this);
24480     },
24481     
24482     update : function()
24483     {
24484 //        var e = this.el.dom.firstChild;
24485 //        
24486 //        if(this.closable){
24487 //            e = e.nextSibling;
24488 //        }
24489 //        
24490 //        e.data = this.html || '';
24491
24492         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24493     }
24494    
24495 });
24496
24497  
24498
24499      /*
24500  * - LGPL
24501  *
24502  * Graph
24503  * 
24504  */
24505
24506
24507 /**
24508  * @class Roo.bootstrap.Graph
24509  * @extends Roo.bootstrap.Component
24510  * Bootstrap Graph class
24511 > Prameters
24512  -sm {number} sm 4
24513  -md {number} md 5
24514  @cfg {String} graphtype  bar | vbar | pie
24515  @cfg {number} g_x coodinator | centre x (pie)
24516  @cfg {number} g_y coodinator | centre y (pie)
24517  @cfg {number} g_r radius (pie)
24518  @cfg {number} g_height height of the chart (respected by all elements in the set)
24519  @cfg {number} g_width width of the chart (respected by all elements in the set)
24520  @cfg {Object} title The title of the chart
24521     
24522  -{Array}  values
24523  -opts (object) options for the chart 
24524      o {
24525      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24526      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24527      o vgutter (number)
24528      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.
24529      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24530      o to
24531      o stretch (boolean)
24532      o }
24533  -opts (object) options for the pie
24534      o{
24535      o cut
24536      o startAngle (number)
24537      o endAngle (number)
24538      } 
24539  *
24540  * @constructor
24541  * Create a new Input
24542  * @param {Object} config The config object
24543  */
24544
24545 Roo.bootstrap.Graph = function(config){
24546     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24547     
24548     this.addEvents({
24549         // img events
24550         /**
24551          * @event click
24552          * The img click event for the img.
24553          * @param {Roo.EventObject} e
24554          */
24555         "click" : true
24556     });
24557 };
24558
24559 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24560     
24561     sm: 4,
24562     md: 5,
24563     graphtype: 'bar',
24564     g_height: 250,
24565     g_width: 400,
24566     g_x: 50,
24567     g_y: 50,
24568     g_r: 30,
24569     opts:{
24570         //g_colors: this.colors,
24571         g_type: 'soft',
24572         g_gutter: '20%'
24573
24574     },
24575     title : false,
24576
24577     getAutoCreate : function(){
24578         
24579         var cfg = {
24580             tag: 'div',
24581             html : null
24582         };
24583         
24584         
24585         return  cfg;
24586     },
24587
24588     onRender : function(ct,position){
24589         
24590         
24591         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24592         
24593         if (typeof(Raphael) == 'undefined') {
24594             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24595             return;
24596         }
24597         
24598         this.raphael = Raphael(this.el.dom);
24599         
24600                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24601                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24602                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24603                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24604                 /*
24605                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24606                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24607                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24608                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24609                 
24610                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24611                 r.barchart(330, 10, 300, 220, data1);
24612                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24613                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24614                 */
24615                 
24616                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24617                 // r.barchart(30, 30, 560, 250,  xdata, {
24618                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24619                 //     axis : "0 0 1 1",
24620                 //     axisxlabels :  xdata
24621                 //     //yvalues : cols,
24622                    
24623                 // });
24624 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24625 //        
24626 //        this.load(null,xdata,{
24627 //                axis : "0 0 1 1",
24628 //                axisxlabels :  xdata
24629 //                });
24630
24631     },
24632
24633     load : function(graphtype,xdata,opts)
24634     {
24635         this.raphael.clear();
24636         if(!graphtype) {
24637             graphtype = this.graphtype;
24638         }
24639         if(!opts){
24640             opts = this.opts;
24641         }
24642         var r = this.raphael,
24643             fin = function () {
24644                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24645             },
24646             fout = function () {
24647                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24648             },
24649             pfin = function() {
24650                 this.sector.stop();
24651                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24652
24653                 if (this.label) {
24654                     this.label[0].stop();
24655                     this.label[0].attr({ r: 7.5 });
24656                     this.label[1].attr({ "font-weight": 800 });
24657                 }
24658             },
24659             pfout = function() {
24660                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24661
24662                 if (this.label) {
24663                     this.label[0].animate({ r: 5 }, 500, "bounce");
24664                     this.label[1].attr({ "font-weight": 400 });
24665                 }
24666             };
24667
24668         switch(graphtype){
24669             case 'bar':
24670                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24671                 break;
24672             case 'hbar':
24673                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24674                 break;
24675             case 'pie':
24676 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24677 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24678 //            
24679                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24680                 
24681                 break;
24682
24683         }
24684         
24685         if(this.title){
24686             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24687         }
24688         
24689     },
24690     
24691     setTitle: function(o)
24692     {
24693         this.title = o;
24694     },
24695     
24696     initEvents: function() {
24697         
24698         if(!this.href){
24699             this.el.on('click', this.onClick, this);
24700         }
24701     },
24702     
24703     onClick : function(e)
24704     {
24705         Roo.log('img onclick');
24706         this.fireEvent('click', this, e);
24707     }
24708    
24709 });
24710
24711  
24712 /*
24713  * - LGPL
24714  *
24715  * numberBox
24716  * 
24717  */
24718 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24719
24720 /**
24721  * @class Roo.bootstrap.dash.NumberBox
24722  * @extends Roo.bootstrap.Component
24723  * Bootstrap NumberBox class
24724  * @cfg {String} headline Box headline
24725  * @cfg {String} content Box content
24726  * @cfg {String} icon Box icon
24727  * @cfg {String} footer Footer text
24728  * @cfg {String} fhref Footer href
24729  * 
24730  * @constructor
24731  * Create a new NumberBox
24732  * @param {Object} config The config object
24733  */
24734
24735
24736 Roo.bootstrap.dash.NumberBox = function(config){
24737     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24738     
24739 };
24740
24741 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24742     
24743     headline : '',
24744     content : '',
24745     icon : '',
24746     footer : '',
24747     fhref : '',
24748     ficon : '',
24749     
24750     getAutoCreate : function(){
24751         
24752         var cfg = {
24753             tag : 'div',
24754             cls : 'small-box ',
24755             cn : [
24756                 {
24757                     tag : 'div',
24758                     cls : 'inner',
24759                     cn :[
24760                         {
24761                             tag : 'h3',
24762                             cls : 'roo-headline',
24763                             html : this.headline
24764                         },
24765                         {
24766                             tag : 'p',
24767                             cls : 'roo-content',
24768                             html : this.content
24769                         }
24770                     ]
24771                 }
24772             ]
24773         };
24774         
24775         if(this.icon){
24776             cfg.cn.push({
24777                 tag : 'div',
24778                 cls : 'icon',
24779                 cn :[
24780                     {
24781                         tag : 'i',
24782                         cls : 'ion ' + this.icon
24783                     }
24784                 ]
24785             });
24786         }
24787         
24788         if(this.footer){
24789             var footer = {
24790                 tag : 'a',
24791                 cls : 'small-box-footer',
24792                 href : this.fhref || '#',
24793                 html : this.footer
24794             };
24795             
24796             cfg.cn.push(footer);
24797             
24798         }
24799         
24800         return  cfg;
24801     },
24802
24803     onRender : function(ct,position){
24804         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24805
24806
24807        
24808                 
24809     },
24810
24811     setHeadline: function (value)
24812     {
24813         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24814     },
24815     
24816     setFooter: function (value, href)
24817     {
24818         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24819         
24820         if(href){
24821             this.el.select('a.small-box-footer',true).first().attr('href', href);
24822         }
24823         
24824     },
24825
24826     setContent: function (value)
24827     {
24828         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24829     },
24830
24831     initEvents: function() 
24832     {   
24833         
24834     }
24835     
24836 });
24837
24838  
24839 /*
24840  * - LGPL
24841  *
24842  * TabBox
24843  * 
24844  */
24845 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24846
24847 /**
24848  * @class Roo.bootstrap.dash.TabBox
24849  * @extends Roo.bootstrap.Component
24850  * Bootstrap TabBox class
24851  * @cfg {String} title Title of the TabBox
24852  * @cfg {String} icon Icon of the TabBox
24853  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24854  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24855  * 
24856  * @constructor
24857  * Create a new TabBox
24858  * @param {Object} config The config object
24859  */
24860
24861
24862 Roo.bootstrap.dash.TabBox = function(config){
24863     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24864     this.addEvents({
24865         // raw events
24866         /**
24867          * @event addpane
24868          * When a pane is added
24869          * @param {Roo.bootstrap.dash.TabPane} pane
24870          */
24871         "addpane" : true,
24872         /**
24873          * @event activatepane
24874          * When a pane is activated
24875          * @param {Roo.bootstrap.dash.TabPane} pane
24876          */
24877         "activatepane" : true
24878         
24879          
24880     });
24881     
24882     this.panes = [];
24883 };
24884
24885 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24886
24887     title : '',
24888     icon : false,
24889     showtabs : true,
24890     tabScrollable : false,
24891     
24892     getChildContainer : function()
24893     {
24894         return this.el.select('.tab-content', true).first();
24895     },
24896     
24897     getAutoCreate : function(){
24898         
24899         var header = {
24900             tag: 'li',
24901             cls: 'pull-left header',
24902             html: this.title,
24903             cn : []
24904         };
24905         
24906         if(this.icon){
24907             header.cn.push({
24908                 tag: 'i',
24909                 cls: 'fa ' + this.icon
24910             });
24911         }
24912         
24913         var h = {
24914             tag: 'ul',
24915             cls: 'nav nav-tabs pull-right',
24916             cn: [
24917                 header
24918             ]
24919         };
24920         
24921         if(this.tabScrollable){
24922             h = {
24923                 tag: 'div',
24924                 cls: 'tab-header',
24925                 cn: [
24926                     {
24927                         tag: 'ul',
24928                         cls: 'nav nav-tabs pull-right',
24929                         cn: [
24930                             header
24931                         ]
24932                     }
24933                 ]
24934             };
24935         }
24936         
24937         var cfg = {
24938             tag: 'div',
24939             cls: 'nav-tabs-custom',
24940             cn: [
24941                 h,
24942                 {
24943                     tag: 'div',
24944                     cls: 'tab-content no-padding',
24945                     cn: []
24946                 }
24947             ]
24948         };
24949
24950         return  cfg;
24951     },
24952     initEvents : function()
24953     {
24954         //Roo.log('add add pane handler');
24955         this.on('addpane', this.onAddPane, this);
24956     },
24957      /**
24958      * Updates the box title
24959      * @param {String} html to set the title to.
24960      */
24961     setTitle : function(value)
24962     {
24963         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24964     },
24965     onAddPane : function(pane)
24966     {
24967         this.panes.push(pane);
24968         //Roo.log('addpane');
24969         //Roo.log(pane);
24970         // tabs are rendere left to right..
24971         if(!this.showtabs){
24972             return;
24973         }
24974         
24975         var ctr = this.el.select('.nav-tabs', true).first();
24976          
24977          
24978         var existing = ctr.select('.nav-tab',true);
24979         var qty = existing.getCount();;
24980         
24981         
24982         var tab = ctr.createChild({
24983             tag : 'li',
24984             cls : 'nav-tab' + (qty ? '' : ' active'),
24985             cn : [
24986                 {
24987                     tag : 'a',
24988                     href:'#',
24989                     html : pane.title
24990                 }
24991             ]
24992         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24993         pane.tab = tab;
24994         
24995         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24996         if (!qty) {
24997             pane.el.addClass('active');
24998         }
24999         
25000                 
25001     },
25002     onTabClick : function(ev,un,ob,pane)
25003     {
25004         //Roo.log('tab - prev default');
25005         ev.preventDefault();
25006         
25007         
25008         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25009         pane.tab.addClass('active');
25010         //Roo.log(pane.title);
25011         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25012         // technically we should have a deactivate event.. but maybe add later.
25013         // and it should not de-activate the selected tab...
25014         this.fireEvent('activatepane', pane);
25015         pane.el.addClass('active');
25016         pane.fireEvent('activate');
25017         
25018         
25019     },
25020     
25021     getActivePane : function()
25022     {
25023         var r = false;
25024         Roo.each(this.panes, function(p) {
25025             if(p.el.hasClass('active')){
25026                 r = p;
25027                 return false;
25028             }
25029             
25030             return;
25031         });
25032         
25033         return r;
25034     }
25035     
25036     
25037 });
25038
25039  
25040 /*
25041  * - LGPL
25042  *
25043  * Tab pane
25044  * 
25045  */
25046 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25047 /**
25048  * @class Roo.bootstrap.TabPane
25049  * @extends Roo.bootstrap.Component
25050  * Bootstrap TabPane class
25051  * @cfg {Boolean} active (false | true) Default false
25052  * @cfg {String} title title of panel
25053
25054  * 
25055  * @constructor
25056  * Create a new TabPane
25057  * @param {Object} config The config object
25058  */
25059
25060 Roo.bootstrap.dash.TabPane = function(config){
25061     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25062     
25063     this.addEvents({
25064         // raw events
25065         /**
25066          * @event activate
25067          * When a pane is activated
25068          * @param {Roo.bootstrap.dash.TabPane} pane
25069          */
25070         "activate" : true
25071          
25072     });
25073 };
25074
25075 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25076     
25077     active : false,
25078     title : '',
25079     
25080     // the tabBox that this is attached to.
25081     tab : false,
25082      
25083     getAutoCreate : function() 
25084     {
25085         var cfg = {
25086             tag: 'div',
25087             cls: 'tab-pane'
25088         };
25089         
25090         if(this.active){
25091             cfg.cls += ' active';
25092         }
25093         
25094         return cfg;
25095     },
25096     initEvents  : function()
25097     {
25098         //Roo.log('trigger add pane handler');
25099         this.parent().fireEvent('addpane', this)
25100     },
25101     
25102      /**
25103      * Updates the tab title 
25104      * @param {String} html to set the title to.
25105      */
25106     setTitle: function(str)
25107     {
25108         if (!this.tab) {
25109             return;
25110         }
25111         this.title = str;
25112         this.tab.select('a', true).first().dom.innerHTML = str;
25113         
25114     }
25115     
25116     
25117     
25118 });
25119
25120  
25121
25122
25123  /*
25124  * - LGPL
25125  *
25126  * menu
25127  * 
25128  */
25129 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25130
25131 /**
25132  * @class Roo.bootstrap.menu.Menu
25133  * @extends Roo.bootstrap.Component
25134  * Bootstrap Menu class - container for Menu
25135  * @cfg {String} html Text of the menu
25136  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25137  * @cfg {String} icon Font awesome icon
25138  * @cfg {String} pos Menu align to (top | bottom) default bottom
25139  * 
25140  * 
25141  * @constructor
25142  * Create a new Menu
25143  * @param {Object} config The config object
25144  */
25145
25146
25147 Roo.bootstrap.menu.Menu = function(config){
25148     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25149     
25150     this.addEvents({
25151         /**
25152          * @event beforeshow
25153          * Fires before this menu is displayed
25154          * @param {Roo.bootstrap.menu.Menu} this
25155          */
25156         beforeshow : true,
25157         /**
25158          * @event beforehide
25159          * Fires before this menu is hidden
25160          * @param {Roo.bootstrap.menu.Menu} this
25161          */
25162         beforehide : true,
25163         /**
25164          * @event show
25165          * Fires after this menu is displayed
25166          * @param {Roo.bootstrap.menu.Menu} this
25167          */
25168         show : true,
25169         /**
25170          * @event hide
25171          * Fires after this menu is hidden
25172          * @param {Roo.bootstrap.menu.Menu} this
25173          */
25174         hide : true,
25175         /**
25176          * @event click
25177          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25178          * @param {Roo.bootstrap.menu.Menu} this
25179          * @param {Roo.EventObject} e
25180          */
25181         click : true
25182     });
25183     
25184 };
25185
25186 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25187     
25188     submenu : false,
25189     html : '',
25190     weight : 'default',
25191     icon : false,
25192     pos : 'bottom',
25193     
25194     
25195     getChildContainer : function() {
25196         if(this.isSubMenu){
25197             return this.el;
25198         }
25199         
25200         return this.el.select('ul.dropdown-menu', true).first();  
25201     },
25202     
25203     getAutoCreate : function()
25204     {
25205         var text = [
25206             {
25207                 tag : 'span',
25208                 cls : 'roo-menu-text',
25209                 html : this.html
25210             }
25211         ];
25212         
25213         if(this.icon){
25214             text.unshift({
25215                 tag : 'i',
25216                 cls : 'fa ' + this.icon
25217             })
25218         }
25219         
25220         
25221         var cfg = {
25222             tag : 'div',
25223             cls : 'btn-group',
25224             cn : [
25225                 {
25226                     tag : 'button',
25227                     cls : 'dropdown-button btn btn-' + this.weight,
25228                     cn : text
25229                 },
25230                 {
25231                     tag : 'button',
25232                     cls : 'dropdown-toggle btn btn-' + this.weight,
25233                     cn : [
25234                         {
25235                             tag : 'span',
25236                             cls : 'caret'
25237                         }
25238                     ]
25239                 },
25240                 {
25241                     tag : 'ul',
25242                     cls : 'dropdown-menu'
25243                 }
25244             ]
25245             
25246         };
25247         
25248         if(this.pos == 'top'){
25249             cfg.cls += ' dropup';
25250         }
25251         
25252         if(this.isSubMenu){
25253             cfg = {
25254                 tag : 'ul',
25255                 cls : 'dropdown-menu'
25256             }
25257         }
25258         
25259         return cfg;
25260     },
25261     
25262     onRender : function(ct, position)
25263     {
25264         this.isSubMenu = ct.hasClass('dropdown-submenu');
25265         
25266         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25267     },
25268     
25269     initEvents : function() 
25270     {
25271         if(this.isSubMenu){
25272             return;
25273         }
25274         
25275         this.hidden = true;
25276         
25277         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25278         this.triggerEl.on('click', this.onTriggerPress, this);
25279         
25280         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25281         this.buttonEl.on('click', this.onClick, this);
25282         
25283     },
25284     
25285     list : function()
25286     {
25287         if(this.isSubMenu){
25288             return this.el;
25289         }
25290         
25291         return this.el.select('ul.dropdown-menu', true).first();
25292     },
25293     
25294     onClick : function(e)
25295     {
25296         this.fireEvent("click", this, e);
25297     },
25298     
25299     onTriggerPress  : function(e)
25300     {   
25301         if (this.isVisible()) {
25302             this.hide();
25303         } else {
25304             this.show();
25305         }
25306     },
25307     
25308     isVisible : function(){
25309         return !this.hidden;
25310     },
25311     
25312     show : function()
25313     {
25314         this.fireEvent("beforeshow", this);
25315         
25316         this.hidden = false;
25317         this.el.addClass('open');
25318         
25319         Roo.get(document).on("mouseup", this.onMouseUp, this);
25320         
25321         this.fireEvent("show", this);
25322         
25323         
25324     },
25325     
25326     hide : function()
25327     {
25328         this.fireEvent("beforehide", this);
25329         
25330         this.hidden = true;
25331         this.el.removeClass('open');
25332         
25333         Roo.get(document).un("mouseup", this.onMouseUp);
25334         
25335         this.fireEvent("hide", this);
25336     },
25337     
25338     onMouseUp : function()
25339     {
25340         this.hide();
25341     }
25342     
25343 });
25344
25345  
25346  /*
25347  * - LGPL
25348  *
25349  * menu item
25350  * 
25351  */
25352 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25353
25354 /**
25355  * @class Roo.bootstrap.menu.Item
25356  * @extends Roo.bootstrap.Component
25357  * Bootstrap MenuItem class
25358  * @cfg {Boolean} submenu (true | false) default false
25359  * @cfg {String} html text of the item
25360  * @cfg {String} href the link
25361  * @cfg {Boolean} disable (true | false) default false
25362  * @cfg {Boolean} preventDefault (true | false) default true
25363  * @cfg {String} icon Font awesome icon
25364  * @cfg {String} pos Submenu align to (left | right) default right 
25365  * 
25366  * 
25367  * @constructor
25368  * Create a new Item
25369  * @param {Object} config The config object
25370  */
25371
25372
25373 Roo.bootstrap.menu.Item = function(config){
25374     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25375     this.addEvents({
25376         /**
25377          * @event mouseover
25378          * Fires when the mouse is hovering over this menu
25379          * @param {Roo.bootstrap.menu.Item} this
25380          * @param {Roo.EventObject} e
25381          */
25382         mouseover : true,
25383         /**
25384          * @event mouseout
25385          * Fires when the mouse exits this menu
25386          * @param {Roo.bootstrap.menu.Item} this
25387          * @param {Roo.EventObject} e
25388          */
25389         mouseout : true,
25390         // raw events
25391         /**
25392          * @event click
25393          * The raw click event for the entire grid.
25394          * @param {Roo.EventObject} e
25395          */
25396         click : true
25397     });
25398 };
25399
25400 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25401     
25402     submenu : false,
25403     href : '',
25404     html : '',
25405     preventDefault: true,
25406     disable : false,
25407     icon : false,
25408     pos : 'right',
25409     
25410     getAutoCreate : function()
25411     {
25412         var text = [
25413             {
25414                 tag : 'span',
25415                 cls : 'roo-menu-item-text',
25416                 html : this.html
25417             }
25418         ];
25419         
25420         if(this.icon){
25421             text.unshift({
25422                 tag : 'i',
25423                 cls : 'fa ' + this.icon
25424             })
25425         }
25426         
25427         var cfg = {
25428             tag : 'li',
25429             cn : [
25430                 {
25431                     tag : 'a',
25432                     href : this.href || '#',
25433                     cn : text
25434                 }
25435             ]
25436         };
25437         
25438         if(this.disable){
25439             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25440         }
25441         
25442         if(this.submenu){
25443             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25444             
25445             if(this.pos == 'left'){
25446                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25447             }
25448         }
25449         
25450         return cfg;
25451     },
25452     
25453     initEvents : function() 
25454     {
25455         this.el.on('mouseover', this.onMouseOver, this);
25456         this.el.on('mouseout', this.onMouseOut, this);
25457         
25458         this.el.select('a', true).first().on('click', this.onClick, this);
25459         
25460     },
25461     
25462     onClick : function(e)
25463     {
25464         if(this.preventDefault){
25465             e.preventDefault();
25466         }
25467         
25468         this.fireEvent("click", this, e);
25469     },
25470     
25471     onMouseOver : function(e)
25472     {
25473         if(this.submenu && this.pos == 'left'){
25474             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25475         }
25476         
25477         this.fireEvent("mouseover", this, e);
25478     },
25479     
25480     onMouseOut : function(e)
25481     {
25482         this.fireEvent("mouseout", this, e);
25483     }
25484 });
25485
25486  
25487
25488  /*
25489  * - LGPL
25490  *
25491  * menu separator
25492  * 
25493  */
25494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25495
25496 /**
25497  * @class Roo.bootstrap.menu.Separator
25498  * @extends Roo.bootstrap.Component
25499  * Bootstrap Separator class
25500  * 
25501  * @constructor
25502  * Create a new Separator
25503  * @param {Object} config The config object
25504  */
25505
25506
25507 Roo.bootstrap.menu.Separator = function(config){
25508     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25509 };
25510
25511 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25512     
25513     getAutoCreate : function(){
25514         var cfg = {
25515             tag : 'li',
25516             cls: 'divider'
25517         };
25518         
25519         return cfg;
25520     }
25521    
25522 });
25523
25524  
25525
25526  /*
25527  * - LGPL
25528  *
25529  * Tooltip
25530  * 
25531  */
25532
25533 /**
25534  * @class Roo.bootstrap.Tooltip
25535  * Bootstrap Tooltip class
25536  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25537  * to determine which dom element triggers the tooltip.
25538  * 
25539  * It needs to add support for additional attributes like tooltip-position
25540  * 
25541  * @constructor
25542  * Create a new Toolti
25543  * @param {Object} config The config object
25544  */
25545
25546 Roo.bootstrap.Tooltip = function(config){
25547     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25548     
25549     this.alignment = Roo.bootstrap.Tooltip.alignment;
25550     
25551     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25552         this.alignment = config.alignment;
25553     }
25554     
25555 };
25556
25557 Roo.apply(Roo.bootstrap.Tooltip, {
25558     /**
25559      * @function init initialize tooltip monitoring.
25560      * @static
25561      */
25562     currentEl : false,
25563     currentTip : false,
25564     currentRegion : false,
25565     
25566     //  init : delay?
25567     
25568     init : function()
25569     {
25570         Roo.get(document).on('mouseover', this.enter ,this);
25571         Roo.get(document).on('mouseout', this.leave, this);
25572          
25573         
25574         this.currentTip = new Roo.bootstrap.Tooltip();
25575     },
25576     
25577     enter : function(ev)
25578     {
25579         var dom = ev.getTarget();
25580         
25581         //Roo.log(['enter',dom]);
25582         var el = Roo.fly(dom);
25583         if (this.currentEl) {
25584             //Roo.log(dom);
25585             //Roo.log(this.currentEl);
25586             //Roo.log(this.currentEl.contains(dom));
25587             if (this.currentEl == el) {
25588                 return;
25589             }
25590             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25591                 return;
25592             }
25593
25594         }
25595         
25596         if (this.currentTip.el) {
25597             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25598         }    
25599         //Roo.log(ev);
25600         
25601         if(!el || el.dom == document){
25602             return;
25603         }
25604         
25605         var bindEl = el;
25606         
25607         // you can not look for children, as if el is the body.. then everythign is the child..
25608         if (!el.attr('tooltip')) { //
25609             if (!el.select("[tooltip]").elements.length) {
25610                 return;
25611             }
25612             // is the mouse over this child...?
25613             bindEl = el.select("[tooltip]").first();
25614             var xy = ev.getXY();
25615             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25616                 //Roo.log("not in region.");
25617                 return;
25618             }
25619             //Roo.log("child element over..");
25620             
25621         }
25622         this.currentEl = bindEl;
25623         this.currentTip.bind(bindEl);
25624         this.currentRegion = Roo.lib.Region.getRegion(dom);
25625         this.currentTip.enter();
25626         
25627     },
25628     leave : function(ev)
25629     {
25630         var dom = ev.getTarget();
25631         //Roo.log(['leave',dom]);
25632         if (!this.currentEl) {
25633             return;
25634         }
25635         
25636         
25637         if (dom != this.currentEl.dom) {
25638             return;
25639         }
25640         var xy = ev.getXY();
25641         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25642             return;
25643         }
25644         // only activate leave if mouse cursor is outside... bounding box..
25645         
25646         
25647         
25648         
25649         if (this.currentTip) {
25650             this.currentTip.leave();
25651         }
25652         //Roo.log('clear currentEl');
25653         this.currentEl = false;
25654         
25655         
25656     },
25657     alignment : {
25658         'left' : ['r-l', [-2,0], 'right'],
25659         'right' : ['l-r', [2,0], 'left'],
25660         'bottom' : ['t-b', [0,2], 'top'],
25661         'top' : [ 'b-t', [0,-2], 'bottom']
25662     }
25663     
25664 });
25665
25666
25667 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25668     
25669     
25670     bindEl : false,
25671     
25672     delay : null, // can be { show : 300 , hide: 500}
25673     
25674     timeout : null,
25675     
25676     hoverState : null, //???
25677     
25678     placement : 'bottom', 
25679     
25680     alignment : false,
25681     
25682     getAutoCreate : function(){
25683     
25684         var cfg = {
25685            cls : 'tooltip',
25686            role : 'tooltip',
25687            cn : [
25688                 {
25689                     cls : 'tooltip-arrow'
25690                 },
25691                 {
25692                     cls : 'tooltip-inner'
25693                 }
25694            ]
25695         };
25696         
25697         return cfg;
25698     },
25699     bind : function(el)
25700     {
25701         this.bindEl = el;
25702     },
25703       
25704     
25705     enter : function () {
25706        
25707         if (this.timeout != null) {
25708             clearTimeout(this.timeout);
25709         }
25710         
25711         this.hoverState = 'in';
25712          //Roo.log("enter - show");
25713         if (!this.delay || !this.delay.show) {
25714             this.show();
25715             return;
25716         }
25717         var _t = this;
25718         this.timeout = setTimeout(function () {
25719             if (_t.hoverState == 'in') {
25720                 _t.show();
25721             }
25722         }, this.delay.show);
25723     },
25724     leave : function()
25725     {
25726         clearTimeout(this.timeout);
25727     
25728         this.hoverState = 'out';
25729          if (!this.delay || !this.delay.hide) {
25730             this.hide();
25731             return;
25732         }
25733        
25734         var _t = this;
25735         this.timeout = setTimeout(function () {
25736             //Roo.log("leave - timeout");
25737             
25738             if (_t.hoverState == 'out') {
25739                 _t.hide();
25740                 Roo.bootstrap.Tooltip.currentEl = false;
25741             }
25742         }, delay);
25743     },
25744     
25745     show : function (msg)
25746     {
25747         if (!this.el) {
25748             this.render(document.body);
25749         }
25750         // set content.
25751         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25752         
25753         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25754         
25755         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25756         
25757         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25758         
25759         var placement = typeof this.placement == 'function' ?
25760             this.placement.call(this, this.el, on_el) :
25761             this.placement;
25762             
25763         var autoToken = /\s?auto?\s?/i;
25764         var autoPlace = autoToken.test(placement);
25765         if (autoPlace) {
25766             placement = placement.replace(autoToken, '') || 'top';
25767         }
25768         
25769         //this.el.detach()
25770         //this.el.setXY([0,0]);
25771         this.el.show();
25772         //this.el.dom.style.display='block';
25773         
25774         //this.el.appendTo(on_el);
25775         
25776         var p = this.getPosition();
25777         var box = this.el.getBox();
25778         
25779         if (autoPlace) {
25780             // fixme..
25781         }
25782         
25783         var align = this.alignment[placement];
25784         
25785         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25786         
25787         if(placement == 'top' || placement == 'bottom'){
25788             if(xy[0] < 0){
25789                 placement = 'right';
25790             }
25791             
25792             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25793                 placement = 'left';
25794             }
25795             
25796             var scroll = Roo.select('body', true).first().getScroll();
25797             
25798             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25799                 placement = 'top';
25800             }
25801             
25802         }
25803         
25804         this.el.alignTo(this.bindEl, align[0],align[1]);
25805         //var arrow = this.el.select('.arrow',true).first();
25806         //arrow.set(align[2], 
25807         
25808         this.el.addClass(placement);
25809         
25810         this.el.addClass('in fade');
25811         
25812         this.hoverState = null;
25813         
25814         if (this.el.hasClass('fade')) {
25815             // fade it?
25816         }
25817         
25818     },
25819     hide : function()
25820     {
25821          
25822         if (!this.el) {
25823             return;
25824         }
25825         //this.el.setXY([0,0]);
25826         this.el.removeClass('in');
25827         //this.el.hide();
25828         
25829     }
25830     
25831 });
25832  
25833
25834  /*
25835  * - LGPL
25836  *
25837  * Location Picker
25838  * 
25839  */
25840
25841 /**
25842  * @class Roo.bootstrap.LocationPicker
25843  * @extends Roo.bootstrap.Component
25844  * Bootstrap LocationPicker class
25845  * @cfg {Number} latitude Position when init default 0
25846  * @cfg {Number} longitude Position when init default 0
25847  * @cfg {Number} zoom default 15
25848  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25849  * @cfg {Boolean} mapTypeControl default false
25850  * @cfg {Boolean} disableDoubleClickZoom default false
25851  * @cfg {Boolean} scrollwheel default true
25852  * @cfg {Boolean} streetViewControl default false
25853  * @cfg {Number} radius default 0
25854  * @cfg {String} locationName
25855  * @cfg {Boolean} draggable default true
25856  * @cfg {Boolean} enableAutocomplete default false
25857  * @cfg {Boolean} enableReverseGeocode default true
25858  * @cfg {String} markerTitle
25859  * 
25860  * @constructor
25861  * Create a new LocationPicker
25862  * @param {Object} config The config object
25863  */
25864
25865
25866 Roo.bootstrap.LocationPicker = function(config){
25867     
25868     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25869     
25870     this.addEvents({
25871         /**
25872          * @event initial
25873          * Fires when the picker initialized.
25874          * @param {Roo.bootstrap.LocationPicker} this
25875          * @param {Google Location} location
25876          */
25877         initial : true,
25878         /**
25879          * @event positionchanged
25880          * Fires when the picker position changed.
25881          * @param {Roo.bootstrap.LocationPicker} this
25882          * @param {Google Location} location
25883          */
25884         positionchanged : true,
25885         /**
25886          * @event resize
25887          * Fires when the map resize.
25888          * @param {Roo.bootstrap.LocationPicker} this
25889          */
25890         resize : true,
25891         /**
25892          * @event show
25893          * Fires when the map show.
25894          * @param {Roo.bootstrap.LocationPicker} this
25895          */
25896         show : true,
25897         /**
25898          * @event hide
25899          * Fires when the map hide.
25900          * @param {Roo.bootstrap.LocationPicker} this
25901          */
25902         hide : true,
25903         /**
25904          * @event mapClick
25905          * Fires when click the map.
25906          * @param {Roo.bootstrap.LocationPicker} this
25907          * @param {Map event} e
25908          */
25909         mapClick : true,
25910         /**
25911          * @event mapRightClick
25912          * Fires when right click the map.
25913          * @param {Roo.bootstrap.LocationPicker} this
25914          * @param {Map event} e
25915          */
25916         mapRightClick : true,
25917         /**
25918          * @event markerClick
25919          * Fires when click the marker.
25920          * @param {Roo.bootstrap.LocationPicker} this
25921          * @param {Map event} e
25922          */
25923         markerClick : true,
25924         /**
25925          * @event markerRightClick
25926          * Fires when right click the marker.
25927          * @param {Roo.bootstrap.LocationPicker} this
25928          * @param {Map event} e
25929          */
25930         markerRightClick : true,
25931         /**
25932          * @event OverlayViewDraw
25933          * Fires when OverlayView Draw
25934          * @param {Roo.bootstrap.LocationPicker} this
25935          */
25936         OverlayViewDraw : true,
25937         /**
25938          * @event OverlayViewOnAdd
25939          * Fires when OverlayView Draw
25940          * @param {Roo.bootstrap.LocationPicker} this
25941          */
25942         OverlayViewOnAdd : true,
25943         /**
25944          * @event OverlayViewOnRemove
25945          * Fires when OverlayView Draw
25946          * @param {Roo.bootstrap.LocationPicker} this
25947          */
25948         OverlayViewOnRemove : true,
25949         /**
25950          * @event OverlayViewShow
25951          * Fires when OverlayView Draw
25952          * @param {Roo.bootstrap.LocationPicker} this
25953          * @param {Pixel} cpx
25954          */
25955         OverlayViewShow : true,
25956         /**
25957          * @event OverlayViewHide
25958          * Fires when OverlayView Draw
25959          * @param {Roo.bootstrap.LocationPicker} this
25960          */
25961         OverlayViewHide : true,
25962         /**
25963          * @event loadexception
25964          * Fires when load google lib failed.
25965          * @param {Roo.bootstrap.LocationPicker} this
25966          */
25967         loadexception : true
25968     });
25969         
25970 };
25971
25972 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25973     
25974     gMapContext: false,
25975     
25976     latitude: 0,
25977     longitude: 0,
25978     zoom: 15,
25979     mapTypeId: false,
25980     mapTypeControl: false,
25981     disableDoubleClickZoom: false,
25982     scrollwheel: true,
25983     streetViewControl: false,
25984     radius: 0,
25985     locationName: '',
25986     draggable: true,
25987     enableAutocomplete: false,
25988     enableReverseGeocode: true,
25989     markerTitle: '',
25990     
25991     getAutoCreate: function()
25992     {
25993
25994         var cfg = {
25995             tag: 'div',
25996             cls: 'roo-location-picker'
25997         };
25998         
25999         return cfg
26000     },
26001     
26002     initEvents: function(ct, position)
26003     {       
26004         if(!this.el.getWidth() || this.isApplied()){
26005             return;
26006         }
26007         
26008         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26009         
26010         this.initial();
26011     },
26012     
26013     initial: function()
26014     {
26015         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26016             this.fireEvent('loadexception', this);
26017             return;
26018         }
26019         
26020         if(!this.mapTypeId){
26021             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26022         }
26023         
26024         this.gMapContext = this.GMapContext();
26025         
26026         this.initOverlayView();
26027         
26028         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26029         
26030         var _this = this;
26031                 
26032         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26033             _this.setPosition(_this.gMapContext.marker.position);
26034         });
26035         
26036         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26037             _this.fireEvent('mapClick', this, event);
26038             
26039         });
26040
26041         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26042             _this.fireEvent('mapRightClick', this, event);
26043             
26044         });
26045         
26046         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26047             _this.fireEvent('markerClick', this, event);
26048             
26049         });
26050
26051         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26052             _this.fireEvent('markerRightClick', this, event);
26053             
26054         });
26055         
26056         this.setPosition(this.gMapContext.location);
26057         
26058         this.fireEvent('initial', this, this.gMapContext.location);
26059     },
26060     
26061     initOverlayView: function()
26062     {
26063         var _this = this;
26064         
26065         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26066             
26067             draw: function()
26068             {
26069                 _this.fireEvent('OverlayViewDraw', _this);
26070             },
26071             
26072             onAdd: function()
26073             {
26074                 _this.fireEvent('OverlayViewOnAdd', _this);
26075             },
26076             
26077             onRemove: function()
26078             {
26079                 _this.fireEvent('OverlayViewOnRemove', _this);
26080             },
26081             
26082             show: function(cpx)
26083             {
26084                 _this.fireEvent('OverlayViewShow', _this, cpx);
26085             },
26086             
26087             hide: function()
26088             {
26089                 _this.fireEvent('OverlayViewHide', _this);
26090             }
26091             
26092         });
26093     },
26094     
26095     fromLatLngToContainerPixel: function(event)
26096     {
26097         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26098     },
26099     
26100     isApplied: function() 
26101     {
26102         return this.getGmapContext() == false ? false : true;
26103     },
26104     
26105     getGmapContext: function() 
26106     {
26107         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26108     },
26109     
26110     GMapContext: function() 
26111     {
26112         var position = new google.maps.LatLng(this.latitude, this.longitude);
26113         
26114         var _map = new google.maps.Map(this.el.dom, {
26115             center: position,
26116             zoom: this.zoom,
26117             mapTypeId: this.mapTypeId,
26118             mapTypeControl: this.mapTypeControl,
26119             disableDoubleClickZoom: this.disableDoubleClickZoom,
26120             scrollwheel: this.scrollwheel,
26121             streetViewControl: this.streetViewControl,
26122             locationName: this.locationName,
26123             draggable: this.draggable,
26124             enableAutocomplete: this.enableAutocomplete,
26125             enableReverseGeocode: this.enableReverseGeocode
26126         });
26127         
26128         var _marker = new google.maps.Marker({
26129             position: position,
26130             map: _map,
26131             title: this.markerTitle,
26132             draggable: this.draggable
26133         });
26134         
26135         return {
26136             map: _map,
26137             marker: _marker,
26138             circle: null,
26139             location: position,
26140             radius: this.radius,
26141             locationName: this.locationName,
26142             addressComponents: {
26143                 formatted_address: null,
26144                 addressLine1: null,
26145                 addressLine2: null,
26146                 streetName: null,
26147                 streetNumber: null,
26148                 city: null,
26149                 district: null,
26150                 state: null,
26151                 stateOrProvince: null
26152             },
26153             settings: this,
26154             domContainer: this.el.dom,
26155             geodecoder: new google.maps.Geocoder()
26156         };
26157     },
26158     
26159     drawCircle: function(center, radius, options) 
26160     {
26161         if (this.gMapContext.circle != null) {
26162             this.gMapContext.circle.setMap(null);
26163         }
26164         if (radius > 0) {
26165             radius *= 1;
26166             options = Roo.apply({}, options, {
26167                 strokeColor: "#0000FF",
26168                 strokeOpacity: .35,
26169                 strokeWeight: 2,
26170                 fillColor: "#0000FF",
26171                 fillOpacity: .2
26172             });
26173             
26174             options.map = this.gMapContext.map;
26175             options.radius = radius;
26176             options.center = center;
26177             this.gMapContext.circle = new google.maps.Circle(options);
26178             return this.gMapContext.circle;
26179         }
26180         
26181         return null;
26182     },
26183     
26184     setPosition: function(location) 
26185     {
26186         this.gMapContext.location = location;
26187         this.gMapContext.marker.setPosition(location);
26188         this.gMapContext.map.panTo(location);
26189         this.drawCircle(location, this.gMapContext.radius, {});
26190         
26191         var _this = this;
26192         
26193         if (this.gMapContext.settings.enableReverseGeocode) {
26194             this.gMapContext.geodecoder.geocode({
26195                 latLng: this.gMapContext.location
26196             }, function(results, status) {
26197                 
26198                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26199                     _this.gMapContext.locationName = results[0].formatted_address;
26200                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26201                     
26202                     _this.fireEvent('positionchanged', this, location);
26203                 }
26204             });
26205             
26206             return;
26207         }
26208         
26209         this.fireEvent('positionchanged', this, location);
26210     },
26211     
26212     resize: function()
26213     {
26214         google.maps.event.trigger(this.gMapContext.map, "resize");
26215         
26216         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26217         
26218         this.fireEvent('resize', this);
26219     },
26220     
26221     setPositionByLatLng: function(latitude, longitude)
26222     {
26223         this.setPosition(new google.maps.LatLng(latitude, longitude));
26224     },
26225     
26226     getCurrentPosition: function() 
26227     {
26228         return {
26229             latitude: this.gMapContext.location.lat(),
26230             longitude: this.gMapContext.location.lng()
26231         };
26232     },
26233     
26234     getAddressName: function() 
26235     {
26236         return this.gMapContext.locationName;
26237     },
26238     
26239     getAddressComponents: function() 
26240     {
26241         return this.gMapContext.addressComponents;
26242     },
26243     
26244     address_component_from_google_geocode: function(address_components) 
26245     {
26246         var result = {};
26247         
26248         for (var i = 0; i < address_components.length; i++) {
26249             var component = address_components[i];
26250             if (component.types.indexOf("postal_code") >= 0) {
26251                 result.postalCode = component.short_name;
26252             } else if (component.types.indexOf("street_number") >= 0) {
26253                 result.streetNumber = component.short_name;
26254             } else if (component.types.indexOf("route") >= 0) {
26255                 result.streetName = component.short_name;
26256             } else if (component.types.indexOf("neighborhood") >= 0) {
26257                 result.city = component.short_name;
26258             } else if (component.types.indexOf("locality") >= 0) {
26259                 result.city = component.short_name;
26260             } else if (component.types.indexOf("sublocality") >= 0) {
26261                 result.district = component.short_name;
26262             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26263                 result.stateOrProvince = component.short_name;
26264             } else if (component.types.indexOf("country") >= 0) {
26265                 result.country = component.short_name;
26266             }
26267         }
26268         
26269         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26270         result.addressLine2 = "";
26271         return result;
26272     },
26273     
26274     setZoomLevel: function(zoom)
26275     {
26276         this.gMapContext.map.setZoom(zoom);
26277     },
26278     
26279     show: function()
26280     {
26281         if(!this.el){
26282             return;
26283         }
26284         
26285         this.el.show();
26286         
26287         this.resize();
26288         
26289         this.fireEvent('show', this);
26290     },
26291     
26292     hide: function()
26293     {
26294         if(!this.el){
26295             return;
26296         }
26297         
26298         this.el.hide();
26299         
26300         this.fireEvent('hide', this);
26301     }
26302     
26303 });
26304
26305 Roo.apply(Roo.bootstrap.LocationPicker, {
26306     
26307     OverlayView : function(map, options)
26308     {
26309         options = options || {};
26310         
26311         this.setMap(map);
26312     }
26313     
26314     
26315 });/*
26316  * - LGPL
26317  *
26318  * Alert
26319  * 
26320  */
26321
26322 /**
26323  * @class Roo.bootstrap.Alert
26324  * @extends Roo.bootstrap.Component
26325  * Bootstrap Alert class
26326  * @cfg {String} title The title of alert
26327  * @cfg {String} html The content of alert
26328  * @cfg {String} weight (  success | info | warning | danger )
26329  * @cfg {String} faicon font-awesomeicon
26330  * 
26331  * @constructor
26332  * Create a new alert
26333  * @param {Object} config The config object
26334  */
26335
26336
26337 Roo.bootstrap.Alert = function(config){
26338     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26339     
26340 };
26341
26342 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26343     
26344     title: '',
26345     html: '',
26346     weight: false,
26347     faicon: false,
26348     
26349     getAutoCreate : function()
26350     {
26351         
26352         var cfg = {
26353             tag : 'div',
26354             cls : 'alert',
26355             cn : [
26356                 {
26357                     tag : 'i',
26358                     cls : 'roo-alert-icon'
26359                     
26360                 },
26361                 {
26362                     tag : 'b',
26363                     cls : 'roo-alert-title',
26364                     html : this.title
26365                 },
26366                 {
26367                     tag : 'span',
26368                     cls : 'roo-alert-text',
26369                     html : this.html
26370                 }
26371             ]
26372         };
26373         
26374         if(this.faicon){
26375             cfg.cn[0].cls += ' fa ' + this.faicon;
26376         }
26377         
26378         if(this.weight){
26379             cfg.cls += ' alert-' + this.weight;
26380         }
26381         
26382         return cfg;
26383     },
26384     
26385     initEvents: function() 
26386     {
26387         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26388     },
26389     
26390     setTitle : function(str)
26391     {
26392         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26393     },
26394     
26395     setText : function(str)
26396     {
26397         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26398     },
26399     
26400     setWeight : function(weight)
26401     {
26402         if(this.weight){
26403             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26404         }
26405         
26406         this.weight = weight;
26407         
26408         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26409     },
26410     
26411     setIcon : function(icon)
26412     {
26413         if(this.faicon){
26414             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26415         }
26416         
26417         this.faicon = icon;
26418         
26419         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26420     },
26421     
26422     hide: function() 
26423     {
26424         this.el.hide();   
26425     },
26426     
26427     show: function() 
26428     {  
26429         this.el.show();   
26430     }
26431     
26432 });
26433
26434  
26435 /*
26436 * Licence: LGPL
26437 */
26438
26439 /**
26440  * @class Roo.bootstrap.UploadCropbox
26441  * @extends Roo.bootstrap.Component
26442  * Bootstrap UploadCropbox class
26443  * @cfg {String} emptyText show when image has been loaded
26444  * @cfg {String} rotateNotify show when image too small to rotate
26445  * @cfg {Number} errorTimeout default 3000
26446  * @cfg {Number} minWidth default 300
26447  * @cfg {Number} minHeight default 300
26448  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26449  * @cfg {Boolean} isDocument (true|false) default false
26450  * @cfg {String} url action url
26451  * @cfg {String} paramName default 'imageUpload'
26452  * @cfg {String} method default POST
26453  * @cfg {Boolean} loadMask (true|false) default true
26454  * @cfg {Boolean} loadingText default 'Loading...'
26455  * 
26456  * @constructor
26457  * Create a new UploadCropbox
26458  * @param {Object} config The config object
26459  */
26460
26461 Roo.bootstrap.UploadCropbox = function(config){
26462     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26463     
26464     this.addEvents({
26465         /**
26466          * @event beforeselectfile
26467          * Fire before select file
26468          * @param {Roo.bootstrap.UploadCropbox} this
26469          */
26470         "beforeselectfile" : true,
26471         /**
26472          * @event initial
26473          * Fire after initEvent
26474          * @param {Roo.bootstrap.UploadCropbox} this
26475          */
26476         "initial" : true,
26477         /**
26478          * @event crop
26479          * Fire after initEvent
26480          * @param {Roo.bootstrap.UploadCropbox} this
26481          * @param {String} data
26482          */
26483         "crop" : true,
26484         /**
26485          * @event prepare
26486          * Fire when preparing the file data
26487          * @param {Roo.bootstrap.UploadCropbox} this
26488          * @param {Object} file
26489          */
26490         "prepare" : true,
26491         /**
26492          * @event exception
26493          * Fire when get exception
26494          * @param {Roo.bootstrap.UploadCropbox} this
26495          * @param {XMLHttpRequest} xhr
26496          */
26497         "exception" : true,
26498         /**
26499          * @event beforeloadcanvas
26500          * Fire before load the canvas
26501          * @param {Roo.bootstrap.UploadCropbox} this
26502          * @param {String} src
26503          */
26504         "beforeloadcanvas" : true,
26505         /**
26506          * @event trash
26507          * Fire when trash image
26508          * @param {Roo.bootstrap.UploadCropbox} this
26509          */
26510         "trash" : true,
26511         /**
26512          * @event download
26513          * Fire when download the image
26514          * @param {Roo.bootstrap.UploadCropbox} this
26515          */
26516         "download" : true,
26517         /**
26518          * @event footerbuttonclick
26519          * Fire when footerbuttonclick
26520          * @param {Roo.bootstrap.UploadCropbox} this
26521          * @param {String} type
26522          */
26523         "footerbuttonclick" : true,
26524         /**
26525          * @event resize
26526          * Fire when resize
26527          * @param {Roo.bootstrap.UploadCropbox} this
26528          */
26529         "resize" : true,
26530         /**
26531          * @event rotate
26532          * Fire when rotate the image
26533          * @param {Roo.bootstrap.UploadCropbox} this
26534          * @param {String} pos
26535          */
26536         "rotate" : true,
26537         /**
26538          * @event inspect
26539          * Fire when inspect the file
26540          * @param {Roo.bootstrap.UploadCropbox} this
26541          * @param {Object} file
26542          */
26543         "inspect" : true,
26544         /**
26545          * @event upload
26546          * Fire when xhr upload the file
26547          * @param {Roo.bootstrap.UploadCropbox} this
26548          * @param {Object} data
26549          */
26550         "upload" : true,
26551         /**
26552          * @event arrange
26553          * Fire when arrange the file data
26554          * @param {Roo.bootstrap.UploadCropbox} this
26555          * @param {Object} formData
26556          */
26557         "arrange" : true
26558     });
26559     
26560     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26561 };
26562
26563 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26564     
26565     emptyText : 'Click to upload image',
26566     rotateNotify : 'Image is too small to rotate',
26567     errorTimeout : 3000,
26568     scale : 0,
26569     baseScale : 1,
26570     rotate : 0,
26571     dragable : false,
26572     pinching : false,
26573     mouseX : 0,
26574     mouseY : 0,
26575     cropData : false,
26576     minWidth : 300,
26577     minHeight : 300,
26578     file : false,
26579     exif : {},
26580     baseRotate : 1,
26581     cropType : 'image/jpeg',
26582     buttons : false,
26583     canvasLoaded : false,
26584     isDocument : false,
26585     method : 'POST',
26586     paramName : 'imageUpload',
26587     loadMask : true,
26588     loadingText : 'Loading...',
26589     maskEl : false,
26590     
26591     getAutoCreate : function()
26592     {
26593         var cfg = {
26594             tag : 'div',
26595             cls : 'roo-upload-cropbox',
26596             cn : [
26597                 {
26598                     tag : 'input',
26599                     cls : 'roo-upload-cropbox-selector',
26600                     type : 'file'
26601                 },
26602                 {
26603                     tag : 'div',
26604                     cls : 'roo-upload-cropbox-body',
26605                     style : 'cursor:pointer',
26606                     cn : [
26607                         {
26608                             tag : 'div',
26609                             cls : 'roo-upload-cropbox-preview'
26610                         },
26611                         {
26612                             tag : 'div',
26613                             cls : 'roo-upload-cropbox-thumb'
26614                         },
26615                         {
26616                             tag : 'div',
26617                             cls : 'roo-upload-cropbox-empty-notify',
26618                             html : this.emptyText
26619                         },
26620                         {
26621                             tag : 'div',
26622                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26623                             html : this.rotateNotify
26624                         }
26625                     ]
26626                 },
26627                 {
26628                     tag : 'div',
26629                     cls : 'roo-upload-cropbox-footer',
26630                     cn : {
26631                         tag : 'div',
26632                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26633                         cn : []
26634                     }
26635                 }
26636             ]
26637         };
26638         
26639         return cfg;
26640     },
26641     
26642     onRender : function(ct, position)
26643     {
26644         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26645         
26646         if (this.buttons.length) {
26647             
26648             Roo.each(this.buttons, function(bb) {
26649                 
26650                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26651                 
26652                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26653                 
26654             }, this);
26655         }
26656         
26657         if(this.loadMask){
26658             this.maskEl = this.el;
26659         }
26660     },
26661     
26662     initEvents : function()
26663     {
26664         this.urlAPI = (window.createObjectURL && window) || 
26665                                 (window.URL && URL.revokeObjectURL && URL) || 
26666                                 (window.webkitURL && webkitURL);
26667                         
26668         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26669         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26670         
26671         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26672         this.selectorEl.hide();
26673         
26674         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26675         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26676         
26677         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26678         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26679         this.thumbEl.hide();
26680         
26681         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26682         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26683         
26684         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26685         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686         this.errorEl.hide();
26687         
26688         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26689         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26690         this.footerEl.hide();
26691         
26692         this.setThumbBoxSize();
26693         
26694         this.bind();
26695         
26696         this.resize();
26697         
26698         this.fireEvent('initial', this);
26699     },
26700
26701     bind : function()
26702     {
26703         var _this = this;
26704         
26705         window.addEventListener("resize", function() { _this.resize(); } );
26706         
26707         this.bodyEl.on('click', this.beforeSelectFile, this);
26708         
26709         if(Roo.isTouch){
26710             this.bodyEl.on('touchstart', this.onTouchStart, this);
26711             this.bodyEl.on('touchmove', this.onTouchMove, this);
26712             this.bodyEl.on('touchend', this.onTouchEnd, this);
26713         }
26714         
26715         if(!Roo.isTouch){
26716             this.bodyEl.on('mousedown', this.onMouseDown, this);
26717             this.bodyEl.on('mousemove', this.onMouseMove, this);
26718             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26719             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26720             Roo.get(document).on('mouseup', this.onMouseUp, this);
26721         }
26722         
26723         this.selectorEl.on('change', this.onFileSelected, this);
26724     },
26725     
26726     reset : function()
26727     {    
26728         this.scale = 0;
26729         this.baseScale = 1;
26730         this.rotate = 0;
26731         this.baseRotate = 1;
26732         this.dragable = false;
26733         this.pinching = false;
26734         this.mouseX = 0;
26735         this.mouseY = 0;
26736         this.cropData = false;
26737         this.notifyEl.dom.innerHTML = this.emptyText;
26738         
26739         this.selectorEl.dom.value = '';
26740         
26741     },
26742     
26743     resize : function()
26744     {
26745         if(this.fireEvent('resize', this) != false){
26746             this.setThumbBoxPosition();
26747             this.setCanvasPosition();
26748         }
26749     },
26750     
26751     onFooterButtonClick : function(e, el, o, type)
26752     {
26753         switch (type) {
26754             case 'rotate-left' :
26755                 this.onRotateLeft(e);
26756                 break;
26757             case 'rotate-right' :
26758                 this.onRotateRight(e);
26759                 break;
26760             case 'picture' :
26761                 this.beforeSelectFile(e);
26762                 break;
26763             case 'trash' :
26764                 this.trash(e);
26765                 break;
26766             case 'crop' :
26767                 this.crop(e);
26768                 break;
26769             case 'download' :
26770                 this.download(e);
26771                 break;
26772             default :
26773                 break;
26774         }
26775         
26776         this.fireEvent('footerbuttonclick', this, type);
26777     },
26778     
26779     beforeSelectFile : function(e)
26780     {
26781         e.preventDefault();
26782         
26783         if(this.fireEvent('beforeselectfile', this) != false){
26784             this.selectorEl.dom.click();
26785         }
26786     },
26787     
26788     onFileSelected : function(e)
26789     {
26790         e.preventDefault();
26791         
26792         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26793             return;
26794         }
26795         
26796         var file = this.selectorEl.dom.files[0];
26797         
26798         if(this.fireEvent('inspect', this, file) != false){
26799             this.prepare(file);
26800         }
26801         
26802     },
26803     
26804     trash : function(e)
26805     {
26806         this.fireEvent('trash', this);
26807     },
26808     
26809     download : function(e)
26810     {
26811         this.fireEvent('download', this);
26812     },
26813     
26814     loadCanvas : function(src)
26815     {   
26816         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26817             
26818             this.reset();
26819             
26820             this.imageEl = document.createElement('img');
26821             
26822             var _this = this;
26823             
26824             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26825             
26826             this.imageEl.src = src;
26827         }
26828     },
26829     
26830     onLoadCanvas : function()
26831     {   
26832         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26833         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26834         
26835         this.bodyEl.un('click', this.beforeSelectFile, this);
26836         
26837         this.notifyEl.hide();
26838         this.thumbEl.show();
26839         this.footerEl.show();
26840         
26841         this.baseRotateLevel();
26842         
26843         if(this.isDocument){
26844             this.setThumbBoxSize();
26845         }
26846         
26847         this.setThumbBoxPosition();
26848         
26849         this.baseScaleLevel();
26850         
26851         this.draw();
26852         
26853         this.resize();
26854         
26855         this.canvasLoaded = true;
26856         
26857         if(this.loadMask){
26858             this.maskEl.unmask();
26859         }
26860         
26861     },
26862     
26863     setCanvasPosition : function()
26864     {   
26865         if(!this.canvasEl){
26866             return;
26867         }
26868         
26869         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26870         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26871         
26872         this.previewEl.setLeft(pw);
26873         this.previewEl.setTop(ph);
26874         
26875     },
26876     
26877     onMouseDown : function(e)
26878     {   
26879         e.stopEvent();
26880         
26881         this.dragable = true;
26882         this.pinching = false;
26883         
26884         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26885             this.dragable = false;
26886             return;
26887         }
26888         
26889         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26890         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26891         
26892     },
26893     
26894     onMouseMove : function(e)
26895     {   
26896         e.stopEvent();
26897         
26898         if(!this.canvasLoaded){
26899             return;
26900         }
26901         
26902         if (!this.dragable){
26903             return;
26904         }
26905         
26906         var minX = Math.ceil(this.thumbEl.getLeft(true));
26907         var minY = Math.ceil(this.thumbEl.getTop(true));
26908         
26909         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26910         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26911         
26912         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26913         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26914         
26915         x = x - this.mouseX;
26916         y = y - this.mouseY;
26917         
26918         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26919         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26920         
26921         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26922         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26923         
26924         this.previewEl.setLeft(bgX);
26925         this.previewEl.setTop(bgY);
26926         
26927         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26928         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26929     },
26930     
26931     onMouseUp : function(e)
26932     {   
26933         e.stopEvent();
26934         
26935         this.dragable = false;
26936     },
26937     
26938     onMouseWheel : function(e)
26939     {   
26940         e.stopEvent();
26941         
26942         this.startScale = this.scale;
26943         
26944         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26945         
26946         if(!this.zoomable()){
26947             this.scale = this.startScale;
26948             return;
26949         }
26950         
26951         this.draw();
26952         
26953         return;
26954     },
26955     
26956     zoomable : function()
26957     {
26958         var minScale = this.thumbEl.getWidth() / this.minWidth;
26959         
26960         if(this.minWidth < this.minHeight){
26961             minScale = this.thumbEl.getHeight() / this.minHeight;
26962         }
26963         
26964         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26965         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26966         
26967         if(
26968                 this.isDocument &&
26969                 (this.rotate == 0 || this.rotate == 180) && 
26970                 (
26971                     width > this.imageEl.OriginWidth || 
26972                     height > this.imageEl.OriginHeight ||
26973                     (width < this.minWidth && height < this.minHeight)
26974                 )
26975         ){
26976             return false;
26977         }
26978         
26979         if(
26980                 this.isDocument &&
26981                 (this.rotate == 90 || this.rotate == 270) && 
26982                 (
26983                     width > this.imageEl.OriginWidth || 
26984                     height > this.imageEl.OriginHeight ||
26985                     (width < this.minHeight && height < this.minWidth)
26986                 )
26987         ){
26988             return false;
26989         }
26990         
26991         if(
26992                 !this.isDocument &&
26993                 (this.rotate == 0 || this.rotate == 180) && 
26994                 (
26995                     width < this.minWidth || 
26996                     width > this.imageEl.OriginWidth || 
26997                     height < this.minHeight || 
26998                     height > this.imageEl.OriginHeight
26999                 )
27000         ){
27001             return false;
27002         }
27003         
27004         if(
27005                 !this.isDocument &&
27006                 (this.rotate == 90 || this.rotate == 270) && 
27007                 (
27008                     width < this.minHeight || 
27009                     width > this.imageEl.OriginWidth || 
27010                     height < this.minWidth || 
27011                     height > this.imageEl.OriginHeight
27012                 )
27013         ){
27014             return false;
27015         }
27016         
27017         return true;
27018         
27019     },
27020     
27021     onRotateLeft : function(e)
27022     {   
27023         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27024             
27025             var minScale = this.thumbEl.getWidth() / this.minWidth;
27026             
27027             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27028             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27029             
27030             this.startScale = this.scale;
27031             
27032             while (this.getScaleLevel() < minScale){
27033             
27034                 this.scale = this.scale + 1;
27035                 
27036                 if(!this.zoomable()){
27037                     break;
27038                 }
27039                 
27040                 if(
27041                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27042                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27043                 ){
27044                     continue;
27045                 }
27046                 
27047                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27048
27049                 this.draw();
27050                 
27051                 return;
27052             }
27053             
27054             this.scale = this.startScale;
27055             
27056             this.onRotateFail();
27057             
27058             return false;
27059         }
27060         
27061         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27062
27063         if(this.isDocument){
27064             this.setThumbBoxSize();
27065             this.setThumbBoxPosition();
27066             this.setCanvasPosition();
27067         }
27068         
27069         this.draw();
27070         
27071         this.fireEvent('rotate', this, 'left');
27072         
27073     },
27074     
27075     onRotateRight : function(e)
27076     {
27077         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27078             
27079             var minScale = this.thumbEl.getWidth() / this.minWidth;
27080         
27081             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27082             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27083             
27084             this.startScale = this.scale;
27085             
27086             while (this.getScaleLevel() < minScale){
27087             
27088                 this.scale = this.scale + 1;
27089                 
27090                 if(!this.zoomable()){
27091                     break;
27092                 }
27093                 
27094                 if(
27095                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27096                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27097                 ){
27098                     continue;
27099                 }
27100                 
27101                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27102
27103                 this.draw();
27104                 
27105                 return;
27106             }
27107             
27108             this.scale = this.startScale;
27109             
27110             this.onRotateFail();
27111             
27112             return false;
27113         }
27114         
27115         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27116
27117         if(this.isDocument){
27118             this.setThumbBoxSize();
27119             this.setThumbBoxPosition();
27120             this.setCanvasPosition();
27121         }
27122         
27123         this.draw();
27124         
27125         this.fireEvent('rotate', this, 'right');
27126     },
27127     
27128     onRotateFail : function()
27129     {
27130         this.errorEl.show(true);
27131         
27132         var _this = this;
27133         
27134         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27135     },
27136     
27137     draw : function()
27138     {
27139         this.previewEl.dom.innerHTML = '';
27140         
27141         var canvasEl = document.createElement("canvas");
27142         
27143         var contextEl = canvasEl.getContext("2d");
27144         
27145         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27146         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27147         var center = this.imageEl.OriginWidth / 2;
27148         
27149         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27150             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27151             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27152             center = this.imageEl.OriginHeight / 2;
27153         }
27154         
27155         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27156         
27157         contextEl.translate(center, center);
27158         contextEl.rotate(this.rotate * Math.PI / 180);
27159
27160         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27161         
27162         this.canvasEl = document.createElement("canvas");
27163         
27164         this.contextEl = this.canvasEl.getContext("2d");
27165         
27166         switch (this.rotate) {
27167             case 0 :
27168                 
27169                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27170                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27171                 
27172                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27173                 
27174                 break;
27175             case 90 : 
27176                 
27177                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27178                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27179                 
27180                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27181                     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);
27182                     break;
27183                 }
27184                 
27185                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27186                 
27187                 break;
27188             case 180 :
27189                 
27190                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27191                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27192                 
27193                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27194                     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);
27195                     break;
27196                 }
27197                 
27198                 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);
27199                 
27200                 break;
27201             case 270 :
27202                 
27203                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27204                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27205         
27206                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27207                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27208                     break;
27209                 }
27210                 
27211                 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);
27212                 
27213                 break;
27214             default : 
27215                 break;
27216         }
27217         
27218         this.previewEl.appendChild(this.canvasEl);
27219         
27220         this.setCanvasPosition();
27221     },
27222     
27223     crop : function()
27224     {
27225         if(!this.canvasLoaded){
27226             return;
27227         }
27228         
27229         var imageCanvas = document.createElement("canvas");
27230         
27231         var imageContext = imageCanvas.getContext("2d");
27232         
27233         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27234         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27235         
27236         var center = imageCanvas.width / 2;
27237         
27238         imageContext.translate(center, center);
27239         
27240         imageContext.rotate(this.rotate * Math.PI / 180);
27241         
27242         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27243         
27244         var canvas = document.createElement("canvas");
27245         
27246         var context = canvas.getContext("2d");
27247                 
27248         canvas.width = this.minWidth;
27249         canvas.height = this.minHeight;
27250
27251         switch (this.rotate) {
27252             case 0 :
27253                 
27254                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27255                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27256                 
27257                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27258                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27259                 
27260                 var targetWidth = this.minWidth - 2 * x;
27261                 var targetHeight = this.minHeight - 2 * y;
27262                 
27263                 var scale = 1;
27264                 
27265                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27266                     scale = targetWidth / width;
27267                 }
27268                 
27269                 if(x > 0 && y == 0){
27270                     scale = targetHeight / height;
27271                 }
27272                 
27273                 if(x > 0 && y > 0){
27274                     scale = targetWidth / width;
27275                     
27276                     if(width < height){
27277                         scale = targetHeight / height;
27278                     }
27279                 }
27280                 
27281                 context.scale(scale, scale);
27282                 
27283                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27284                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27285
27286                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27287                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27288
27289                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27290                 
27291                 break;
27292             case 90 : 
27293                 
27294                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27295                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27296                 
27297                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27298                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27299                 
27300                 var targetWidth = this.minWidth - 2 * x;
27301                 var targetHeight = this.minHeight - 2 * y;
27302                 
27303                 var scale = 1;
27304                 
27305                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27306                     scale = targetWidth / width;
27307                 }
27308                 
27309                 if(x > 0 && y == 0){
27310                     scale = targetHeight / height;
27311                 }
27312                 
27313                 if(x > 0 && y > 0){
27314                     scale = targetWidth / width;
27315                     
27316                     if(width < height){
27317                         scale = targetHeight / height;
27318                     }
27319                 }
27320                 
27321                 context.scale(scale, scale);
27322                 
27323                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27324                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27325
27326                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27327                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27328                 
27329                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27330                 
27331                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27332                 
27333                 break;
27334             case 180 :
27335                 
27336                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27337                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27338                 
27339                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27340                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27341                 
27342                 var targetWidth = this.minWidth - 2 * x;
27343                 var targetHeight = this.minHeight - 2 * y;
27344                 
27345                 var scale = 1;
27346                 
27347                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27348                     scale = targetWidth / width;
27349                 }
27350                 
27351                 if(x > 0 && y == 0){
27352                     scale = targetHeight / height;
27353                 }
27354                 
27355                 if(x > 0 && y > 0){
27356                     scale = targetWidth / width;
27357                     
27358                     if(width < height){
27359                         scale = targetHeight / height;
27360                     }
27361                 }
27362                 
27363                 context.scale(scale, scale);
27364                 
27365                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27366                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27367
27368                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27369                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27370
27371                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27372                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27373                 
27374                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27375                 
27376                 break;
27377             case 270 :
27378                 
27379                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27380                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27381                 
27382                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27383                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27384                 
27385                 var targetWidth = this.minWidth - 2 * x;
27386                 var targetHeight = this.minHeight - 2 * y;
27387                 
27388                 var scale = 1;
27389                 
27390                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27391                     scale = targetWidth / width;
27392                 }
27393                 
27394                 if(x > 0 && y == 0){
27395                     scale = targetHeight / height;
27396                 }
27397                 
27398                 if(x > 0 && y > 0){
27399                     scale = targetWidth / width;
27400                     
27401                     if(width < height){
27402                         scale = targetHeight / height;
27403                     }
27404                 }
27405                 
27406                 context.scale(scale, scale);
27407                 
27408                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27409                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27410
27411                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27412                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27413                 
27414                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27415                 
27416                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27417                 
27418                 break;
27419             default : 
27420                 break;
27421         }
27422         
27423         this.cropData = canvas.toDataURL(this.cropType);
27424         
27425         if(this.fireEvent('crop', this, this.cropData) !== false){
27426             this.process(this.file, this.cropData);
27427         }
27428         
27429         return;
27430         
27431     },
27432     
27433     setThumbBoxSize : function()
27434     {
27435         var width, height;
27436         
27437         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27438             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27439             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27440             
27441             this.minWidth = width;
27442             this.minHeight = height;
27443             
27444             if(this.rotate == 90 || this.rotate == 270){
27445                 this.minWidth = height;
27446                 this.minHeight = width;
27447             }
27448         }
27449         
27450         height = 300;
27451         width = Math.ceil(this.minWidth * height / this.minHeight);
27452         
27453         if(this.minWidth > this.minHeight){
27454             width = 300;
27455             height = Math.ceil(this.minHeight * width / this.minWidth);
27456         }
27457         
27458         this.thumbEl.setStyle({
27459             width : width + 'px',
27460             height : height + 'px'
27461         });
27462
27463         return;
27464             
27465     },
27466     
27467     setThumbBoxPosition : function()
27468     {
27469         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27470         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27471         
27472         this.thumbEl.setLeft(x);
27473         this.thumbEl.setTop(y);
27474         
27475     },
27476     
27477     baseRotateLevel : function()
27478     {
27479         this.baseRotate = 1;
27480         
27481         if(
27482                 typeof(this.exif) != 'undefined' &&
27483                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27484                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27485         ){
27486             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27487         }
27488         
27489         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27490         
27491     },
27492     
27493     baseScaleLevel : function()
27494     {
27495         var width, height;
27496         
27497         if(this.isDocument){
27498             
27499             if(this.baseRotate == 6 || this.baseRotate == 8){
27500             
27501                 height = this.thumbEl.getHeight();
27502                 this.baseScale = height / this.imageEl.OriginWidth;
27503
27504                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27505                     width = this.thumbEl.getWidth();
27506                     this.baseScale = width / this.imageEl.OriginHeight;
27507                 }
27508
27509                 return;
27510             }
27511
27512             height = this.thumbEl.getHeight();
27513             this.baseScale = height / this.imageEl.OriginHeight;
27514
27515             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27516                 width = this.thumbEl.getWidth();
27517                 this.baseScale = width / this.imageEl.OriginWidth;
27518             }
27519
27520             return;
27521         }
27522         
27523         if(this.baseRotate == 6 || this.baseRotate == 8){
27524             
27525             width = this.thumbEl.getHeight();
27526             this.baseScale = width / this.imageEl.OriginHeight;
27527             
27528             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27529                 height = this.thumbEl.getWidth();
27530                 this.baseScale = height / this.imageEl.OriginHeight;
27531             }
27532             
27533             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27534                 height = this.thumbEl.getWidth();
27535                 this.baseScale = height / this.imageEl.OriginHeight;
27536                 
27537                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27538                     width = this.thumbEl.getHeight();
27539                     this.baseScale = width / this.imageEl.OriginWidth;
27540                 }
27541             }
27542             
27543             return;
27544         }
27545         
27546         width = this.thumbEl.getWidth();
27547         this.baseScale = width / this.imageEl.OriginWidth;
27548         
27549         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27550             height = this.thumbEl.getHeight();
27551             this.baseScale = height / this.imageEl.OriginHeight;
27552         }
27553         
27554         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27555             
27556             height = this.thumbEl.getHeight();
27557             this.baseScale = height / this.imageEl.OriginHeight;
27558             
27559             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27560                 width = this.thumbEl.getWidth();
27561                 this.baseScale = width / this.imageEl.OriginWidth;
27562             }
27563             
27564         }
27565         
27566         return;
27567     },
27568     
27569     getScaleLevel : function()
27570     {
27571         return this.baseScale * Math.pow(1.1, this.scale);
27572     },
27573     
27574     onTouchStart : function(e)
27575     {
27576         if(!this.canvasLoaded){
27577             this.beforeSelectFile(e);
27578             return;
27579         }
27580         
27581         var touches = e.browserEvent.touches;
27582         
27583         if(!touches){
27584             return;
27585         }
27586         
27587         if(touches.length == 1){
27588             this.onMouseDown(e);
27589             return;
27590         }
27591         
27592         if(touches.length != 2){
27593             return;
27594         }
27595         
27596         var coords = [];
27597         
27598         for(var i = 0, finger; finger = touches[i]; i++){
27599             coords.push(finger.pageX, finger.pageY);
27600         }
27601         
27602         var x = Math.pow(coords[0] - coords[2], 2);
27603         var y = Math.pow(coords[1] - coords[3], 2);
27604         
27605         this.startDistance = Math.sqrt(x + y);
27606         
27607         this.startScale = this.scale;
27608         
27609         this.pinching = true;
27610         this.dragable = false;
27611         
27612     },
27613     
27614     onTouchMove : function(e)
27615     {
27616         if(!this.pinching && !this.dragable){
27617             return;
27618         }
27619         
27620         var touches = e.browserEvent.touches;
27621         
27622         if(!touches){
27623             return;
27624         }
27625         
27626         if(this.dragable){
27627             this.onMouseMove(e);
27628             return;
27629         }
27630         
27631         var coords = [];
27632         
27633         for(var i = 0, finger; finger = touches[i]; i++){
27634             coords.push(finger.pageX, finger.pageY);
27635         }
27636         
27637         var x = Math.pow(coords[0] - coords[2], 2);
27638         var y = Math.pow(coords[1] - coords[3], 2);
27639         
27640         this.endDistance = Math.sqrt(x + y);
27641         
27642         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27643         
27644         if(!this.zoomable()){
27645             this.scale = this.startScale;
27646             return;
27647         }
27648         
27649         this.draw();
27650         
27651     },
27652     
27653     onTouchEnd : function(e)
27654     {
27655         this.pinching = false;
27656         this.dragable = false;
27657         
27658     },
27659     
27660     process : function(file, crop)
27661     {
27662         if(this.loadMask){
27663             this.maskEl.mask(this.loadingText);
27664         }
27665         
27666         this.xhr = new XMLHttpRequest();
27667         
27668         file.xhr = this.xhr;
27669
27670         this.xhr.open(this.method, this.url, true);
27671         
27672         var headers = {
27673             "Accept": "application/json",
27674             "Cache-Control": "no-cache",
27675             "X-Requested-With": "XMLHttpRequest"
27676         };
27677         
27678         for (var headerName in headers) {
27679             var headerValue = headers[headerName];
27680             if (headerValue) {
27681                 this.xhr.setRequestHeader(headerName, headerValue);
27682             }
27683         }
27684         
27685         var _this = this;
27686         
27687         this.xhr.onload = function()
27688         {
27689             _this.xhrOnLoad(_this.xhr);
27690         }
27691         
27692         this.xhr.onerror = function()
27693         {
27694             _this.xhrOnError(_this.xhr);
27695         }
27696         
27697         var formData = new FormData();
27698
27699         formData.append('returnHTML', 'NO');
27700         
27701         if(crop){
27702             formData.append('crop', crop);
27703         }
27704         
27705         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27706             formData.append(this.paramName, file, file.name);
27707         }
27708         
27709         if(typeof(file.filename) != 'undefined'){
27710             formData.append('filename', file.filename);
27711         }
27712         
27713         if(typeof(file.mimetype) != 'undefined'){
27714             formData.append('mimetype', file.mimetype);
27715         }
27716         
27717         if(this.fireEvent('arrange', this, formData) != false){
27718             this.xhr.send(formData);
27719         };
27720     },
27721     
27722     xhrOnLoad : function(xhr)
27723     {
27724         if(this.loadMask){
27725             this.maskEl.unmask();
27726         }
27727         
27728         if (xhr.readyState !== 4) {
27729             this.fireEvent('exception', this, xhr);
27730             return;
27731         }
27732
27733         var response = Roo.decode(xhr.responseText);
27734         
27735         if(!response.success){
27736             this.fireEvent('exception', this, xhr);
27737             return;
27738         }
27739         
27740         var response = Roo.decode(xhr.responseText);
27741         
27742         this.fireEvent('upload', this, response);
27743         
27744     },
27745     
27746     xhrOnError : function()
27747     {
27748         if(this.loadMask){
27749             this.maskEl.unmask();
27750         }
27751         
27752         Roo.log('xhr on error');
27753         
27754         var response = Roo.decode(xhr.responseText);
27755           
27756         Roo.log(response);
27757         
27758     },
27759     
27760     prepare : function(file)
27761     {   
27762         if(this.loadMask){
27763             this.maskEl.mask(this.loadingText);
27764         }
27765         
27766         this.file = false;
27767         this.exif = {};
27768         
27769         if(typeof(file) === 'string'){
27770             this.loadCanvas(file);
27771             return;
27772         }
27773         
27774         if(!file || !this.urlAPI){
27775             return;
27776         }
27777         
27778         this.file = file;
27779         this.cropType = file.type;
27780         
27781         var _this = this;
27782         
27783         if(this.fireEvent('prepare', this, this.file) != false){
27784             
27785             var reader = new FileReader();
27786             
27787             reader.onload = function (e) {
27788                 if (e.target.error) {
27789                     Roo.log(e.target.error);
27790                     return;
27791                 }
27792                 
27793                 var buffer = e.target.result,
27794                     dataView = new DataView(buffer),
27795                     offset = 2,
27796                     maxOffset = dataView.byteLength - 4,
27797                     markerBytes,
27798                     markerLength;
27799                 
27800                 if (dataView.getUint16(0) === 0xffd8) {
27801                     while (offset < maxOffset) {
27802                         markerBytes = dataView.getUint16(offset);
27803                         
27804                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27805                             markerLength = dataView.getUint16(offset + 2) + 2;
27806                             if (offset + markerLength > dataView.byteLength) {
27807                                 Roo.log('Invalid meta data: Invalid segment size.');
27808                                 break;
27809                             }
27810                             
27811                             if(markerBytes == 0xffe1){
27812                                 _this.parseExifData(
27813                                     dataView,
27814                                     offset,
27815                                     markerLength
27816                                 );
27817                             }
27818                             
27819                             offset += markerLength;
27820                             
27821                             continue;
27822                         }
27823                         
27824                         break;
27825                     }
27826                     
27827                 }
27828                 
27829                 var url = _this.urlAPI.createObjectURL(_this.file);
27830                 
27831                 _this.loadCanvas(url);
27832                 
27833                 return;
27834             }
27835             
27836             reader.readAsArrayBuffer(this.file);
27837             
27838         }
27839         
27840     },
27841     
27842     parseExifData : function(dataView, offset, length)
27843     {
27844         var tiffOffset = offset + 10,
27845             littleEndian,
27846             dirOffset;
27847     
27848         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27849             // No Exif data, might be XMP data instead
27850             return;
27851         }
27852         
27853         // Check for the ASCII code for "Exif" (0x45786966):
27854         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27855             // No Exif data, might be XMP data instead
27856             return;
27857         }
27858         if (tiffOffset + 8 > dataView.byteLength) {
27859             Roo.log('Invalid Exif data: Invalid segment size.');
27860             return;
27861         }
27862         // Check for the two null bytes:
27863         if (dataView.getUint16(offset + 8) !== 0x0000) {
27864             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27865             return;
27866         }
27867         // Check the byte alignment:
27868         switch (dataView.getUint16(tiffOffset)) {
27869         case 0x4949:
27870             littleEndian = true;
27871             break;
27872         case 0x4D4D:
27873             littleEndian = false;
27874             break;
27875         default:
27876             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27877             return;
27878         }
27879         // Check for the TIFF tag marker (0x002A):
27880         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27881             Roo.log('Invalid Exif data: Missing TIFF marker.');
27882             return;
27883         }
27884         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27885         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27886         
27887         this.parseExifTags(
27888             dataView,
27889             tiffOffset,
27890             tiffOffset + dirOffset,
27891             littleEndian
27892         );
27893     },
27894     
27895     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27896     {
27897         var tagsNumber,
27898             dirEndOffset,
27899             i;
27900         if (dirOffset + 6 > dataView.byteLength) {
27901             Roo.log('Invalid Exif data: Invalid directory offset.');
27902             return;
27903         }
27904         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27905         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27906         if (dirEndOffset + 4 > dataView.byteLength) {
27907             Roo.log('Invalid Exif data: Invalid directory size.');
27908             return;
27909         }
27910         for (i = 0; i < tagsNumber; i += 1) {
27911             this.parseExifTag(
27912                 dataView,
27913                 tiffOffset,
27914                 dirOffset + 2 + 12 * i, // tag offset
27915                 littleEndian
27916             );
27917         }
27918         // Return the offset to the next directory:
27919         return dataView.getUint32(dirEndOffset, littleEndian);
27920     },
27921     
27922     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27923     {
27924         var tag = dataView.getUint16(offset, littleEndian);
27925         
27926         this.exif[tag] = this.getExifValue(
27927             dataView,
27928             tiffOffset,
27929             offset,
27930             dataView.getUint16(offset + 2, littleEndian), // tag type
27931             dataView.getUint32(offset + 4, littleEndian), // tag length
27932             littleEndian
27933         );
27934     },
27935     
27936     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27937     {
27938         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27939             tagSize,
27940             dataOffset,
27941             values,
27942             i,
27943             str,
27944             c;
27945     
27946         if (!tagType) {
27947             Roo.log('Invalid Exif data: Invalid tag type.');
27948             return;
27949         }
27950         
27951         tagSize = tagType.size * length;
27952         // Determine if the value is contained in the dataOffset bytes,
27953         // or if the value at the dataOffset is a pointer to the actual data:
27954         dataOffset = tagSize > 4 ?
27955                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27956         if (dataOffset + tagSize > dataView.byteLength) {
27957             Roo.log('Invalid Exif data: Invalid data offset.');
27958             return;
27959         }
27960         if (length === 1) {
27961             return tagType.getValue(dataView, dataOffset, littleEndian);
27962         }
27963         values = [];
27964         for (i = 0; i < length; i += 1) {
27965             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27966         }
27967         
27968         if (tagType.ascii) {
27969             str = '';
27970             // Concatenate the chars:
27971             for (i = 0; i < values.length; i += 1) {
27972                 c = values[i];
27973                 // Ignore the terminating NULL byte(s):
27974                 if (c === '\u0000') {
27975                     break;
27976                 }
27977                 str += c;
27978             }
27979             return str;
27980         }
27981         return values;
27982     }
27983     
27984 });
27985
27986 Roo.apply(Roo.bootstrap.UploadCropbox, {
27987     tags : {
27988         'Orientation': 0x0112
27989     },
27990     
27991     Orientation: {
27992             1: 0, //'top-left',
27993 //            2: 'top-right',
27994             3: 180, //'bottom-right',
27995 //            4: 'bottom-left',
27996 //            5: 'left-top',
27997             6: 90, //'right-top',
27998 //            7: 'right-bottom',
27999             8: 270 //'left-bottom'
28000     },
28001     
28002     exifTagTypes : {
28003         // byte, 8-bit unsigned int:
28004         1: {
28005             getValue: function (dataView, dataOffset) {
28006                 return dataView.getUint8(dataOffset);
28007             },
28008             size: 1
28009         },
28010         // ascii, 8-bit byte:
28011         2: {
28012             getValue: function (dataView, dataOffset) {
28013                 return String.fromCharCode(dataView.getUint8(dataOffset));
28014             },
28015             size: 1,
28016             ascii: true
28017         },
28018         // short, 16 bit int:
28019         3: {
28020             getValue: function (dataView, dataOffset, littleEndian) {
28021                 return dataView.getUint16(dataOffset, littleEndian);
28022             },
28023             size: 2
28024         },
28025         // long, 32 bit int:
28026         4: {
28027             getValue: function (dataView, dataOffset, littleEndian) {
28028                 return dataView.getUint32(dataOffset, littleEndian);
28029             },
28030             size: 4
28031         },
28032         // rational = two long values, first is numerator, second is denominator:
28033         5: {
28034             getValue: function (dataView, dataOffset, littleEndian) {
28035                 return dataView.getUint32(dataOffset, littleEndian) /
28036                     dataView.getUint32(dataOffset + 4, littleEndian);
28037             },
28038             size: 8
28039         },
28040         // slong, 32 bit signed int:
28041         9: {
28042             getValue: function (dataView, dataOffset, littleEndian) {
28043                 return dataView.getInt32(dataOffset, littleEndian);
28044             },
28045             size: 4
28046         },
28047         // srational, two slongs, first is numerator, second is denominator:
28048         10: {
28049             getValue: function (dataView, dataOffset, littleEndian) {
28050                 return dataView.getInt32(dataOffset, littleEndian) /
28051                     dataView.getInt32(dataOffset + 4, littleEndian);
28052             },
28053             size: 8
28054         }
28055     },
28056     
28057     footer : {
28058         STANDARD : [
28059             {
28060                 tag : 'div',
28061                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28062                 action : 'rotate-left',
28063                 cn : [
28064                     {
28065                         tag : 'button',
28066                         cls : 'btn btn-default',
28067                         html : '<i class="fa fa-undo"></i>'
28068                     }
28069                 ]
28070             },
28071             {
28072                 tag : 'div',
28073                 cls : 'btn-group roo-upload-cropbox-picture',
28074                 action : 'picture',
28075                 cn : [
28076                     {
28077                         tag : 'button',
28078                         cls : 'btn btn-default',
28079                         html : '<i class="fa fa-picture-o"></i>'
28080                     }
28081                 ]
28082             },
28083             {
28084                 tag : 'div',
28085                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28086                 action : 'rotate-right',
28087                 cn : [
28088                     {
28089                         tag : 'button',
28090                         cls : 'btn btn-default',
28091                         html : '<i class="fa fa-repeat"></i>'
28092                     }
28093                 ]
28094             }
28095         ],
28096         DOCUMENT : [
28097             {
28098                 tag : 'div',
28099                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28100                 action : 'rotate-left',
28101                 cn : [
28102                     {
28103                         tag : 'button',
28104                         cls : 'btn btn-default',
28105                         html : '<i class="fa fa-undo"></i>'
28106                     }
28107                 ]
28108             },
28109             {
28110                 tag : 'div',
28111                 cls : 'btn-group roo-upload-cropbox-download',
28112                 action : 'download',
28113                 cn : [
28114                     {
28115                         tag : 'button',
28116                         cls : 'btn btn-default',
28117                         html : '<i class="fa fa-download"></i>'
28118                     }
28119                 ]
28120             },
28121             {
28122                 tag : 'div',
28123                 cls : 'btn-group roo-upload-cropbox-crop',
28124                 action : 'crop',
28125                 cn : [
28126                     {
28127                         tag : 'button',
28128                         cls : 'btn btn-default',
28129                         html : '<i class="fa fa-crop"></i>'
28130                     }
28131                 ]
28132             },
28133             {
28134                 tag : 'div',
28135                 cls : 'btn-group roo-upload-cropbox-trash',
28136                 action : 'trash',
28137                 cn : [
28138                     {
28139                         tag : 'button',
28140                         cls : 'btn btn-default',
28141                         html : '<i class="fa fa-trash"></i>'
28142                     }
28143                 ]
28144             },
28145             {
28146                 tag : 'div',
28147                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28148                 action : 'rotate-right',
28149                 cn : [
28150                     {
28151                         tag : 'button',
28152                         cls : 'btn btn-default',
28153                         html : '<i class="fa fa-repeat"></i>'
28154                     }
28155                 ]
28156             }
28157         ],
28158         ROTATOR : [
28159             {
28160                 tag : 'div',
28161                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28162                 action : 'rotate-left',
28163                 cn : [
28164                     {
28165                         tag : 'button',
28166                         cls : 'btn btn-default',
28167                         html : '<i class="fa fa-undo"></i>'
28168                     }
28169                 ]
28170             },
28171             {
28172                 tag : 'div',
28173                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28174                 action : 'rotate-right',
28175                 cn : [
28176                     {
28177                         tag : 'button',
28178                         cls : 'btn btn-default',
28179                         html : '<i class="fa fa-repeat"></i>'
28180                     }
28181                 ]
28182             }
28183         ]
28184     }
28185 });
28186
28187 /*
28188 * Licence: LGPL
28189 */
28190
28191 /**
28192  * @class Roo.bootstrap.DocumentManager
28193  * @extends Roo.bootstrap.Component
28194  * Bootstrap DocumentManager class
28195  * @cfg {String} paramName default 'imageUpload'
28196  * @cfg {String} toolTipName default 'filename'
28197  * @cfg {String} method default POST
28198  * @cfg {String} url action url
28199  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28200  * @cfg {Boolean} multiple multiple upload default true
28201  * @cfg {Number} thumbSize default 300
28202  * @cfg {String} fieldLabel
28203  * @cfg {Number} labelWidth default 4
28204  * @cfg {String} labelAlign (left|top) default left
28205  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28206 * @cfg {Number} labellg set the width of label (1-12)
28207  * @cfg {Number} labelmd set the width of label (1-12)
28208  * @cfg {Number} labelsm set the width of label (1-12)
28209  * @cfg {Number} labelxs set the width of label (1-12)
28210  * 
28211  * @constructor
28212  * Create a new DocumentManager
28213  * @param {Object} config The config object
28214  */
28215
28216 Roo.bootstrap.DocumentManager = function(config){
28217     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28218     
28219     this.files = [];
28220     this.delegates = [];
28221     
28222     this.addEvents({
28223         /**
28224          * @event initial
28225          * Fire when initial the DocumentManager
28226          * @param {Roo.bootstrap.DocumentManager} this
28227          */
28228         "initial" : true,
28229         /**
28230          * @event inspect
28231          * inspect selected file
28232          * @param {Roo.bootstrap.DocumentManager} this
28233          * @param {File} file
28234          */
28235         "inspect" : true,
28236         /**
28237          * @event exception
28238          * Fire when xhr load exception
28239          * @param {Roo.bootstrap.DocumentManager} this
28240          * @param {XMLHttpRequest} xhr
28241          */
28242         "exception" : true,
28243         /**
28244          * @event afterupload
28245          * Fire when xhr load exception
28246          * @param {Roo.bootstrap.DocumentManager} this
28247          * @param {XMLHttpRequest} xhr
28248          */
28249         "afterupload" : true,
28250         /**
28251          * @event prepare
28252          * prepare the form data
28253          * @param {Roo.bootstrap.DocumentManager} this
28254          * @param {Object} formData
28255          */
28256         "prepare" : true,
28257         /**
28258          * @event remove
28259          * Fire when remove the file
28260          * @param {Roo.bootstrap.DocumentManager} this
28261          * @param {Object} file
28262          */
28263         "remove" : true,
28264         /**
28265          * @event refresh
28266          * Fire after refresh the file
28267          * @param {Roo.bootstrap.DocumentManager} this
28268          */
28269         "refresh" : true,
28270         /**
28271          * @event click
28272          * Fire after click the image
28273          * @param {Roo.bootstrap.DocumentManager} this
28274          * @param {Object} file
28275          */
28276         "click" : true,
28277         /**
28278          * @event edit
28279          * Fire when upload a image and editable set to true
28280          * @param {Roo.bootstrap.DocumentManager} this
28281          * @param {Object} file
28282          */
28283         "edit" : true,
28284         /**
28285          * @event beforeselectfile
28286          * Fire before select file
28287          * @param {Roo.bootstrap.DocumentManager} this
28288          */
28289         "beforeselectfile" : true,
28290         /**
28291          * @event process
28292          * Fire before process file
28293          * @param {Roo.bootstrap.DocumentManager} this
28294          * @param {Object} file
28295          */
28296         "process" : true
28297         
28298     });
28299 };
28300
28301 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28302     
28303     boxes : 0,
28304     inputName : '',
28305     thumbSize : 300,
28306     multiple : true,
28307     files : false,
28308     method : 'POST',
28309     url : '',
28310     paramName : 'imageUpload',
28311     toolTipName : 'filename',
28312     fieldLabel : '',
28313     labelWidth : 4,
28314     labelAlign : 'left',
28315     editable : true,
28316     delegates : false,
28317     xhr : false, 
28318     
28319     labellg : 0,
28320     labelmd : 0,
28321     labelsm : 0,
28322     labelxs : 0,
28323     
28324     getAutoCreate : function()
28325     {   
28326         var managerWidget = {
28327             tag : 'div',
28328             cls : 'roo-document-manager',
28329             cn : [
28330                 {
28331                     tag : 'input',
28332                     cls : 'roo-document-manager-selector',
28333                     type : 'file'
28334                 },
28335                 {
28336                     tag : 'div',
28337                     cls : 'roo-document-manager-uploader',
28338                     cn : [
28339                         {
28340                             tag : 'div',
28341                             cls : 'roo-document-manager-upload-btn',
28342                             html : '<i class="fa fa-plus"></i>'
28343                         }
28344                     ]
28345                     
28346                 }
28347             ]
28348         };
28349         
28350         var content = [
28351             {
28352                 tag : 'div',
28353                 cls : 'column col-md-12',
28354                 cn : managerWidget
28355             }
28356         ];
28357         
28358         if(this.fieldLabel.length){
28359             
28360             content = [
28361                 {
28362                     tag : 'div',
28363                     cls : 'column col-md-12',
28364                     html : this.fieldLabel
28365                 },
28366                 {
28367                     tag : 'div',
28368                     cls : 'column col-md-12',
28369                     cn : managerWidget
28370                 }
28371             ];
28372
28373             if(this.labelAlign == 'left'){
28374                 content = [
28375                     {
28376                         tag : 'div',
28377                         cls : 'column',
28378                         html : this.fieldLabel
28379                     },
28380                     {
28381                         tag : 'div',
28382                         cls : 'column',
28383                         cn : managerWidget
28384                     }
28385                 ];
28386                 
28387                 if(this.labelWidth > 12){
28388                     content[0].style = "width: " + this.labelWidth + 'px';
28389                 }
28390
28391                 if(this.labelWidth < 13 && this.labelmd == 0){
28392                     this.labelmd = this.labelWidth;
28393                 }
28394
28395                 if(this.labellg > 0){
28396                     content[0].cls += ' col-lg-' + this.labellg;
28397                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28398                 }
28399
28400                 if(this.labelmd > 0){
28401                     content[0].cls += ' col-md-' + this.labelmd;
28402                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28403                 }
28404
28405                 if(this.labelsm > 0){
28406                     content[0].cls += ' col-sm-' + this.labelsm;
28407                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28408                 }
28409
28410                 if(this.labelxs > 0){
28411                     content[0].cls += ' col-xs-' + this.labelxs;
28412                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28413                 }
28414                 
28415             }
28416         }
28417         
28418         var cfg = {
28419             tag : 'div',
28420             cls : 'row clearfix',
28421             cn : content
28422         };
28423         
28424         return cfg;
28425         
28426     },
28427     
28428     initEvents : function()
28429     {
28430         this.managerEl = this.el.select('.roo-document-manager', true).first();
28431         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28432         
28433         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28434         this.selectorEl.hide();
28435         
28436         if(this.multiple){
28437             this.selectorEl.attr('multiple', 'multiple');
28438         }
28439         
28440         this.selectorEl.on('change', this.onFileSelected, this);
28441         
28442         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28443         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28444         
28445         this.uploader.on('click', this.onUploaderClick, this);
28446         
28447         this.renderProgressDialog();
28448         
28449         var _this = this;
28450         
28451         window.addEventListener("resize", function() { _this.refresh(); } );
28452         
28453         this.fireEvent('initial', this);
28454     },
28455     
28456     renderProgressDialog : function()
28457     {
28458         var _this = this;
28459         
28460         this.progressDialog = new Roo.bootstrap.Modal({
28461             cls : 'roo-document-manager-progress-dialog',
28462             allow_close : false,
28463             title : '',
28464             buttons : [
28465                 {
28466                     name  :'cancel',
28467                     weight : 'danger',
28468                     html : 'Cancel'
28469                 }
28470             ], 
28471             listeners : { 
28472                 btnclick : function() {
28473                     _this.uploadCancel();
28474                     this.hide();
28475                 }
28476             }
28477         });
28478          
28479         this.progressDialog.render(Roo.get(document.body));
28480          
28481         this.progress = new Roo.bootstrap.Progress({
28482             cls : 'roo-document-manager-progress',
28483             active : true,
28484             striped : true
28485         });
28486         
28487         this.progress.render(this.progressDialog.getChildContainer());
28488         
28489         this.progressBar = new Roo.bootstrap.ProgressBar({
28490             cls : 'roo-document-manager-progress-bar',
28491             aria_valuenow : 0,
28492             aria_valuemin : 0,
28493             aria_valuemax : 12,
28494             panel : 'success'
28495         });
28496         
28497         this.progressBar.render(this.progress.getChildContainer());
28498     },
28499     
28500     onUploaderClick : function(e)
28501     {
28502         e.preventDefault();
28503      
28504         if(this.fireEvent('beforeselectfile', this) != false){
28505             this.selectorEl.dom.click();
28506         }
28507         
28508     },
28509     
28510     onFileSelected : function(e)
28511     {
28512         e.preventDefault();
28513         
28514         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28515             return;
28516         }
28517         
28518         Roo.each(this.selectorEl.dom.files, function(file){
28519             if(this.fireEvent('inspect', this, file) != false){
28520                 this.files.push(file);
28521             }
28522         }, this);
28523         
28524         this.queue();
28525         
28526     },
28527     
28528     queue : function()
28529     {
28530         this.selectorEl.dom.value = '';
28531         
28532         if(!this.files.length){
28533             return;
28534         }
28535         
28536         if(this.boxes > 0 && this.files.length > this.boxes){
28537             this.files = this.files.slice(0, this.boxes);
28538         }
28539         
28540         this.uploader.show();
28541         
28542         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28543             this.uploader.hide();
28544         }
28545         
28546         var _this = this;
28547         
28548         var files = [];
28549         
28550         var docs = [];
28551         
28552         Roo.each(this.files, function(file){
28553             
28554             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28555                 var f = this.renderPreview(file);
28556                 files.push(f);
28557                 return;
28558             }
28559             
28560             if(file.type.indexOf('image') != -1){
28561                 this.delegates.push(
28562                     (function(){
28563                         _this.process(file);
28564                     }).createDelegate(this)
28565                 );
28566         
28567                 return;
28568             }
28569             
28570             docs.push(
28571                 (function(){
28572                     _this.process(file);
28573                 }).createDelegate(this)
28574             );
28575             
28576         }, this);
28577         
28578         this.files = files;
28579         
28580         this.delegates = this.delegates.concat(docs);
28581         
28582         if(!this.delegates.length){
28583             this.refresh();
28584             return;
28585         }
28586         
28587         this.progressBar.aria_valuemax = this.delegates.length;
28588         
28589         this.arrange();
28590         
28591         return;
28592     },
28593     
28594     arrange : function()
28595     {
28596         if(!this.delegates.length){
28597             this.progressDialog.hide();
28598             this.refresh();
28599             return;
28600         }
28601         
28602         var delegate = this.delegates.shift();
28603         
28604         this.progressDialog.show();
28605         
28606         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28607         
28608         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28609         
28610         delegate();
28611     },
28612     
28613     refresh : function()
28614     {
28615         this.uploader.show();
28616         
28617         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28618             this.uploader.hide();
28619         }
28620         
28621         Roo.isTouch ? this.closable(false) : this.closable(true);
28622         
28623         this.fireEvent('refresh', this);
28624     },
28625     
28626     onRemove : function(e, el, o)
28627     {
28628         e.preventDefault();
28629         
28630         this.fireEvent('remove', this, o);
28631         
28632     },
28633     
28634     remove : function(o)
28635     {
28636         var files = [];
28637         
28638         Roo.each(this.files, function(file){
28639             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28640                 files.push(file);
28641                 return;
28642             }
28643
28644             o.target.remove();
28645
28646         }, this);
28647         
28648         this.files = files;
28649         
28650         this.refresh();
28651     },
28652     
28653     clear : function()
28654     {
28655         Roo.each(this.files, function(file){
28656             if(!file.target){
28657                 return;
28658             }
28659             
28660             file.target.remove();
28661
28662         }, this);
28663         
28664         this.files = [];
28665         
28666         this.refresh();
28667     },
28668     
28669     onClick : function(e, el, o)
28670     {
28671         e.preventDefault();
28672         
28673         this.fireEvent('click', this, o);
28674         
28675     },
28676     
28677     closable : function(closable)
28678     {
28679         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28680             
28681             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28682             
28683             if(closable){
28684                 el.show();
28685                 return;
28686             }
28687             
28688             el.hide();
28689             
28690         }, this);
28691     },
28692     
28693     xhrOnLoad : function(xhr)
28694     {
28695         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28696             el.remove();
28697         }, this);
28698         
28699         if (xhr.readyState !== 4) {
28700             this.arrange();
28701             this.fireEvent('exception', this, xhr);
28702             return;
28703         }
28704
28705         var response = Roo.decode(xhr.responseText);
28706         
28707         if(!response.success){
28708             this.arrange();
28709             this.fireEvent('exception', this, xhr);
28710             return;
28711         }
28712         
28713         var file = this.renderPreview(response.data);
28714         
28715         this.files.push(file);
28716         
28717         this.arrange();
28718         
28719         this.fireEvent('afterupload', this, xhr);
28720         
28721     },
28722     
28723     xhrOnError : function(xhr)
28724     {
28725         Roo.log('xhr on error');
28726         
28727         var response = Roo.decode(xhr.responseText);
28728           
28729         Roo.log(response);
28730         
28731         this.arrange();
28732     },
28733     
28734     process : function(file)
28735     {
28736         if(this.fireEvent('process', this, file) !== false){
28737             if(this.editable && file.type.indexOf('image') != -1){
28738                 this.fireEvent('edit', this, file);
28739                 return;
28740             }
28741
28742             this.uploadStart(file, false);
28743
28744             return;
28745         }
28746         
28747     },
28748     
28749     uploadStart : function(file, crop)
28750     {
28751         this.xhr = new XMLHttpRequest();
28752         
28753         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28754             this.arrange();
28755             return;
28756         }
28757         
28758         file.xhr = this.xhr;
28759             
28760         this.managerEl.createChild({
28761             tag : 'div',
28762             cls : 'roo-document-manager-loading',
28763             cn : [
28764                 {
28765                     tag : 'div',
28766                     tooltip : file.name,
28767                     cls : 'roo-document-manager-thumb',
28768                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28769                 }
28770             ]
28771
28772         });
28773
28774         this.xhr.open(this.method, this.url, true);
28775         
28776         var headers = {
28777             "Accept": "application/json",
28778             "Cache-Control": "no-cache",
28779             "X-Requested-With": "XMLHttpRequest"
28780         };
28781         
28782         for (var headerName in headers) {
28783             var headerValue = headers[headerName];
28784             if (headerValue) {
28785                 this.xhr.setRequestHeader(headerName, headerValue);
28786             }
28787         }
28788         
28789         var _this = this;
28790         
28791         this.xhr.onload = function()
28792         {
28793             _this.xhrOnLoad(_this.xhr);
28794         }
28795         
28796         this.xhr.onerror = function()
28797         {
28798             _this.xhrOnError(_this.xhr);
28799         }
28800         
28801         var formData = new FormData();
28802
28803         formData.append('returnHTML', 'NO');
28804         
28805         if(crop){
28806             formData.append('crop', crop);
28807         }
28808         
28809         formData.append(this.paramName, file, file.name);
28810         
28811         var options = {
28812             file : file, 
28813             manually : false
28814         };
28815         
28816         if(this.fireEvent('prepare', this, formData, options) != false){
28817             
28818             if(options.manually){
28819                 return;
28820             }
28821             
28822             this.xhr.send(formData);
28823             return;
28824         };
28825         
28826         this.uploadCancel();
28827     },
28828     
28829     uploadCancel : function()
28830     {
28831         if (this.xhr) {
28832             this.xhr.abort();
28833         }
28834         
28835         this.delegates = [];
28836         
28837         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28838             el.remove();
28839         }, this);
28840         
28841         this.arrange();
28842     },
28843     
28844     renderPreview : function(file)
28845     {
28846         if(typeof(file.target) != 'undefined' && file.target){
28847             return file;
28848         }
28849         
28850         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28851         
28852         var previewEl = this.managerEl.createChild({
28853             tag : 'div',
28854             cls : 'roo-document-manager-preview',
28855             cn : [
28856                 {
28857                     tag : 'div',
28858                     tooltip : file[this.toolTipName],
28859                     cls : 'roo-document-manager-thumb',
28860                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28861                 },
28862                 {
28863                     tag : 'button',
28864                     cls : 'close',
28865                     html : '<i class="fa fa-times-circle"></i>'
28866                 }
28867             ]
28868         });
28869
28870         var close = previewEl.select('button.close', true).first();
28871
28872         close.on('click', this.onRemove, this, file);
28873
28874         file.target = previewEl;
28875
28876         var image = previewEl.select('img', true).first();
28877         
28878         var _this = this;
28879         
28880         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28881         
28882         image.on('click', this.onClick, this, file);
28883         
28884         return file;
28885         
28886     },
28887     
28888     onPreviewLoad : function(file, image)
28889     {
28890         if(typeof(file.target) == 'undefined' || !file.target){
28891             return;
28892         }
28893         
28894         var width = image.dom.naturalWidth || image.dom.width;
28895         var height = image.dom.naturalHeight || image.dom.height;
28896         
28897         if(width > height){
28898             file.target.addClass('wide');
28899             return;
28900         }
28901         
28902         file.target.addClass('tall');
28903         return;
28904         
28905     },
28906     
28907     uploadFromSource : function(file, crop)
28908     {
28909         this.xhr = new XMLHttpRequest();
28910         
28911         this.managerEl.createChild({
28912             tag : 'div',
28913             cls : 'roo-document-manager-loading',
28914             cn : [
28915                 {
28916                     tag : 'div',
28917                     tooltip : file.name,
28918                     cls : 'roo-document-manager-thumb',
28919                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28920                 }
28921             ]
28922
28923         });
28924
28925         this.xhr.open(this.method, this.url, true);
28926         
28927         var headers = {
28928             "Accept": "application/json",
28929             "Cache-Control": "no-cache",
28930             "X-Requested-With": "XMLHttpRequest"
28931         };
28932         
28933         for (var headerName in headers) {
28934             var headerValue = headers[headerName];
28935             if (headerValue) {
28936                 this.xhr.setRequestHeader(headerName, headerValue);
28937             }
28938         }
28939         
28940         var _this = this;
28941         
28942         this.xhr.onload = function()
28943         {
28944             _this.xhrOnLoad(_this.xhr);
28945         }
28946         
28947         this.xhr.onerror = function()
28948         {
28949             _this.xhrOnError(_this.xhr);
28950         }
28951         
28952         var formData = new FormData();
28953
28954         formData.append('returnHTML', 'NO');
28955         
28956         formData.append('crop', crop);
28957         
28958         if(typeof(file.filename) != 'undefined'){
28959             formData.append('filename', file.filename);
28960         }
28961         
28962         if(typeof(file.mimetype) != 'undefined'){
28963             formData.append('mimetype', file.mimetype);
28964         }
28965         
28966         Roo.log(formData);
28967         
28968         if(this.fireEvent('prepare', this, formData) != false){
28969             this.xhr.send(formData);
28970         };
28971     }
28972 });
28973
28974 /*
28975 * Licence: LGPL
28976 */
28977
28978 /**
28979  * @class Roo.bootstrap.DocumentViewer
28980  * @extends Roo.bootstrap.Component
28981  * Bootstrap DocumentViewer class
28982  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28983  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28984  * 
28985  * @constructor
28986  * Create a new DocumentViewer
28987  * @param {Object} config The config object
28988  */
28989
28990 Roo.bootstrap.DocumentViewer = function(config){
28991     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28992     
28993     this.addEvents({
28994         /**
28995          * @event initial
28996          * Fire after initEvent
28997          * @param {Roo.bootstrap.DocumentViewer} this
28998          */
28999         "initial" : true,
29000         /**
29001          * @event click
29002          * Fire after click
29003          * @param {Roo.bootstrap.DocumentViewer} this
29004          */
29005         "click" : true,
29006         /**
29007          * @event download
29008          * Fire after download button
29009          * @param {Roo.bootstrap.DocumentViewer} this
29010          */
29011         "download" : true,
29012         /**
29013          * @event trash
29014          * Fire after trash button
29015          * @param {Roo.bootstrap.DocumentViewer} this
29016          */
29017         "trash" : true
29018         
29019     });
29020 };
29021
29022 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29023     
29024     showDownload : true,
29025     
29026     showTrash : true,
29027     
29028     getAutoCreate : function()
29029     {
29030         var cfg = {
29031             tag : 'div',
29032             cls : 'roo-document-viewer',
29033             cn : [
29034                 {
29035                     tag : 'div',
29036                     cls : 'roo-document-viewer-body',
29037                     cn : [
29038                         {
29039                             tag : 'div',
29040                             cls : 'roo-document-viewer-thumb',
29041                             cn : [
29042                                 {
29043                                     tag : 'img',
29044                                     cls : 'roo-document-viewer-image'
29045                                 }
29046                             ]
29047                         }
29048                     ]
29049                 },
29050                 {
29051                     tag : 'div',
29052                     cls : 'roo-document-viewer-footer',
29053                     cn : {
29054                         tag : 'div',
29055                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29056                         cn : [
29057                             {
29058                                 tag : 'div',
29059                                 cls : 'btn-group roo-document-viewer-download',
29060                                 cn : [
29061                                     {
29062                                         tag : 'button',
29063                                         cls : 'btn btn-default',
29064                                         html : '<i class="fa fa-download"></i>'
29065                                     }
29066                                 ]
29067                             },
29068                             {
29069                                 tag : 'div',
29070                                 cls : 'btn-group roo-document-viewer-trash',
29071                                 cn : [
29072                                     {
29073                                         tag : 'button',
29074                                         cls : 'btn btn-default',
29075                                         html : '<i class="fa fa-trash"></i>'
29076                                     }
29077                                 ]
29078                             }
29079                         ]
29080                     }
29081                 }
29082             ]
29083         };
29084         
29085         return cfg;
29086     },
29087     
29088     initEvents : function()
29089     {
29090         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29091         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29092         
29093         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29094         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29095         
29096         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29097         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29098         
29099         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29100         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29101         
29102         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29103         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29104         
29105         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29106         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29107         
29108         this.bodyEl.on('click', this.onClick, this);
29109         this.downloadBtn.on('click', this.onDownload, this);
29110         this.trashBtn.on('click', this.onTrash, this);
29111         
29112         this.downloadBtn.hide();
29113         this.trashBtn.hide();
29114         
29115         if(this.showDownload){
29116             this.downloadBtn.show();
29117         }
29118         
29119         if(this.showTrash){
29120             this.trashBtn.show();
29121         }
29122         
29123         if(!this.showDownload && !this.showTrash) {
29124             this.footerEl.hide();
29125         }
29126         
29127     },
29128     
29129     initial : function()
29130     {
29131         this.fireEvent('initial', this);
29132         
29133     },
29134     
29135     onClick : function(e)
29136     {
29137         e.preventDefault();
29138         
29139         this.fireEvent('click', this);
29140     },
29141     
29142     onDownload : function(e)
29143     {
29144         e.preventDefault();
29145         
29146         this.fireEvent('download', this);
29147     },
29148     
29149     onTrash : function(e)
29150     {
29151         e.preventDefault();
29152         
29153         this.fireEvent('trash', this);
29154     }
29155     
29156 });
29157 /*
29158  * - LGPL
29159  *
29160  * nav progress bar
29161  * 
29162  */
29163
29164 /**
29165  * @class Roo.bootstrap.NavProgressBar
29166  * @extends Roo.bootstrap.Component
29167  * Bootstrap NavProgressBar class
29168  * 
29169  * @constructor
29170  * Create a new nav progress bar
29171  * @param {Object} config The config object
29172  */
29173
29174 Roo.bootstrap.NavProgressBar = function(config){
29175     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29176
29177     this.bullets = this.bullets || [];
29178    
29179 //    Roo.bootstrap.NavProgressBar.register(this);
29180      this.addEvents({
29181         /**
29182              * @event changed
29183              * Fires when the active item changes
29184              * @param {Roo.bootstrap.NavProgressBar} this
29185              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29186              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29187          */
29188         'changed': true
29189      });
29190     
29191 };
29192
29193 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29194     
29195     bullets : [],
29196     barItems : [],
29197     
29198     getAutoCreate : function()
29199     {
29200         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29201         
29202         cfg = {
29203             tag : 'div',
29204             cls : 'roo-navigation-bar-group',
29205             cn : [
29206                 {
29207                     tag : 'div',
29208                     cls : 'roo-navigation-top-bar'
29209                 },
29210                 {
29211                     tag : 'div',
29212                     cls : 'roo-navigation-bullets-bar',
29213                     cn : [
29214                         {
29215                             tag : 'ul',
29216                             cls : 'roo-navigation-bar'
29217                         }
29218                     ]
29219                 },
29220                 
29221                 {
29222                     tag : 'div',
29223                     cls : 'roo-navigation-bottom-bar'
29224                 }
29225             ]
29226             
29227         };
29228         
29229         return cfg;
29230         
29231     },
29232     
29233     initEvents: function() 
29234     {
29235         
29236     },
29237     
29238     onRender : function(ct, position) 
29239     {
29240         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29241         
29242         if(this.bullets.length){
29243             Roo.each(this.bullets, function(b){
29244                this.addItem(b);
29245             }, this);
29246         }
29247         
29248         this.format();
29249         
29250     },
29251     
29252     addItem : function(cfg)
29253     {
29254         var item = new Roo.bootstrap.NavProgressItem(cfg);
29255         
29256         item.parentId = this.id;
29257         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29258         
29259         if(cfg.html){
29260             var top = new Roo.bootstrap.Element({
29261                 tag : 'div',
29262                 cls : 'roo-navigation-bar-text'
29263             });
29264             
29265             var bottom = new Roo.bootstrap.Element({
29266                 tag : 'div',
29267                 cls : 'roo-navigation-bar-text'
29268             });
29269             
29270             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29271             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29272             
29273             var topText = new Roo.bootstrap.Element({
29274                 tag : 'span',
29275                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29276             });
29277             
29278             var bottomText = new Roo.bootstrap.Element({
29279                 tag : 'span',
29280                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29281             });
29282             
29283             topText.onRender(top.el, null);
29284             bottomText.onRender(bottom.el, null);
29285             
29286             item.topEl = top;
29287             item.bottomEl = bottom;
29288         }
29289         
29290         this.barItems.push(item);
29291         
29292         return item;
29293     },
29294     
29295     getActive : function()
29296     {
29297         var active = false;
29298         
29299         Roo.each(this.barItems, function(v){
29300             
29301             if (!v.isActive()) {
29302                 return;
29303             }
29304             
29305             active = v;
29306             return false;
29307             
29308         });
29309         
29310         return active;
29311     },
29312     
29313     setActiveItem : function(item)
29314     {
29315         var prev = false;
29316         
29317         Roo.each(this.barItems, function(v){
29318             if (v.rid == item.rid) {
29319                 return ;
29320             }
29321             
29322             if (v.isActive()) {
29323                 v.setActive(false);
29324                 prev = v;
29325             }
29326         });
29327
29328         item.setActive(true);
29329         
29330         this.fireEvent('changed', this, item, prev);
29331     },
29332     
29333     getBarItem: function(rid)
29334     {
29335         var ret = false;
29336         
29337         Roo.each(this.barItems, function(e) {
29338             if (e.rid != rid) {
29339                 return;
29340             }
29341             
29342             ret =  e;
29343             return false;
29344         });
29345         
29346         return ret;
29347     },
29348     
29349     indexOfItem : function(item)
29350     {
29351         var index = false;
29352         
29353         Roo.each(this.barItems, function(v, i){
29354             
29355             if (v.rid != item.rid) {
29356                 return;
29357             }
29358             
29359             index = i;
29360             return false
29361         });
29362         
29363         return index;
29364     },
29365     
29366     setActiveNext : function()
29367     {
29368         var i = this.indexOfItem(this.getActive());
29369         
29370         if (i > this.barItems.length) {
29371             return;
29372         }
29373         
29374         this.setActiveItem(this.barItems[i+1]);
29375     },
29376     
29377     setActivePrev : function()
29378     {
29379         var i = this.indexOfItem(this.getActive());
29380         
29381         if (i  < 1) {
29382             return;
29383         }
29384         
29385         this.setActiveItem(this.barItems[i-1]);
29386     },
29387     
29388     format : function()
29389     {
29390         if(!this.barItems.length){
29391             return;
29392         }
29393      
29394         var width = 100 / this.barItems.length;
29395         
29396         Roo.each(this.barItems, function(i){
29397             i.el.setStyle('width', width + '%');
29398             i.topEl.el.setStyle('width', width + '%');
29399             i.bottomEl.el.setStyle('width', width + '%');
29400         }, this);
29401         
29402     }
29403     
29404 });
29405 /*
29406  * - LGPL
29407  *
29408  * Nav Progress Item
29409  * 
29410  */
29411
29412 /**
29413  * @class Roo.bootstrap.NavProgressItem
29414  * @extends Roo.bootstrap.Component
29415  * Bootstrap NavProgressItem class
29416  * @cfg {String} rid the reference id
29417  * @cfg {Boolean} active (true|false) Is item active default false
29418  * @cfg {Boolean} disabled (true|false) Is item active default false
29419  * @cfg {String} html
29420  * @cfg {String} position (top|bottom) text position default bottom
29421  * @cfg {String} icon show icon instead of number
29422  * 
29423  * @constructor
29424  * Create a new NavProgressItem
29425  * @param {Object} config The config object
29426  */
29427 Roo.bootstrap.NavProgressItem = function(config){
29428     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29429     this.addEvents({
29430         // raw events
29431         /**
29432          * @event click
29433          * The raw click event for the entire grid.
29434          * @param {Roo.bootstrap.NavProgressItem} this
29435          * @param {Roo.EventObject} e
29436          */
29437         "click" : true
29438     });
29439    
29440 };
29441
29442 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29443     
29444     rid : '',
29445     active : false,
29446     disabled : false,
29447     html : '',
29448     position : 'bottom',
29449     icon : false,
29450     
29451     getAutoCreate : function()
29452     {
29453         var iconCls = 'roo-navigation-bar-item-icon';
29454         
29455         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29456         
29457         var cfg = {
29458             tag: 'li',
29459             cls: 'roo-navigation-bar-item',
29460             cn : [
29461                 {
29462                     tag : 'i',
29463                     cls : iconCls
29464                 }
29465             ]
29466         };
29467         
29468         if(this.active){
29469             cfg.cls += ' active';
29470         }
29471         if(this.disabled){
29472             cfg.cls += ' disabled';
29473         }
29474         
29475         return cfg;
29476     },
29477     
29478     disable : function()
29479     {
29480         this.setDisabled(true);
29481     },
29482     
29483     enable : function()
29484     {
29485         this.setDisabled(false);
29486     },
29487     
29488     initEvents: function() 
29489     {
29490         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29491         
29492         this.iconEl.on('click', this.onClick, this);
29493     },
29494     
29495     onClick : function(e)
29496     {
29497         e.preventDefault();
29498         
29499         if(this.disabled){
29500             return;
29501         }
29502         
29503         if(this.fireEvent('click', this, e) === false){
29504             return;
29505         };
29506         
29507         this.parent().setActiveItem(this);
29508     },
29509     
29510     isActive: function () 
29511     {
29512         return this.active;
29513     },
29514     
29515     setActive : function(state)
29516     {
29517         if(this.active == state){
29518             return;
29519         }
29520         
29521         this.active = state;
29522         
29523         if (state) {
29524             this.el.addClass('active');
29525             return;
29526         }
29527         
29528         this.el.removeClass('active');
29529         
29530         return;
29531     },
29532     
29533     setDisabled : function(state)
29534     {
29535         if(this.disabled == state){
29536             return;
29537         }
29538         
29539         this.disabled = state;
29540         
29541         if (state) {
29542             this.el.addClass('disabled');
29543             return;
29544         }
29545         
29546         this.el.removeClass('disabled');
29547     },
29548     
29549     tooltipEl : function()
29550     {
29551         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29552     }
29553 });
29554  
29555
29556  /*
29557  * - LGPL
29558  *
29559  * FieldLabel
29560  * 
29561  */
29562
29563 /**
29564  * @class Roo.bootstrap.FieldLabel
29565  * @extends Roo.bootstrap.Component
29566  * Bootstrap FieldLabel class
29567  * @cfg {String} html contents of the element
29568  * @cfg {String} tag tag of the element default label
29569  * @cfg {String} cls class of the element
29570  * @cfg {String} target label target 
29571  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29572  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29573  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29574  * @cfg {String} iconTooltip default "This field is required"
29575  * 
29576  * @constructor
29577  * Create a new FieldLabel
29578  * @param {Object} config The config object
29579  */
29580
29581 Roo.bootstrap.FieldLabel = function(config){
29582     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29583     
29584     this.addEvents({
29585             /**
29586              * @event invalid
29587              * Fires after the field has been marked as invalid.
29588              * @param {Roo.form.FieldLabel} this
29589              * @param {String} msg The validation message
29590              */
29591             invalid : true,
29592             /**
29593              * @event valid
29594              * Fires after the field has been validated with no errors.
29595              * @param {Roo.form.FieldLabel} this
29596              */
29597             valid : true
29598         });
29599 };
29600
29601 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29602     
29603     tag: 'label',
29604     cls: '',
29605     html: '',
29606     target: '',
29607     allowBlank : true,
29608     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29609     validClass : 'text-success fa fa-lg fa-check',
29610     iconTooltip : 'This field is required',
29611     
29612     getAutoCreate : function(){
29613         
29614         var cfg = {
29615             tag : this.tag,
29616             cls : 'roo-bootstrap-field-label ' + this.cls,
29617             for : this.target,
29618             cn : [
29619                 {
29620                     tag : 'i',
29621                     cls : '',
29622                     tooltip : this.iconTooltip
29623                 },
29624                 {
29625                     tag : 'span',
29626                     html : this.html
29627                 }
29628             ] 
29629         };
29630         
29631         return cfg;
29632     },
29633     
29634     initEvents: function() 
29635     {
29636         Roo.bootstrap.Element.superclass.initEvents.call(this);
29637         
29638         this.iconEl = this.el.select('i', true).first();
29639         
29640         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29641         
29642         Roo.bootstrap.FieldLabel.register(this);
29643     },
29644     
29645     /**
29646      * Mark this field as valid
29647      */
29648     markValid : function()
29649     {
29650         this.iconEl.show();
29651         
29652         this.iconEl.removeClass(this.invalidClass);
29653         
29654         this.iconEl.addClass(this.validClass);
29655         
29656         this.fireEvent('valid', this);
29657     },
29658     
29659     /**
29660      * Mark this field as invalid
29661      * @param {String} msg The validation message
29662      */
29663     markInvalid : function(msg)
29664     {
29665         this.iconEl.show();
29666         
29667         this.iconEl.removeClass(this.validClass);
29668         
29669         this.iconEl.addClass(this.invalidClass);
29670         
29671         this.fireEvent('invalid', this, msg);
29672     }
29673     
29674    
29675 });
29676
29677 Roo.apply(Roo.bootstrap.FieldLabel, {
29678     
29679     groups: {},
29680     
29681      /**
29682     * register a FieldLabel Group
29683     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29684     */
29685     register : function(label)
29686     {
29687         if(this.groups.hasOwnProperty(label.target)){
29688             return;
29689         }
29690      
29691         this.groups[label.target] = label;
29692         
29693     },
29694     /**
29695     * fetch a FieldLabel Group based on the target
29696     * @param {string} target
29697     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29698     */
29699     get: function(target) {
29700         if (typeof(this.groups[target]) == 'undefined') {
29701             return false;
29702         }
29703         
29704         return this.groups[target] ;
29705     }
29706 });
29707
29708  
29709
29710  /*
29711  * - LGPL
29712  *
29713  * page DateSplitField.
29714  * 
29715  */
29716
29717
29718 /**
29719  * @class Roo.bootstrap.DateSplitField
29720  * @extends Roo.bootstrap.Component
29721  * Bootstrap DateSplitField class
29722  * @cfg {string} fieldLabel - the label associated
29723  * @cfg {Number} labelWidth set the width of label (0-12)
29724  * @cfg {String} labelAlign (top|left)
29725  * @cfg {Boolean} dayAllowBlank (true|false) default false
29726  * @cfg {Boolean} monthAllowBlank (true|false) default false
29727  * @cfg {Boolean} yearAllowBlank (true|false) default false
29728  * @cfg {string} dayPlaceholder 
29729  * @cfg {string} monthPlaceholder
29730  * @cfg {string} yearPlaceholder
29731  * @cfg {string} dayFormat default 'd'
29732  * @cfg {string} monthFormat default 'm'
29733  * @cfg {string} yearFormat default 'Y'
29734  * @cfg {Number} labellg set the width of label (1-12)
29735  * @cfg {Number} labelmd set the width of label (1-12)
29736  * @cfg {Number} labelsm set the width of label (1-12)
29737  * @cfg {Number} labelxs set the width of label (1-12)
29738
29739  *     
29740  * @constructor
29741  * Create a new DateSplitField
29742  * @param {Object} config The config object
29743  */
29744
29745 Roo.bootstrap.DateSplitField = function(config){
29746     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29747     
29748     this.addEvents({
29749         // raw events
29750          /**
29751          * @event years
29752          * getting the data of years
29753          * @param {Roo.bootstrap.DateSplitField} this
29754          * @param {Object} years
29755          */
29756         "years" : true,
29757         /**
29758          * @event days
29759          * getting the data of days
29760          * @param {Roo.bootstrap.DateSplitField} this
29761          * @param {Object} days
29762          */
29763         "days" : true,
29764         /**
29765          * @event invalid
29766          * Fires after the field has been marked as invalid.
29767          * @param {Roo.form.Field} this
29768          * @param {String} msg The validation message
29769          */
29770         invalid : true,
29771        /**
29772          * @event valid
29773          * Fires after the field has been validated with no errors.
29774          * @param {Roo.form.Field} this
29775          */
29776         valid : true
29777     });
29778 };
29779
29780 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29781     
29782     fieldLabel : '',
29783     labelAlign : 'top',
29784     labelWidth : 3,
29785     dayAllowBlank : false,
29786     monthAllowBlank : false,
29787     yearAllowBlank : false,
29788     dayPlaceholder : '',
29789     monthPlaceholder : '',
29790     yearPlaceholder : '',
29791     dayFormat : 'd',
29792     monthFormat : 'm',
29793     yearFormat : 'Y',
29794     isFormField : true,
29795     labellg : 0,
29796     labelmd : 0,
29797     labelsm : 0,
29798     labelxs : 0,
29799     
29800     getAutoCreate : function()
29801     {
29802         var cfg = {
29803             tag : 'div',
29804             cls : 'row roo-date-split-field-group',
29805             cn : [
29806                 {
29807                     tag : 'input',
29808                     type : 'hidden',
29809                     cls : 'form-hidden-field roo-date-split-field-group-value',
29810                     name : this.name
29811                 }
29812             ]
29813         };
29814         
29815         var labelCls = 'col-md-12';
29816         var contentCls = 'col-md-4';
29817         
29818         if(this.fieldLabel){
29819             
29820             var label = {
29821                 tag : 'div',
29822                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29823                 cn : [
29824                     {
29825                         tag : 'label',
29826                         html : this.fieldLabel
29827                     }
29828                 ]
29829             };
29830             
29831             if(this.labelAlign == 'left'){
29832             
29833                 if(this.labelWidth > 12){
29834                     label.style = "width: " + this.labelWidth + 'px';
29835                 }
29836
29837                 if(this.labelWidth < 13 && this.labelmd == 0){
29838                     this.labelmd = this.labelWidth;
29839                 }
29840
29841                 if(this.labellg > 0){
29842                     labelCls = ' col-lg-' + this.labellg;
29843                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29844                 }
29845
29846                 if(this.labelmd > 0){
29847                     labelCls = ' col-md-' + this.labelmd;
29848                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29849                 }
29850
29851                 if(this.labelsm > 0){
29852                     labelCls = ' col-sm-' + this.labelsm;
29853                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29854                 }
29855
29856                 if(this.labelxs > 0){
29857                     labelCls = ' col-xs-' + this.labelxs;
29858                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29859                 }
29860             }
29861             
29862             label.cls += ' ' + labelCls;
29863             
29864             cfg.cn.push(label);
29865         }
29866         
29867         Roo.each(['day', 'month', 'year'], function(t){
29868             cfg.cn.push({
29869                 tag : 'div',
29870                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29871             });
29872         }, this);
29873         
29874         return cfg;
29875     },
29876     
29877     inputEl: function ()
29878     {
29879         return this.el.select('.roo-date-split-field-group-value', true).first();
29880     },
29881     
29882     onRender : function(ct, position) 
29883     {
29884         var _this = this;
29885         
29886         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29887         
29888         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29889         
29890         this.dayField = new Roo.bootstrap.ComboBox({
29891             allowBlank : this.dayAllowBlank,
29892             alwaysQuery : true,
29893             displayField : 'value',
29894             editable : false,
29895             fieldLabel : '',
29896             forceSelection : true,
29897             mode : 'local',
29898             placeholder : this.dayPlaceholder,
29899             selectOnFocus : true,
29900             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29901             triggerAction : 'all',
29902             typeAhead : true,
29903             valueField : 'value',
29904             store : new Roo.data.SimpleStore({
29905                 data : (function() {    
29906                     var days = [];
29907                     _this.fireEvent('days', _this, days);
29908                     return days;
29909                 })(),
29910                 fields : [ 'value' ]
29911             }),
29912             listeners : {
29913                 select : function (_self, record, index)
29914                 {
29915                     _this.setValue(_this.getValue());
29916                 }
29917             }
29918         });
29919
29920         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29921         
29922         this.monthField = new Roo.bootstrap.MonthField({
29923             after : '<i class=\"fa fa-calendar\"></i>',
29924             allowBlank : this.monthAllowBlank,
29925             placeholder : this.monthPlaceholder,
29926             readOnly : true,
29927             listeners : {
29928                 render : function (_self)
29929                 {
29930                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29931                         e.preventDefault();
29932                         _self.focus();
29933                     });
29934                 },
29935                 select : function (_self, oldvalue, newvalue)
29936                 {
29937                     _this.setValue(_this.getValue());
29938                 }
29939             }
29940         });
29941         
29942         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29943         
29944         this.yearField = new Roo.bootstrap.ComboBox({
29945             allowBlank : this.yearAllowBlank,
29946             alwaysQuery : true,
29947             displayField : 'value',
29948             editable : false,
29949             fieldLabel : '',
29950             forceSelection : true,
29951             mode : 'local',
29952             placeholder : this.yearPlaceholder,
29953             selectOnFocus : true,
29954             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29955             triggerAction : 'all',
29956             typeAhead : true,
29957             valueField : 'value',
29958             store : new Roo.data.SimpleStore({
29959                 data : (function() {
29960                     var years = [];
29961                     _this.fireEvent('years', _this, years);
29962                     return years;
29963                 })(),
29964                 fields : [ 'value' ]
29965             }),
29966             listeners : {
29967                 select : function (_self, record, index)
29968                 {
29969                     _this.setValue(_this.getValue());
29970                 }
29971             }
29972         });
29973
29974         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29975     },
29976     
29977     setValue : function(v, format)
29978     {
29979         this.inputEl.dom.value = v;
29980         
29981         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29982         
29983         var d = Date.parseDate(v, f);
29984         
29985         if(!d){
29986             this.validate();
29987             return;
29988         }
29989         
29990         this.setDay(d.format(this.dayFormat));
29991         this.setMonth(d.format(this.monthFormat));
29992         this.setYear(d.format(this.yearFormat));
29993         
29994         this.validate();
29995         
29996         return;
29997     },
29998     
29999     setDay : function(v)
30000     {
30001         this.dayField.setValue(v);
30002         this.inputEl.dom.value = this.getValue();
30003         this.validate();
30004         return;
30005     },
30006     
30007     setMonth : function(v)
30008     {
30009         this.monthField.setValue(v, true);
30010         this.inputEl.dom.value = this.getValue();
30011         this.validate();
30012         return;
30013     },
30014     
30015     setYear : function(v)
30016     {
30017         this.yearField.setValue(v);
30018         this.inputEl.dom.value = this.getValue();
30019         this.validate();
30020         return;
30021     },
30022     
30023     getDay : function()
30024     {
30025         return this.dayField.getValue();
30026     },
30027     
30028     getMonth : function()
30029     {
30030         return this.monthField.getValue();
30031     },
30032     
30033     getYear : function()
30034     {
30035         return this.yearField.getValue();
30036     },
30037     
30038     getValue : function()
30039     {
30040         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30041         
30042         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30043         
30044         return date;
30045     },
30046     
30047     reset : function()
30048     {
30049         this.setDay('');
30050         this.setMonth('');
30051         this.setYear('');
30052         this.inputEl.dom.value = '';
30053         this.validate();
30054         return;
30055     },
30056     
30057     validate : function()
30058     {
30059         var d = this.dayField.validate();
30060         var m = this.monthField.validate();
30061         var y = this.yearField.validate();
30062         
30063         var valid = true;
30064         
30065         if(
30066                 (!this.dayAllowBlank && !d) ||
30067                 (!this.monthAllowBlank && !m) ||
30068                 (!this.yearAllowBlank && !y)
30069         ){
30070             valid = false;
30071         }
30072         
30073         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30074             return valid;
30075         }
30076         
30077         if(valid){
30078             this.markValid();
30079             return valid;
30080         }
30081         
30082         this.markInvalid();
30083         
30084         return valid;
30085     },
30086     
30087     markValid : function()
30088     {
30089         
30090         var label = this.el.select('label', true).first();
30091         var icon = this.el.select('i.fa-star', true).first();
30092
30093         if(label && icon){
30094             icon.remove();
30095         }
30096         
30097         this.fireEvent('valid', this);
30098     },
30099     
30100      /**
30101      * Mark this field as invalid
30102      * @param {String} msg The validation message
30103      */
30104     markInvalid : function(msg)
30105     {
30106         
30107         var label = this.el.select('label', true).first();
30108         var icon = this.el.select('i.fa-star', true).first();
30109
30110         if(label && !icon){
30111             this.el.select('.roo-date-split-field-label', true).createChild({
30112                 tag : 'i',
30113                 cls : 'text-danger fa fa-lg fa-star',
30114                 tooltip : 'This field is required',
30115                 style : 'margin-right:5px;'
30116             }, label, true);
30117         }
30118         
30119         this.fireEvent('invalid', this, msg);
30120     },
30121     
30122     clearInvalid : function()
30123     {
30124         var label = this.el.select('label', true).first();
30125         var icon = this.el.select('i.fa-star', true).first();
30126
30127         if(label && icon){
30128             icon.remove();
30129         }
30130         
30131         this.fireEvent('valid', this);
30132     },
30133     
30134     getName: function()
30135     {
30136         return this.name;
30137     }
30138     
30139 });
30140
30141  /**
30142  *
30143  * This is based on 
30144  * http://masonry.desandro.com
30145  *
30146  * The idea is to render all the bricks based on vertical width...
30147  *
30148  * The original code extends 'outlayer' - we might need to use that....
30149  * 
30150  */
30151
30152
30153 /**
30154  * @class Roo.bootstrap.LayoutMasonry
30155  * @extends Roo.bootstrap.Component
30156  * Bootstrap Layout Masonry class
30157  * 
30158  * @constructor
30159  * Create a new Element
30160  * @param {Object} config The config object
30161  */
30162
30163 Roo.bootstrap.LayoutMasonry = function(config){
30164     
30165     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30166     
30167     this.bricks = [];
30168     
30169     Roo.bootstrap.LayoutMasonry.register(this);
30170     
30171     this.addEvents({
30172         // raw events
30173         /**
30174          * @event layout
30175          * Fire after layout the items
30176          * @param {Roo.bootstrap.LayoutMasonry} this
30177          * @param {Roo.EventObject} e
30178          */
30179         "layout" : true
30180     });
30181     
30182 };
30183
30184 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30185     
30186     /**
30187      * @cfg {Boolean} isLayoutInstant = no animation?
30188      */   
30189     isLayoutInstant : false, // needed?
30190    
30191     /**
30192      * @cfg {Number} boxWidth  width of the columns
30193      */   
30194     boxWidth : 450,
30195     
30196       /**
30197      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30198      */   
30199     boxHeight : 0,
30200     
30201     /**
30202      * @cfg {Number} padWidth padding below box..
30203      */   
30204     padWidth : 10, 
30205     
30206     /**
30207      * @cfg {Number} gutter gutter width..
30208      */   
30209     gutter : 10,
30210     
30211      /**
30212      * @cfg {Number} maxCols maximum number of columns
30213      */   
30214     
30215     maxCols: 0,
30216     
30217     /**
30218      * @cfg {Boolean} isAutoInitial defalut true
30219      */   
30220     isAutoInitial : true, 
30221     
30222     containerWidth: 0,
30223     
30224     /**
30225      * @cfg {Boolean} isHorizontal defalut false
30226      */   
30227     isHorizontal : false, 
30228
30229     currentSize : null,
30230     
30231     tag: 'div',
30232     
30233     cls: '',
30234     
30235     bricks: null, //CompositeElement
30236     
30237     cols : 1,
30238     
30239     _isLayoutInited : false,
30240     
30241 //    isAlternative : false, // only use for vertical layout...
30242     
30243     /**
30244      * @cfg {Number} alternativePadWidth padding below box..
30245      */   
30246     alternativePadWidth : 50,
30247     
30248     selectedBrick : [],
30249     
30250     getAutoCreate : function(){
30251         
30252         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30253         
30254         var cfg = {
30255             tag: this.tag,
30256             cls: 'blog-masonary-wrapper ' + this.cls,
30257             cn : {
30258                 cls : 'mas-boxes masonary'
30259             }
30260         };
30261         
30262         return cfg;
30263     },
30264     
30265     getChildContainer: function( )
30266     {
30267         if (this.boxesEl) {
30268             return this.boxesEl;
30269         }
30270         
30271         this.boxesEl = this.el.select('.mas-boxes').first();
30272         
30273         return this.boxesEl;
30274     },
30275     
30276     
30277     initEvents : function()
30278     {
30279         var _this = this;
30280         
30281         if(this.isAutoInitial){
30282             Roo.log('hook children rendered');
30283             this.on('childrenrendered', function() {
30284                 Roo.log('children rendered');
30285                 _this.initial();
30286             } ,this);
30287         }
30288     },
30289     
30290     initial : function()
30291     {
30292         this.selectedBrick = [];
30293         
30294         this.currentSize = this.el.getBox(true);
30295         
30296         Roo.EventManager.onWindowResize(this.resize, this); 
30297
30298         if(!this.isAutoInitial){
30299             this.layout();
30300             return;
30301         }
30302         
30303         this.layout();
30304         
30305         return;
30306         //this.layout.defer(500,this);
30307         
30308     },
30309     
30310     resize : function()
30311     {
30312         var cs = this.el.getBox(true);
30313         
30314         if (
30315                 this.currentSize.width == cs.width && 
30316                 this.currentSize.x == cs.x && 
30317                 this.currentSize.height == cs.height && 
30318                 this.currentSize.y == cs.y 
30319         ) {
30320             Roo.log("no change in with or X or Y");
30321             return;
30322         }
30323         
30324         this.currentSize = cs;
30325         
30326         this.layout();
30327         
30328     },
30329     
30330     layout : function()
30331     {   
30332         this._resetLayout();
30333         
30334         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30335         
30336         this.layoutItems( isInstant );
30337       
30338         this._isLayoutInited = true;
30339         
30340         this.fireEvent('layout', this);
30341         
30342     },
30343     
30344     _resetLayout : function()
30345     {
30346         if(this.isHorizontal){
30347             this.horizontalMeasureColumns();
30348             return;
30349         }
30350         
30351         this.verticalMeasureColumns();
30352         
30353     },
30354     
30355     verticalMeasureColumns : function()
30356     {
30357         this.getContainerWidth();
30358         
30359 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30360 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30361 //            return;
30362 //        }
30363         
30364         var boxWidth = this.boxWidth + this.padWidth;
30365         
30366         if(this.containerWidth < this.boxWidth){
30367             boxWidth = this.containerWidth
30368         }
30369         
30370         var containerWidth = this.containerWidth;
30371         
30372         var cols = Math.floor(containerWidth / boxWidth);
30373         
30374         this.cols = Math.max( cols, 1 );
30375         
30376         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30377         
30378         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30379         
30380         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30381         
30382         this.colWidth = boxWidth + avail - this.padWidth;
30383         
30384         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30385         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30386     },
30387     
30388     horizontalMeasureColumns : function()
30389     {
30390         this.getContainerWidth();
30391         
30392         var boxWidth = this.boxWidth;
30393         
30394         if(this.containerWidth < boxWidth){
30395             boxWidth = this.containerWidth;
30396         }
30397         
30398         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30399         
30400         this.el.setHeight(boxWidth);
30401         
30402     },
30403     
30404     getContainerWidth : function()
30405     {
30406         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30407     },
30408     
30409     layoutItems : function( isInstant )
30410     {
30411         Roo.log(this.bricks);
30412         
30413         var items = Roo.apply([], this.bricks);
30414         
30415         if(this.isHorizontal){
30416             this._horizontalLayoutItems( items , isInstant );
30417             return;
30418         }
30419         
30420 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30421 //            this._verticalAlternativeLayoutItems( items , isInstant );
30422 //            return;
30423 //        }
30424         
30425         this._verticalLayoutItems( items , isInstant );
30426         
30427     },
30428     
30429     _verticalLayoutItems : function ( items , isInstant)
30430     {
30431         if ( !items || !items.length ) {
30432             return;
30433         }
30434         
30435         var standard = [
30436             ['xs', 'xs', 'xs', 'tall'],
30437             ['xs', 'xs', 'tall'],
30438             ['xs', 'xs', 'sm'],
30439             ['xs', 'xs', 'xs'],
30440             ['xs', 'tall'],
30441             ['xs', 'sm'],
30442             ['xs', 'xs'],
30443             ['xs'],
30444             
30445             ['sm', 'xs', 'xs'],
30446             ['sm', 'xs'],
30447             ['sm'],
30448             
30449             ['tall', 'xs', 'xs', 'xs'],
30450             ['tall', 'xs', 'xs'],
30451             ['tall', 'xs'],
30452             ['tall']
30453             
30454         ];
30455         
30456         var queue = [];
30457         
30458         var boxes = [];
30459         
30460         var box = [];
30461         
30462         Roo.each(items, function(item, k){
30463             
30464             switch (item.size) {
30465                 // these layouts take up a full box,
30466                 case 'md' :
30467                 case 'md-left' :
30468                 case 'md-right' :
30469                 case 'wide' :
30470                     
30471                     if(box.length){
30472                         boxes.push(box);
30473                         box = [];
30474                     }
30475                     
30476                     boxes.push([item]);
30477                     
30478                     break;
30479                     
30480                 case 'xs' :
30481                 case 'sm' :
30482                 case 'tall' :
30483                     
30484                     box.push(item);
30485                     
30486                     break;
30487                 default :
30488                     break;
30489                     
30490             }
30491             
30492         }, this);
30493         
30494         if(box.length){
30495             boxes.push(box);
30496             box = [];
30497         }
30498         
30499         var filterPattern = function(box, length)
30500         {
30501             if(!box.length){
30502                 return;
30503             }
30504             
30505             var match = false;
30506             
30507             var pattern = box.slice(0, length);
30508             
30509             var format = [];
30510             
30511             Roo.each(pattern, function(i){
30512                 format.push(i.size);
30513             }, this);
30514             
30515             Roo.each(standard, function(s){
30516                 
30517                 if(String(s) != String(format)){
30518                     return;
30519                 }
30520                 
30521                 match = true;
30522                 return false;
30523                 
30524             }, this);
30525             
30526             if(!match && length == 1){
30527                 return;
30528             }
30529             
30530             if(!match){
30531                 filterPattern(box, length - 1);
30532                 return;
30533             }
30534                 
30535             queue.push(pattern);
30536
30537             box = box.slice(length, box.length);
30538
30539             filterPattern(box, 4);
30540
30541             return;
30542             
30543         }
30544         
30545         Roo.each(boxes, function(box, k){
30546             
30547             if(!box.length){
30548                 return;
30549             }
30550             
30551             if(box.length == 1){
30552                 queue.push(box);
30553                 return;
30554             }
30555             
30556             filterPattern(box, 4);
30557             
30558         }, this);
30559         
30560         this._processVerticalLayoutQueue( queue, isInstant );
30561         
30562     },
30563     
30564 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30565 //    {
30566 //        if ( !items || !items.length ) {
30567 //            return;
30568 //        }
30569 //
30570 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30571 //        
30572 //    },
30573     
30574     _horizontalLayoutItems : function ( items , isInstant)
30575     {
30576         if ( !items || !items.length || items.length < 3) {
30577             return;
30578         }
30579         
30580         items.reverse();
30581         
30582         var eItems = items.slice(0, 3);
30583         
30584         items = items.slice(3, items.length);
30585         
30586         var standard = [
30587             ['xs', 'xs', 'xs', 'wide'],
30588             ['xs', 'xs', 'wide'],
30589             ['xs', 'xs', 'sm'],
30590             ['xs', 'xs', 'xs'],
30591             ['xs', 'wide'],
30592             ['xs', 'sm'],
30593             ['xs', 'xs'],
30594             ['xs'],
30595             
30596             ['sm', 'xs', 'xs'],
30597             ['sm', 'xs'],
30598             ['sm'],
30599             
30600             ['wide', 'xs', 'xs', 'xs'],
30601             ['wide', 'xs', 'xs'],
30602             ['wide', 'xs'],
30603             ['wide'],
30604             
30605             ['wide-thin']
30606         ];
30607         
30608         var queue = [];
30609         
30610         var boxes = [];
30611         
30612         var box = [];
30613         
30614         Roo.each(items, function(item, k){
30615             
30616             switch (item.size) {
30617                 case 'md' :
30618                 case 'md-left' :
30619                 case 'md-right' :
30620                 case 'tall' :
30621                     
30622                     if(box.length){
30623                         boxes.push(box);
30624                         box = [];
30625                     }
30626                     
30627                     boxes.push([item]);
30628                     
30629                     break;
30630                     
30631                 case 'xs' :
30632                 case 'sm' :
30633                 case 'wide' :
30634                 case 'wide-thin' :
30635                     
30636                     box.push(item);
30637                     
30638                     break;
30639                 default :
30640                     break;
30641                     
30642             }
30643             
30644         }, this);
30645         
30646         if(box.length){
30647             boxes.push(box);
30648             box = [];
30649         }
30650         
30651         var filterPattern = function(box, length)
30652         {
30653             if(!box.length){
30654                 return;
30655             }
30656             
30657             var match = false;
30658             
30659             var pattern = box.slice(0, length);
30660             
30661             var format = [];
30662             
30663             Roo.each(pattern, function(i){
30664                 format.push(i.size);
30665             }, this);
30666             
30667             Roo.each(standard, function(s){
30668                 
30669                 if(String(s) != String(format)){
30670                     return;
30671                 }
30672                 
30673                 match = true;
30674                 return false;
30675                 
30676             }, this);
30677             
30678             if(!match && length == 1){
30679                 return;
30680             }
30681             
30682             if(!match){
30683                 filterPattern(box, length - 1);
30684                 return;
30685             }
30686                 
30687             queue.push(pattern);
30688
30689             box = box.slice(length, box.length);
30690
30691             filterPattern(box, 4);
30692
30693             return;
30694             
30695         }
30696         
30697         Roo.each(boxes, function(box, k){
30698             
30699             if(!box.length){
30700                 return;
30701             }
30702             
30703             if(box.length == 1){
30704                 queue.push(box);
30705                 return;
30706             }
30707             
30708             filterPattern(box, 4);
30709             
30710         }, this);
30711         
30712         
30713         var prune = [];
30714         
30715         var pos = this.el.getBox(true);
30716         
30717         var minX = pos.x;
30718         
30719         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30720         
30721         var hit_end = false;
30722         
30723         Roo.each(queue, function(box){
30724             
30725             if(hit_end){
30726                 
30727                 Roo.each(box, function(b){
30728                 
30729                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30730                     b.el.hide();
30731
30732                 }, this);
30733
30734                 return;
30735             }
30736             
30737             var mx = 0;
30738             
30739             Roo.each(box, function(b){
30740                 
30741                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30742                 b.el.show();
30743
30744                 mx = Math.max(mx, b.x);
30745                 
30746             }, this);
30747             
30748             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30749             
30750             if(maxX < minX){
30751                 
30752                 Roo.each(box, function(b){
30753                 
30754                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30755                     b.el.hide();
30756                     
30757                 }, this);
30758                 
30759                 hit_end = true;
30760                 
30761                 return;
30762             }
30763             
30764             prune.push(box);
30765             
30766         }, this);
30767         
30768         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30769     },
30770     
30771     /** Sets position of item in DOM
30772     * @param {Element} item
30773     * @param {Number} x - horizontal position
30774     * @param {Number} y - vertical position
30775     * @param {Boolean} isInstant - disables transitions
30776     */
30777     _processVerticalLayoutQueue : function( queue, isInstant )
30778     {
30779         var pos = this.el.getBox(true);
30780         var x = pos.x;
30781         var y = pos.y;
30782         var maxY = [];
30783         
30784         for (var i = 0; i < this.cols; i++){
30785             maxY[i] = pos.y;
30786         }
30787         
30788         Roo.each(queue, function(box, k){
30789             
30790             var col = k % this.cols;
30791             
30792             Roo.each(box, function(b,kk){
30793                 
30794                 b.el.position('absolute');
30795                 
30796                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30797                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30798                 
30799                 if(b.size == 'md-left' || b.size == 'md-right'){
30800                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30801                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30802                 }
30803                 
30804                 b.el.setWidth(width);
30805                 b.el.setHeight(height);
30806                 // iframe?
30807                 b.el.select('iframe',true).setSize(width,height);
30808                 
30809             }, this);
30810             
30811             for (var i = 0; i < this.cols; i++){
30812                 
30813                 if(maxY[i] < maxY[col]){
30814                     col = i;
30815                     continue;
30816                 }
30817                 
30818                 col = Math.min(col, i);
30819                 
30820             }
30821             
30822             x = pos.x + col * (this.colWidth + this.padWidth);
30823             
30824             y = maxY[col];
30825             
30826             var positions = [];
30827             
30828             switch (box.length){
30829                 case 1 :
30830                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30831                     break;
30832                 case 2 :
30833                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30834                     break;
30835                 case 3 :
30836                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30837                     break;
30838                 case 4 :
30839                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30840                     break;
30841                 default :
30842                     break;
30843             }
30844             
30845             Roo.each(box, function(b,kk){
30846                 
30847                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30848                 
30849                 var sz = b.el.getSize();
30850                 
30851                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30852                 
30853             }, this);
30854             
30855         }, this);
30856         
30857         var mY = 0;
30858         
30859         for (var i = 0; i < this.cols; i++){
30860             mY = Math.max(mY, maxY[i]);
30861         }
30862         
30863         this.el.setHeight(mY - pos.y);
30864         
30865     },
30866     
30867 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30868 //    {
30869 //        var pos = this.el.getBox(true);
30870 //        var x = pos.x;
30871 //        var y = pos.y;
30872 //        var maxX = pos.right;
30873 //        
30874 //        var maxHeight = 0;
30875 //        
30876 //        Roo.each(items, function(item, k){
30877 //            
30878 //            var c = k % 2;
30879 //            
30880 //            item.el.position('absolute');
30881 //                
30882 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30883 //
30884 //            item.el.setWidth(width);
30885 //
30886 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30887 //
30888 //            item.el.setHeight(height);
30889 //            
30890 //            if(c == 0){
30891 //                item.el.setXY([x, y], isInstant ? false : true);
30892 //            } else {
30893 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30894 //            }
30895 //            
30896 //            y = y + height + this.alternativePadWidth;
30897 //            
30898 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30899 //            
30900 //        }, this);
30901 //        
30902 //        this.el.setHeight(maxHeight);
30903 //        
30904 //    },
30905     
30906     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30907     {
30908         var pos = this.el.getBox(true);
30909         
30910         var minX = pos.x;
30911         var minY = pos.y;
30912         
30913         var maxX = pos.right;
30914         
30915         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30916         
30917         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30918         
30919         Roo.each(queue, function(box, k){
30920             
30921             Roo.each(box, function(b, kk){
30922                 
30923                 b.el.position('absolute');
30924                 
30925                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30926                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30927                 
30928                 if(b.size == 'md-left' || b.size == 'md-right'){
30929                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30930                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30931                 }
30932                 
30933                 b.el.setWidth(width);
30934                 b.el.setHeight(height);
30935                 
30936             }, this);
30937             
30938             if(!box.length){
30939                 return;
30940             }
30941             
30942             var positions = [];
30943             
30944             switch (box.length){
30945                 case 1 :
30946                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30947                     break;
30948                 case 2 :
30949                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30950                     break;
30951                 case 3 :
30952                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30953                     break;
30954                 case 4 :
30955                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30956                     break;
30957                 default :
30958                     break;
30959             }
30960             
30961             Roo.each(box, function(b,kk){
30962                 
30963                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30964                 
30965                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30966                 
30967             }, this);
30968             
30969         }, this);
30970         
30971     },
30972     
30973     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30974     {
30975         Roo.each(eItems, function(b,k){
30976             
30977             b.size = (k == 0) ? 'sm' : 'xs';
30978             b.x = (k == 0) ? 2 : 1;
30979             b.y = (k == 0) ? 2 : 1;
30980             
30981             b.el.position('absolute');
30982             
30983             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30984                 
30985             b.el.setWidth(width);
30986             
30987             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30988             
30989             b.el.setHeight(height);
30990             
30991         }, this);
30992
30993         var positions = [];
30994         
30995         positions.push({
30996             x : maxX - this.unitWidth * 2 - this.gutter,
30997             y : minY
30998         });
30999         
31000         positions.push({
31001             x : maxX - this.unitWidth,
31002             y : minY + (this.unitWidth + this.gutter) * 2
31003         });
31004         
31005         positions.push({
31006             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31007             y : minY
31008         });
31009         
31010         Roo.each(eItems, function(b,k){
31011             
31012             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31013
31014         }, this);
31015         
31016     },
31017     
31018     getVerticalOneBoxColPositions : function(x, y, box)
31019     {
31020         var pos = [];
31021         
31022         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31023         
31024         if(box[0].size == 'md-left'){
31025             rand = 0;
31026         }
31027         
31028         if(box[0].size == 'md-right'){
31029             rand = 1;
31030         }
31031         
31032         pos.push({
31033             x : x + (this.unitWidth + this.gutter) * rand,
31034             y : y
31035         });
31036         
31037         return pos;
31038     },
31039     
31040     getVerticalTwoBoxColPositions : function(x, y, box)
31041     {
31042         var pos = [];
31043         
31044         if(box[0].size == 'xs'){
31045             
31046             pos.push({
31047                 x : x,
31048                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31049             });
31050
31051             pos.push({
31052                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31053                 y : y
31054             });
31055             
31056             return pos;
31057             
31058         }
31059         
31060         pos.push({
31061             x : x,
31062             y : y
31063         });
31064
31065         pos.push({
31066             x : x + (this.unitWidth + this.gutter) * 2,
31067             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31068         });
31069         
31070         return pos;
31071         
31072     },
31073     
31074     getVerticalThreeBoxColPositions : function(x, y, box)
31075     {
31076         var pos = [];
31077         
31078         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31079             
31080             pos.push({
31081                 x : x,
31082                 y : y
31083             });
31084
31085             pos.push({
31086                 x : x + (this.unitWidth + this.gutter) * 1,
31087                 y : y
31088             });
31089             
31090             pos.push({
31091                 x : x + (this.unitWidth + this.gutter) * 2,
31092                 y : y
31093             });
31094             
31095             return pos;
31096             
31097         }
31098         
31099         if(box[0].size == 'xs' && box[1].size == 'xs'){
31100             
31101             pos.push({
31102                 x : x,
31103                 y : y
31104             });
31105
31106             pos.push({
31107                 x : x,
31108                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31109             });
31110             
31111             pos.push({
31112                 x : x + (this.unitWidth + this.gutter) * 1,
31113                 y : y
31114             });
31115             
31116             return pos;
31117             
31118         }
31119         
31120         pos.push({
31121             x : x,
31122             y : y
31123         });
31124
31125         pos.push({
31126             x : x + (this.unitWidth + this.gutter) * 2,
31127             y : y
31128         });
31129
31130         pos.push({
31131             x : x + (this.unitWidth + this.gutter) * 2,
31132             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31133         });
31134             
31135         return pos;
31136         
31137     },
31138     
31139     getVerticalFourBoxColPositions : function(x, y, box)
31140     {
31141         var pos = [];
31142         
31143         if(box[0].size == 'xs'){
31144             
31145             pos.push({
31146                 x : x,
31147                 y : y
31148             });
31149
31150             pos.push({
31151                 x : x,
31152                 y : y + (this.unitHeight + this.gutter) * 1
31153             });
31154             
31155             pos.push({
31156                 x : x,
31157                 y : y + (this.unitHeight + this.gutter) * 2
31158             });
31159             
31160             pos.push({
31161                 x : x + (this.unitWidth + this.gutter) * 1,
31162                 y : y
31163             });
31164             
31165             return pos;
31166             
31167         }
31168         
31169         pos.push({
31170             x : x,
31171             y : y
31172         });
31173
31174         pos.push({
31175             x : x + (this.unitWidth + this.gutter) * 2,
31176             y : y
31177         });
31178
31179         pos.push({
31180             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31181             y : y + (this.unitHeight + this.gutter) * 1
31182         });
31183
31184         pos.push({
31185             x : x + (this.unitWidth + this.gutter) * 2,
31186             y : y + (this.unitWidth + this.gutter) * 2
31187         });
31188
31189         return pos;
31190         
31191     },
31192     
31193     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31194     {
31195         var pos = [];
31196         
31197         if(box[0].size == 'md-left'){
31198             pos.push({
31199                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31200                 y : minY
31201             });
31202             
31203             return pos;
31204         }
31205         
31206         if(box[0].size == 'md-right'){
31207             pos.push({
31208                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31209                 y : minY + (this.unitWidth + this.gutter) * 1
31210             });
31211             
31212             return pos;
31213         }
31214         
31215         var rand = Math.floor(Math.random() * (4 - box[0].y));
31216         
31217         pos.push({
31218             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31219             y : minY + (this.unitWidth + this.gutter) * rand
31220         });
31221         
31222         return pos;
31223         
31224     },
31225     
31226     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31227     {
31228         var pos = [];
31229         
31230         if(box[0].size == 'xs'){
31231             
31232             pos.push({
31233                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31234                 y : minY
31235             });
31236
31237             pos.push({
31238                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31239                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31240             });
31241             
31242             return pos;
31243             
31244         }
31245         
31246         pos.push({
31247             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31248             y : minY
31249         });
31250
31251         pos.push({
31252             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31253             y : minY + (this.unitWidth + this.gutter) * 2
31254         });
31255         
31256         return pos;
31257         
31258     },
31259     
31260     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31261     {
31262         var pos = [];
31263         
31264         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31265             
31266             pos.push({
31267                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31268                 y : minY
31269             });
31270
31271             pos.push({
31272                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31273                 y : minY + (this.unitWidth + this.gutter) * 1
31274             });
31275             
31276             pos.push({
31277                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31278                 y : minY + (this.unitWidth + this.gutter) * 2
31279             });
31280             
31281             return pos;
31282             
31283         }
31284         
31285         if(box[0].size == 'xs' && box[1].size == 'xs'){
31286             
31287             pos.push({
31288                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31289                 y : minY
31290             });
31291
31292             pos.push({
31293                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31294                 y : minY
31295             });
31296             
31297             pos.push({
31298                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31299                 y : minY + (this.unitWidth + this.gutter) * 1
31300             });
31301             
31302             return pos;
31303             
31304         }
31305         
31306         pos.push({
31307             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31308             y : minY
31309         });
31310
31311         pos.push({
31312             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31313             y : minY + (this.unitWidth + this.gutter) * 2
31314         });
31315
31316         pos.push({
31317             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31318             y : minY + (this.unitWidth + this.gutter) * 2
31319         });
31320             
31321         return pos;
31322         
31323     },
31324     
31325     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31326     {
31327         var pos = [];
31328         
31329         if(box[0].size == 'xs'){
31330             
31331             pos.push({
31332                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31333                 y : minY
31334             });
31335
31336             pos.push({
31337                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31338                 y : minY
31339             });
31340             
31341             pos.push({
31342                 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),
31343                 y : minY
31344             });
31345             
31346             pos.push({
31347                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31348                 y : minY + (this.unitWidth + this.gutter) * 1
31349             });
31350             
31351             return pos;
31352             
31353         }
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) * 2
31363         });
31364         
31365         pos.push({
31366             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31367             y : minY + (this.unitWidth + this.gutter) * 2
31368         });
31369         
31370         pos.push({
31371             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),
31372             y : minY + (this.unitWidth + this.gutter) * 2
31373         });
31374
31375         return pos;
31376         
31377     },
31378     
31379     /**
31380     * remove a Masonry Brick
31381     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31382     */
31383     removeBrick : function(brick_id)
31384     {
31385         if (!brick_id) {
31386             return;
31387         }
31388         
31389         for (var i = 0; i<this.bricks.length; i++) {
31390             if (this.bricks[i].id == brick_id) {
31391                 this.bricks.splice(i,1);
31392                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31393                 this.initial();
31394             }
31395         }
31396     },
31397     
31398     /**
31399     * adds a Masonry Brick
31400     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31401     */
31402     addBrick : function(cfg)
31403     {
31404         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31405         //this.register(cn);
31406         cn.parentId = this.id;
31407         cn.onRender(this.el, null);
31408         return cn;
31409     },
31410     
31411     /**
31412     * register a Masonry Brick
31413     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31414     */
31415     
31416     register : function(brick)
31417     {
31418         this.bricks.push(brick);
31419         brick.masonryId = this.id;
31420     },
31421     
31422     /**
31423     * clear all the Masonry Brick
31424     */
31425     clearAll : function()
31426     {
31427         this.bricks = [];
31428         //this.getChildContainer().dom.innerHTML = "";
31429         this.el.dom.innerHTML = '';
31430     },
31431     
31432     getSelected : function()
31433     {
31434         if (!this.selectedBrick) {
31435             return false;
31436         }
31437         
31438         return this.selectedBrick;
31439     }
31440 });
31441
31442 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31443     
31444     groups: {},
31445      /**
31446     * register a Masonry Layout
31447     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31448     */
31449     
31450     register : function(layout)
31451     {
31452         this.groups[layout.id] = layout;
31453     },
31454     /**
31455     * fetch a  Masonry Layout based on the masonry layout ID
31456     * @param {string} the masonry layout to add
31457     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31458     */
31459     
31460     get: function(layout_id) {
31461         if (typeof(this.groups[layout_id]) == 'undefined') {
31462             return false;
31463         }
31464         return this.groups[layout_id] ;
31465     }
31466     
31467     
31468     
31469 });
31470
31471  
31472
31473  /**
31474  *
31475  * This is based on 
31476  * http://masonry.desandro.com
31477  *
31478  * The idea is to render all the bricks based on vertical width...
31479  *
31480  * The original code extends 'outlayer' - we might need to use that....
31481  * 
31482  */
31483
31484
31485 /**
31486  * @class Roo.bootstrap.LayoutMasonryAuto
31487  * @extends Roo.bootstrap.Component
31488  * Bootstrap Layout Masonry class
31489  * 
31490  * @constructor
31491  * Create a new Element
31492  * @param {Object} config The config object
31493  */
31494
31495 Roo.bootstrap.LayoutMasonryAuto = function(config){
31496     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31497 };
31498
31499 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31500     
31501       /**
31502      * @cfg {Boolean} isFitWidth  - resize the width..
31503      */   
31504     isFitWidth : false,  // options..
31505     /**
31506      * @cfg {Boolean} isOriginLeft = left align?
31507      */   
31508     isOriginLeft : true,
31509     /**
31510      * @cfg {Boolean} isOriginTop = top align?
31511      */   
31512     isOriginTop : false,
31513     /**
31514      * @cfg {Boolean} isLayoutInstant = no animation?
31515      */   
31516     isLayoutInstant : false, // needed?
31517     /**
31518      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31519      */   
31520     isResizingContainer : true,
31521     /**
31522      * @cfg {Number} columnWidth  width of the columns 
31523      */   
31524     
31525     columnWidth : 0,
31526     
31527     /**
31528      * @cfg {Number} maxCols maximum number of columns
31529      */   
31530     
31531     maxCols: 0,
31532     /**
31533      * @cfg {Number} padHeight padding below box..
31534      */   
31535     
31536     padHeight : 10, 
31537     
31538     /**
31539      * @cfg {Boolean} isAutoInitial defalut true
31540      */   
31541     
31542     isAutoInitial : true, 
31543     
31544     // private?
31545     gutter : 0,
31546     
31547     containerWidth: 0,
31548     initialColumnWidth : 0,
31549     currentSize : null,
31550     
31551     colYs : null, // array.
31552     maxY : 0,
31553     padWidth: 10,
31554     
31555     
31556     tag: 'div',
31557     cls: '',
31558     bricks: null, //CompositeElement
31559     cols : 0, // array?
31560     // element : null, // wrapped now this.el
31561     _isLayoutInited : null, 
31562     
31563     
31564     getAutoCreate : function(){
31565         
31566         var cfg = {
31567             tag: this.tag,
31568             cls: 'blog-masonary-wrapper ' + this.cls,
31569             cn : {
31570                 cls : 'mas-boxes masonary'
31571             }
31572         };
31573         
31574         return cfg;
31575     },
31576     
31577     getChildContainer: function( )
31578     {
31579         if (this.boxesEl) {
31580             return this.boxesEl;
31581         }
31582         
31583         this.boxesEl = this.el.select('.mas-boxes').first();
31584         
31585         return this.boxesEl;
31586     },
31587     
31588     
31589     initEvents : function()
31590     {
31591         var _this = this;
31592         
31593         if(this.isAutoInitial){
31594             Roo.log('hook children rendered');
31595             this.on('childrenrendered', function() {
31596                 Roo.log('children rendered');
31597                 _this.initial();
31598             } ,this);
31599         }
31600         
31601     },
31602     
31603     initial : function()
31604     {
31605         this.reloadItems();
31606
31607         this.currentSize = this.el.getBox(true);
31608
31609         /// was window resize... - let's see if this works..
31610         Roo.EventManager.onWindowResize(this.resize, this); 
31611
31612         if(!this.isAutoInitial){
31613             this.layout();
31614             return;
31615         }
31616         
31617         this.layout.defer(500,this);
31618     },
31619     
31620     reloadItems: function()
31621     {
31622         this.bricks = this.el.select('.masonry-brick', true);
31623         
31624         this.bricks.each(function(b) {
31625             //Roo.log(b.getSize());
31626             if (!b.attr('originalwidth')) {
31627                 b.attr('originalwidth',  b.getSize().width);
31628             }
31629             
31630         });
31631         
31632         Roo.log(this.bricks.elements.length);
31633     },
31634     
31635     resize : function()
31636     {
31637         Roo.log('resize');
31638         var cs = this.el.getBox(true);
31639         
31640         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31641             Roo.log("no change in with or X");
31642             return;
31643         }
31644         this.currentSize = cs;
31645         this.layout();
31646     },
31647     
31648     layout : function()
31649     {
31650          Roo.log('layout');
31651         this._resetLayout();
31652         //this._manageStamps();
31653       
31654         // don't animate first layout
31655         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31656         this.layoutItems( isInstant );
31657       
31658         // flag for initalized
31659         this._isLayoutInited = true;
31660     },
31661     
31662     layoutItems : function( isInstant )
31663     {
31664         //var items = this._getItemsForLayout( this.items );
31665         // original code supports filtering layout items.. we just ignore it..
31666         
31667         this._layoutItems( this.bricks , isInstant );
31668       
31669         this._postLayout();
31670     },
31671     _layoutItems : function ( items , isInstant)
31672     {
31673        //this.fireEvent( 'layout', this, items );
31674     
31675
31676         if ( !items || !items.elements.length ) {
31677           // no items, emit event with empty array
31678             return;
31679         }
31680
31681         var queue = [];
31682         items.each(function(item) {
31683             Roo.log("layout item");
31684             Roo.log(item);
31685             // get x/y object from method
31686             var position = this._getItemLayoutPosition( item );
31687             // enqueue
31688             position.item = item;
31689             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31690             queue.push( position );
31691         }, this);
31692       
31693         this._processLayoutQueue( queue );
31694     },
31695     /** Sets position of item in DOM
31696     * @param {Element} item
31697     * @param {Number} x - horizontal position
31698     * @param {Number} y - vertical position
31699     * @param {Boolean} isInstant - disables transitions
31700     */
31701     _processLayoutQueue : function( queue )
31702     {
31703         for ( var i=0, len = queue.length; i < len; i++ ) {
31704             var obj = queue[i];
31705             obj.item.position('absolute');
31706             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31707         }
31708     },
31709       
31710     
31711     /**
31712     * Any logic you want to do after each layout,
31713     * i.e. size the container
31714     */
31715     _postLayout : function()
31716     {
31717         this.resizeContainer();
31718     },
31719     
31720     resizeContainer : function()
31721     {
31722         if ( !this.isResizingContainer ) {
31723             return;
31724         }
31725         var size = this._getContainerSize();
31726         if ( size ) {
31727             this.el.setSize(size.width,size.height);
31728             this.boxesEl.setSize(size.width,size.height);
31729         }
31730     },
31731     
31732     
31733     
31734     _resetLayout : function()
31735     {
31736         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31737         this.colWidth = this.el.getWidth();
31738         //this.gutter = this.el.getWidth(); 
31739         
31740         this.measureColumns();
31741
31742         // reset column Y
31743         var i = this.cols;
31744         this.colYs = [];
31745         while (i--) {
31746             this.colYs.push( 0 );
31747         }
31748     
31749         this.maxY = 0;
31750     },
31751
31752     measureColumns : function()
31753     {
31754         this.getContainerWidth();
31755       // if columnWidth is 0, default to outerWidth of first item
31756         if ( !this.columnWidth ) {
31757             var firstItem = this.bricks.first();
31758             Roo.log(firstItem);
31759             this.columnWidth  = this.containerWidth;
31760             if (firstItem && firstItem.attr('originalwidth') ) {
31761                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31762             }
31763             // columnWidth fall back to item of first element
31764             Roo.log("set column width?");
31765                         this.initialColumnWidth = this.columnWidth  ;
31766
31767             // if first elem has no width, default to size of container
31768             
31769         }
31770         
31771         
31772         if (this.initialColumnWidth) {
31773             this.columnWidth = this.initialColumnWidth;
31774         }
31775         
31776         
31777             
31778         // column width is fixed at the top - however if container width get's smaller we should
31779         // reduce it...
31780         
31781         // this bit calcs how man columns..
31782             
31783         var columnWidth = this.columnWidth += this.gutter;
31784       
31785         // calculate columns
31786         var containerWidth = this.containerWidth + this.gutter;
31787         
31788         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31789         // fix rounding errors, typically with gutters
31790         var excess = columnWidth - containerWidth % columnWidth;
31791         
31792         
31793         // if overshoot is less than a pixel, round up, otherwise floor it
31794         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31795         cols = Math[ mathMethod ]( cols );
31796         this.cols = Math.max( cols, 1 );
31797         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31798         
31799          // padding positioning..
31800         var totalColWidth = this.cols * this.columnWidth;
31801         var padavail = this.containerWidth - totalColWidth;
31802         // so for 2 columns - we need 3 'pads'
31803         
31804         var padNeeded = (1+this.cols) * this.padWidth;
31805         
31806         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31807         
31808         this.columnWidth += padExtra
31809         //this.padWidth = Math.floor(padavail /  ( this.cols));
31810         
31811         // adjust colum width so that padding is fixed??
31812         
31813         // we have 3 columns ... total = width * 3
31814         // we have X left over... that should be used by 
31815         
31816         //if (this.expandC) {
31817             
31818         //}
31819         
31820         
31821         
31822     },
31823     
31824     getContainerWidth : function()
31825     {
31826        /* // container is parent if fit width
31827         var container = this.isFitWidth ? this.element.parentNode : this.element;
31828         // check that this.size and size are there
31829         // IE8 triggers resize on body size change, so they might not be
31830         
31831         var size = getSize( container );  //FIXME
31832         this.containerWidth = size && size.innerWidth; //FIXME
31833         */
31834          
31835         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31836         
31837     },
31838     
31839     _getItemLayoutPosition : function( item )  // what is item?
31840     {
31841         // we resize the item to our columnWidth..
31842       
31843         item.setWidth(this.columnWidth);
31844         item.autoBoxAdjust  = false;
31845         
31846         var sz = item.getSize();
31847  
31848         // how many columns does this brick span
31849         var remainder = this.containerWidth % this.columnWidth;
31850         
31851         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31852         // round if off by 1 pixel, otherwise use ceil
31853         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31854         colSpan = Math.min( colSpan, this.cols );
31855         
31856         // normally this should be '1' as we dont' currently allow multi width columns..
31857         
31858         var colGroup = this._getColGroup( colSpan );
31859         // get the minimum Y value from the columns
31860         var minimumY = Math.min.apply( Math, colGroup );
31861         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31862         
31863         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31864          
31865         // position the brick
31866         var position = {
31867             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31868             y: this.currentSize.y + minimumY + this.padHeight
31869         };
31870         
31871         Roo.log(position);
31872         // apply setHeight to necessary columns
31873         var setHeight = minimumY + sz.height + this.padHeight;
31874         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31875         
31876         var setSpan = this.cols + 1 - colGroup.length;
31877         for ( var i = 0; i < setSpan; i++ ) {
31878           this.colYs[ shortColIndex + i ] = setHeight ;
31879         }
31880       
31881         return position;
31882     },
31883     
31884     /**
31885      * @param {Number} colSpan - number of columns the element spans
31886      * @returns {Array} colGroup
31887      */
31888     _getColGroup : function( colSpan )
31889     {
31890         if ( colSpan < 2 ) {
31891           // if brick spans only one column, use all the column Ys
31892           return this.colYs;
31893         }
31894       
31895         var colGroup = [];
31896         // how many different places could this brick fit horizontally
31897         var groupCount = this.cols + 1 - colSpan;
31898         // for each group potential horizontal position
31899         for ( var i = 0; i < groupCount; i++ ) {
31900           // make an array of colY values for that one group
31901           var groupColYs = this.colYs.slice( i, i + colSpan );
31902           // and get the max value of the array
31903           colGroup[i] = Math.max.apply( Math, groupColYs );
31904         }
31905         return colGroup;
31906     },
31907     /*
31908     _manageStamp : function( stamp )
31909     {
31910         var stampSize =  stamp.getSize();
31911         var offset = stamp.getBox();
31912         // get the columns that this stamp affects
31913         var firstX = this.isOriginLeft ? offset.x : offset.right;
31914         var lastX = firstX + stampSize.width;
31915         var firstCol = Math.floor( firstX / this.columnWidth );
31916         firstCol = Math.max( 0, firstCol );
31917         
31918         var lastCol = Math.floor( lastX / this.columnWidth );
31919         // lastCol should not go over if multiple of columnWidth #425
31920         lastCol -= lastX % this.columnWidth ? 0 : 1;
31921         lastCol = Math.min( this.cols - 1, lastCol );
31922         
31923         // set colYs to bottom of the stamp
31924         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31925             stampSize.height;
31926             
31927         for ( var i = firstCol; i <= lastCol; i++ ) {
31928           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31929         }
31930     },
31931     */
31932     
31933     _getContainerSize : function()
31934     {
31935         this.maxY = Math.max.apply( Math, this.colYs );
31936         var size = {
31937             height: this.maxY
31938         };
31939       
31940         if ( this.isFitWidth ) {
31941             size.width = this._getContainerFitWidth();
31942         }
31943       
31944         return size;
31945     },
31946     
31947     _getContainerFitWidth : function()
31948     {
31949         var unusedCols = 0;
31950         // count unused columns
31951         var i = this.cols;
31952         while ( --i ) {
31953           if ( this.colYs[i] !== 0 ) {
31954             break;
31955           }
31956           unusedCols++;
31957         }
31958         // fit container to columns that have been used
31959         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31960     },
31961     
31962     needsResizeLayout : function()
31963     {
31964         var previousWidth = this.containerWidth;
31965         this.getContainerWidth();
31966         return previousWidth !== this.containerWidth;
31967     }
31968  
31969 });
31970
31971  
31972
31973  /*
31974  * - LGPL
31975  *
31976  * element
31977  * 
31978  */
31979
31980 /**
31981  * @class Roo.bootstrap.MasonryBrick
31982  * @extends Roo.bootstrap.Component
31983  * Bootstrap MasonryBrick class
31984  * 
31985  * @constructor
31986  * Create a new MasonryBrick
31987  * @param {Object} config The config object
31988  */
31989
31990 Roo.bootstrap.MasonryBrick = function(config){
31991     
31992     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31993     
31994     Roo.bootstrap.MasonryBrick.register(this);
31995     
31996     this.addEvents({
31997         // raw events
31998         /**
31999          * @event click
32000          * When a MasonryBrick is clcik
32001          * @param {Roo.bootstrap.MasonryBrick} this
32002          * @param {Roo.EventObject} e
32003          */
32004         "click" : true
32005     });
32006 };
32007
32008 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32009     
32010     /**
32011      * @cfg {String} title
32012      */   
32013     title : '',
32014     /**
32015      * @cfg {String} html
32016      */   
32017     html : '',
32018     /**
32019      * @cfg {String} bgimage
32020      */   
32021     bgimage : '',
32022     /**
32023      * @cfg {String} videourl
32024      */   
32025     videourl : '',
32026     /**
32027      * @cfg {String} cls
32028      */   
32029     cls : '',
32030     /**
32031      * @cfg {String} href
32032      */   
32033     href : '',
32034     /**
32035      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32036      */   
32037     size : 'xs',
32038     
32039     /**
32040      * @cfg {String} placetitle (center|bottom)
32041      */   
32042     placetitle : '',
32043     
32044     /**
32045      * @cfg {Boolean} isFitContainer defalut true
32046      */   
32047     isFitContainer : true, 
32048     
32049     /**
32050      * @cfg {Boolean} preventDefault defalut false
32051      */   
32052     preventDefault : false, 
32053     
32054     /**
32055      * @cfg {Boolean} inverse defalut false
32056      */   
32057     maskInverse : false, 
32058     
32059     getAutoCreate : function()
32060     {
32061         if(!this.isFitContainer){
32062             return this.getSplitAutoCreate();
32063         }
32064         
32065         var cls = 'masonry-brick masonry-brick-full';
32066         
32067         if(this.href.length){
32068             cls += ' masonry-brick-link';
32069         }
32070         
32071         if(this.bgimage.length){
32072             cls += ' masonry-brick-image';
32073         }
32074         
32075         if(this.maskInverse){
32076             cls += ' mask-inverse';
32077         }
32078         
32079         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32080             cls += ' enable-mask';
32081         }
32082         
32083         if(this.size){
32084             cls += ' masonry-' + this.size + '-brick';
32085         }
32086         
32087         if(this.placetitle.length){
32088             
32089             switch (this.placetitle) {
32090                 case 'center' :
32091                     cls += ' masonry-center-title';
32092                     break;
32093                 case 'bottom' :
32094                     cls += ' masonry-bottom-title';
32095                     break;
32096                 default:
32097                     break;
32098             }
32099             
32100         } else {
32101             if(!this.html.length && !this.bgimage.length){
32102                 cls += ' masonry-center-title';
32103             }
32104
32105             if(!this.html.length && this.bgimage.length){
32106                 cls += ' masonry-bottom-title';
32107             }
32108         }
32109         
32110         if(this.cls){
32111             cls += ' ' + this.cls;
32112         }
32113         
32114         var cfg = {
32115             tag: (this.href.length) ? 'a' : 'div',
32116             cls: cls,
32117             cn: [
32118                 {
32119                     tag: 'div',
32120                     cls: 'masonry-brick-mask'
32121                 },
32122                 {
32123                     tag: 'div',
32124                     cls: 'masonry-brick-paragraph',
32125                     cn: []
32126                 }
32127             ]
32128         };
32129         
32130         if(this.href.length){
32131             cfg.href = this.href;
32132         }
32133         
32134         var cn = cfg.cn[1].cn;
32135         
32136         if(this.title.length){
32137             cn.push({
32138                 tag: 'h4',
32139                 cls: 'masonry-brick-title',
32140                 html: this.title
32141             });
32142         }
32143         
32144         if(this.html.length){
32145             cn.push({
32146                 tag: 'p',
32147                 cls: 'masonry-brick-text',
32148                 html: this.html
32149             });
32150         }
32151         
32152         if (!this.title.length && !this.html.length) {
32153             cfg.cn[1].cls += ' hide';
32154         }
32155         
32156         if(this.bgimage.length){
32157             cfg.cn.push({
32158                 tag: 'img',
32159                 cls: 'masonry-brick-image-view',
32160                 src: this.bgimage
32161             });
32162         }
32163         
32164         if(this.videourl.length){
32165             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32166             // youtube support only?
32167             cfg.cn.push({
32168                 tag: 'iframe',
32169                 cls: 'masonry-brick-image-view',
32170                 src: vurl,
32171                 frameborder : 0,
32172                 allowfullscreen : true
32173             });
32174         }
32175         
32176         return cfg;
32177         
32178     },
32179     
32180     getSplitAutoCreate : function()
32181     {
32182         var cls = 'masonry-brick masonry-brick-split';
32183         
32184         if(this.href.length){
32185             cls += ' masonry-brick-link';
32186         }
32187         
32188         if(this.bgimage.length){
32189             cls += ' masonry-brick-image';
32190         }
32191         
32192         if(this.size){
32193             cls += ' masonry-' + this.size + '-brick';
32194         }
32195         
32196         switch (this.placetitle) {
32197             case 'center' :
32198                 cls += ' masonry-center-title';
32199                 break;
32200             case 'bottom' :
32201                 cls += ' masonry-bottom-title';
32202                 break;
32203             default:
32204                 if(!this.bgimage.length){
32205                     cls += ' masonry-center-title';
32206                 }
32207
32208                 if(this.bgimage.length){
32209                     cls += ' masonry-bottom-title';
32210                 }
32211                 break;
32212         }
32213         
32214         if(this.cls){
32215             cls += ' ' + this.cls;
32216         }
32217         
32218         var cfg = {
32219             tag: (this.href.length) ? 'a' : 'div',
32220             cls: cls,
32221             cn: [
32222                 {
32223                     tag: 'div',
32224                     cls: 'masonry-brick-split-head',
32225                     cn: [
32226                         {
32227                             tag: 'div',
32228                             cls: 'masonry-brick-paragraph',
32229                             cn: []
32230                         }
32231                     ]
32232                 },
32233                 {
32234                     tag: 'div',
32235                     cls: 'masonry-brick-split-body',
32236                     cn: []
32237                 }
32238             ]
32239         };
32240         
32241         if(this.href.length){
32242             cfg.href = this.href;
32243         }
32244         
32245         if(this.title.length){
32246             cfg.cn[0].cn[0].cn.push({
32247                 tag: 'h4',
32248                 cls: 'masonry-brick-title',
32249                 html: this.title
32250             });
32251         }
32252         
32253         if(this.html.length){
32254             cfg.cn[1].cn.push({
32255                 tag: 'p',
32256                 cls: 'masonry-brick-text',
32257                 html: this.html
32258             });
32259         }
32260
32261         if(this.bgimage.length){
32262             cfg.cn[0].cn.push({
32263                 tag: 'img',
32264                 cls: 'masonry-brick-image-view',
32265                 src: this.bgimage
32266             });
32267         }
32268         
32269         if(this.videourl.length){
32270             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32271             // youtube support only?
32272             cfg.cn[0].cn.cn.push({
32273                 tag: 'iframe',
32274                 cls: 'masonry-brick-image-view',
32275                 src: vurl,
32276                 frameborder : 0,
32277                 allowfullscreen : true
32278             });
32279         }
32280         
32281         return cfg;
32282     },
32283     
32284     initEvents: function() 
32285     {
32286         switch (this.size) {
32287             case 'xs' :
32288                 this.x = 1;
32289                 this.y = 1;
32290                 break;
32291             case 'sm' :
32292                 this.x = 2;
32293                 this.y = 2;
32294                 break;
32295             case 'md' :
32296             case 'md-left' :
32297             case 'md-right' :
32298                 this.x = 3;
32299                 this.y = 3;
32300                 break;
32301             case 'tall' :
32302                 this.x = 2;
32303                 this.y = 3;
32304                 break;
32305             case 'wide' :
32306                 this.x = 3;
32307                 this.y = 2;
32308                 break;
32309             case 'wide-thin' :
32310                 this.x = 3;
32311                 this.y = 1;
32312                 break;
32313                         
32314             default :
32315                 break;
32316         }
32317         
32318         if(Roo.isTouch){
32319             this.el.on('touchstart', this.onTouchStart, this);
32320             this.el.on('touchmove', this.onTouchMove, this);
32321             this.el.on('touchend', this.onTouchEnd, this);
32322             this.el.on('contextmenu', this.onContextMenu, this);
32323         } else {
32324             this.el.on('mouseenter'  ,this.enter, this);
32325             this.el.on('mouseleave', this.leave, this);
32326             this.el.on('click', this.onClick, this);
32327         }
32328         
32329         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32330             this.parent().bricks.push(this);   
32331         }
32332         
32333     },
32334     
32335     onClick: function(e, el)
32336     {
32337         var time = this.endTimer - this.startTimer;
32338         // Roo.log(e.preventDefault());
32339         if(Roo.isTouch){
32340             if(time > 1000){
32341                 e.preventDefault();
32342                 return;
32343             }
32344         }
32345         
32346         if(!this.preventDefault){
32347             return;
32348         }
32349         
32350         e.preventDefault();
32351         
32352         if (this.activcClass != '') {
32353             this.selectBrick();
32354         }
32355         
32356         this.fireEvent('click', this);
32357     },
32358     
32359     enter: function(e, el)
32360     {
32361         e.preventDefault();
32362         
32363         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32364             return;
32365         }
32366         
32367         if(this.bgimage.length && this.html.length){
32368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32369         }
32370     },
32371     
32372     leave: function(e, el)
32373     {
32374         e.preventDefault();
32375         
32376         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32377             return;
32378         }
32379         
32380         if(this.bgimage.length && this.html.length){
32381             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32382         }
32383     },
32384     
32385     onTouchStart: function(e, el)
32386     {
32387 //        e.preventDefault();
32388         
32389         this.touchmoved = false;
32390         
32391         if(!this.isFitContainer){
32392             return;
32393         }
32394         
32395         if(!this.bgimage.length || !this.html.length){
32396             return;
32397         }
32398         
32399         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32400         
32401         this.timer = new Date().getTime();
32402         
32403     },
32404     
32405     onTouchMove: function(e, el)
32406     {
32407         this.touchmoved = true;
32408     },
32409     
32410     onContextMenu : function(e,el)
32411     {
32412         e.preventDefault();
32413         e.stopPropagation();
32414         return false;
32415     },
32416     
32417     onTouchEnd: function(e, el)
32418     {
32419 //        e.preventDefault();
32420         
32421         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32422         
32423             this.leave(e,el);
32424             
32425             return;
32426         }
32427         
32428         if(!this.bgimage.length || !this.html.length){
32429             
32430             if(this.href.length){
32431                 window.location.href = this.href;
32432             }
32433             
32434             return;
32435         }
32436         
32437         if(!this.isFitContainer){
32438             return;
32439         }
32440         
32441         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32442         
32443         window.location.href = this.href;
32444     },
32445     
32446     //selection on single brick only
32447     selectBrick : function() {
32448         
32449         if (!this.parentId) {
32450             return;
32451         }
32452         
32453         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32454         var index = m.selectedBrick.indexOf(this.id);
32455         
32456         if ( index > -1) {
32457             m.selectedBrick.splice(index,1);
32458             this.el.removeClass(this.activeClass);
32459             return;
32460         }
32461         
32462         for(var i = 0; i < m.selectedBrick.length; i++) {
32463             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32464             b.el.removeClass(b.activeClass);
32465         }
32466         
32467         m.selectedBrick = [];
32468         
32469         m.selectedBrick.push(this.id);
32470         this.el.addClass(this.activeClass);
32471         return;
32472     }
32473     
32474 });
32475
32476 Roo.apply(Roo.bootstrap.MasonryBrick, {
32477     
32478     //groups: {},
32479     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32480      /**
32481     * register a Masonry Brick
32482     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32483     */
32484     
32485     register : function(brick)
32486     {
32487         //this.groups[brick.id] = brick;
32488         this.groups.add(brick.id, brick);
32489     },
32490     /**
32491     * fetch a  masonry brick based on the masonry brick ID
32492     * @param {string} the masonry brick to add
32493     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32494     */
32495     
32496     get: function(brick_id) 
32497     {
32498         // if (typeof(this.groups[brick_id]) == 'undefined') {
32499         //     return false;
32500         // }
32501         // return this.groups[brick_id] ;
32502         
32503         if(this.groups.key(brick_id)) {
32504             return this.groups.key(brick_id);
32505         }
32506         
32507         return false;
32508     }
32509     
32510     
32511     
32512 });
32513
32514  /*
32515  * - LGPL
32516  *
32517  * element
32518  * 
32519  */
32520
32521 /**
32522  * @class Roo.bootstrap.Brick
32523  * @extends Roo.bootstrap.Component
32524  * Bootstrap Brick class
32525  * 
32526  * @constructor
32527  * Create a new Brick
32528  * @param {Object} config The config object
32529  */
32530
32531 Roo.bootstrap.Brick = function(config){
32532     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32533     
32534     this.addEvents({
32535         // raw events
32536         /**
32537          * @event click
32538          * When a Brick is click
32539          * @param {Roo.bootstrap.Brick} this
32540          * @param {Roo.EventObject} e
32541          */
32542         "click" : true
32543     });
32544 };
32545
32546 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32547     
32548     /**
32549      * @cfg {String} title
32550      */   
32551     title : '',
32552     /**
32553      * @cfg {String} html
32554      */   
32555     html : '',
32556     /**
32557      * @cfg {String} bgimage
32558      */   
32559     bgimage : '',
32560     /**
32561      * @cfg {String} cls
32562      */   
32563     cls : '',
32564     /**
32565      * @cfg {String} href
32566      */   
32567     href : '',
32568     /**
32569      * @cfg {String} video
32570      */   
32571     video : '',
32572     /**
32573      * @cfg {Boolean} square
32574      */   
32575     square : true,
32576     
32577     getAutoCreate : function()
32578     {
32579         var cls = 'roo-brick';
32580         
32581         if(this.href.length){
32582             cls += ' roo-brick-link';
32583         }
32584         
32585         if(this.bgimage.length){
32586             cls += ' roo-brick-image';
32587         }
32588         
32589         if(!this.html.length && !this.bgimage.length){
32590             cls += ' roo-brick-center-title';
32591         }
32592         
32593         if(!this.html.length && this.bgimage.length){
32594             cls += ' roo-brick-bottom-title';
32595         }
32596         
32597         if(this.cls){
32598             cls += ' ' + this.cls;
32599         }
32600         
32601         var cfg = {
32602             tag: (this.href.length) ? 'a' : 'div',
32603             cls: cls,
32604             cn: [
32605                 {
32606                     tag: 'div',
32607                     cls: 'roo-brick-paragraph',
32608                     cn: []
32609                 }
32610             ]
32611         };
32612         
32613         if(this.href.length){
32614             cfg.href = this.href;
32615         }
32616         
32617         var cn = cfg.cn[0].cn;
32618         
32619         if(this.title.length){
32620             cn.push({
32621                 tag: 'h4',
32622                 cls: 'roo-brick-title',
32623                 html: this.title
32624             });
32625         }
32626         
32627         if(this.html.length){
32628             cn.push({
32629                 tag: 'p',
32630                 cls: 'roo-brick-text',
32631                 html: this.html
32632             });
32633         } else {
32634             cn.cls += ' hide';
32635         }
32636         
32637         if(this.bgimage.length){
32638             cfg.cn.push({
32639                 tag: 'img',
32640                 cls: 'roo-brick-image-view',
32641                 src: this.bgimage
32642             });
32643         }
32644         
32645         return cfg;
32646     },
32647     
32648     initEvents: function() 
32649     {
32650         if(this.title.length || this.html.length){
32651             this.el.on('mouseenter'  ,this.enter, this);
32652             this.el.on('mouseleave', this.leave, this);
32653         }
32654         
32655         Roo.EventManager.onWindowResize(this.resize, this); 
32656         
32657         if(this.bgimage.length){
32658             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32659             this.imageEl.on('load', this.onImageLoad, this);
32660             return;
32661         }
32662         
32663         this.resize();
32664     },
32665     
32666     onImageLoad : function()
32667     {
32668         this.resize();
32669     },
32670     
32671     resize : function()
32672     {
32673         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32674         
32675         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32676         
32677         if(this.bgimage.length){
32678             var image = this.el.select('.roo-brick-image-view', true).first();
32679             
32680             image.setWidth(paragraph.getWidth());
32681             
32682             if(this.square){
32683                 image.setHeight(paragraph.getWidth());
32684             }
32685             
32686             this.el.setHeight(image.getHeight());
32687             paragraph.setHeight(image.getHeight());
32688             
32689         }
32690         
32691     },
32692     
32693     enter: function(e, el)
32694     {
32695         e.preventDefault();
32696         
32697         if(this.bgimage.length){
32698             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32699             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32700         }
32701     },
32702     
32703     leave: function(e, el)
32704     {
32705         e.preventDefault();
32706         
32707         if(this.bgimage.length){
32708             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32709             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32710         }
32711     }
32712     
32713 });
32714
32715  
32716
32717  /*
32718  * - LGPL
32719  *
32720  * Input
32721  * 
32722  */
32723
32724 /**
32725  * @class Roo.bootstrap.NumberField
32726  * @extends Roo.bootstrap.Input
32727  * Bootstrap NumberField class
32728  * 
32729  * 
32730  * 
32731  * 
32732  * @constructor
32733  * Create a new NumberField
32734  * @param {Object} config The config object
32735  */
32736
32737 Roo.bootstrap.NumberField = function(config){
32738     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32739 };
32740
32741 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32742     
32743     /**
32744      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32745      */
32746     allowDecimals : true,
32747     /**
32748      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32749      */
32750     decimalSeparator : ".",
32751     /**
32752      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32753      */
32754     decimalPrecision : 2,
32755     /**
32756      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32757      */
32758     allowNegative : true,
32759     /**
32760      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32761      */
32762     minValue : Number.NEGATIVE_INFINITY,
32763     /**
32764      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32765      */
32766     maxValue : Number.MAX_VALUE,
32767     /**
32768      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32769      */
32770     minText : "The minimum value for this field is {0}",
32771     /**
32772      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32773      */
32774     maxText : "The maximum value for this field is {0}",
32775     /**
32776      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32777      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32778      */
32779     nanText : "{0} is not a valid number",
32780     /**
32781      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32782      */
32783     castInt : true,
32784
32785     // private
32786     initEvents : function()
32787     {   
32788         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32789         
32790         var allowed = "0123456789";
32791         
32792         if(this.allowDecimals){
32793             allowed += this.decimalSeparator;
32794         }
32795         
32796         if(this.allowNegative){
32797             allowed += "-";
32798         }
32799         
32800         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32801         
32802         var keyPress = function(e){
32803             
32804             var k = e.getKey();
32805             
32806             var c = e.getCharCode();
32807             
32808             if(
32809                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32810                     allowed.indexOf(String.fromCharCode(c)) === -1
32811             ){
32812                 e.stopEvent();
32813                 return;
32814             }
32815             
32816             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32817                 return;
32818             }
32819             
32820             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32821                 e.stopEvent();
32822             }
32823         };
32824         
32825         this.el.on("keypress", keyPress, this);
32826     },
32827     
32828     validateValue : function(value)
32829     {
32830         
32831         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32832             return false;
32833         }
32834         
32835         var num = this.parseValue(value);
32836         
32837         if(isNaN(num)){
32838             this.markInvalid(String.format(this.nanText, value));
32839             return false;
32840         }
32841         
32842         if(num < this.minValue){
32843             this.markInvalid(String.format(this.minText, this.minValue));
32844             return false;
32845         }
32846         
32847         if(num > this.maxValue){
32848             this.markInvalid(String.format(this.maxText, this.maxValue));
32849             return false;
32850         }
32851         
32852         return true;
32853     },
32854
32855     getValue : function()
32856     {
32857         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32858     },
32859
32860     parseValue : function(value)
32861     {
32862         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32863         return isNaN(value) ? '' : value;
32864     },
32865
32866     fixPrecision : function(value)
32867     {
32868         var nan = isNaN(value);
32869         
32870         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32871             return nan ? '' : value;
32872         }
32873         return parseFloat(value).toFixed(this.decimalPrecision);
32874     },
32875
32876     setValue : function(v)
32877     {
32878         v = this.fixPrecision(v);
32879         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32880     },
32881
32882     decimalPrecisionFcn : function(v)
32883     {
32884         return Math.floor(v);
32885     },
32886
32887     beforeBlur : function()
32888     {
32889         if(!this.castInt){
32890             return;
32891         }
32892         
32893         var v = this.parseValue(this.getRawValue());
32894         if(v){
32895             this.setValue(v);
32896         }
32897     }
32898     
32899 });
32900
32901  
32902
32903 /*
32904 * Licence: LGPL
32905 */
32906
32907 /**
32908  * @class Roo.bootstrap.DocumentSlider
32909  * @extends Roo.bootstrap.Component
32910  * Bootstrap DocumentSlider class
32911  * 
32912  * @constructor
32913  * Create a new DocumentViewer
32914  * @param {Object} config The config object
32915  */
32916
32917 Roo.bootstrap.DocumentSlider = function(config){
32918     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32919     
32920     this.files = [];
32921     
32922     this.addEvents({
32923         /**
32924          * @event initial
32925          * Fire after initEvent
32926          * @param {Roo.bootstrap.DocumentSlider} this
32927          */
32928         "initial" : true,
32929         /**
32930          * @event update
32931          * Fire after update
32932          * @param {Roo.bootstrap.DocumentSlider} this
32933          */
32934         "update" : true,
32935         /**
32936          * @event click
32937          * Fire after click
32938          * @param {Roo.bootstrap.DocumentSlider} this
32939          */
32940         "click" : true
32941     });
32942 };
32943
32944 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32945     
32946     files : false,
32947     
32948     indicator : 0,
32949     
32950     getAutoCreate : function()
32951     {
32952         var cfg = {
32953             tag : 'div',
32954             cls : 'roo-document-slider',
32955             cn : [
32956                 {
32957                     tag : 'div',
32958                     cls : 'roo-document-slider-header',
32959                     cn : [
32960                         {
32961                             tag : 'div',
32962                             cls : 'roo-document-slider-header-title'
32963                         }
32964                     ]
32965                 },
32966                 {
32967                     tag : 'div',
32968                     cls : 'roo-document-slider-body',
32969                     cn : [
32970                         {
32971                             tag : 'div',
32972                             cls : 'roo-document-slider-prev',
32973                             cn : [
32974                                 {
32975                                     tag : 'i',
32976                                     cls : 'fa fa-chevron-left'
32977                                 }
32978                             ]
32979                         },
32980                         {
32981                             tag : 'div',
32982                             cls : 'roo-document-slider-thumb',
32983                             cn : [
32984                                 {
32985                                     tag : 'img',
32986                                     cls : 'roo-document-slider-image'
32987                                 }
32988                             ]
32989                         },
32990                         {
32991                             tag : 'div',
32992                             cls : 'roo-document-slider-next',
32993                             cn : [
32994                                 {
32995                                     tag : 'i',
32996                                     cls : 'fa fa-chevron-right'
32997                                 }
32998                             ]
32999                         }
33000                     ]
33001                 }
33002             ]
33003         };
33004         
33005         return cfg;
33006     },
33007     
33008     initEvents : function()
33009     {
33010         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33011         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33012         
33013         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33014         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33015         
33016         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33017         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33018         
33019         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33020         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33021         
33022         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33023         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33024         
33025         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33026         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33027         
33028         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33029         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33030         
33031         this.thumbEl.on('click', this.onClick, this);
33032         
33033         this.prevIndicator.on('click', this.prev, this);
33034         
33035         this.nextIndicator.on('click', this.next, this);
33036         
33037     },
33038     
33039     initial : function()
33040     {
33041         if(this.files.length){
33042             this.indicator = 1;
33043             this.update()
33044         }
33045         
33046         this.fireEvent('initial', this);
33047     },
33048     
33049     update : function()
33050     {
33051         this.imageEl.attr('src', this.files[this.indicator - 1]);
33052         
33053         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33054         
33055         this.prevIndicator.show();
33056         
33057         if(this.indicator == 1){
33058             this.prevIndicator.hide();
33059         }
33060         
33061         this.nextIndicator.show();
33062         
33063         if(this.indicator == this.files.length){
33064             this.nextIndicator.hide();
33065         }
33066         
33067         this.thumbEl.scrollTo('top');
33068         
33069         this.fireEvent('update', this);
33070     },
33071     
33072     onClick : function(e)
33073     {
33074         e.preventDefault();
33075         
33076         this.fireEvent('click', this);
33077     },
33078     
33079     prev : function(e)
33080     {
33081         e.preventDefault();
33082         
33083         this.indicator = Math.max(1, this.indicator - 1);
33084         
33085         this.update();
33086     },
33087     
33088     next : function(e)
33089     {
33090         e.preventDefault();
33091         
33092         this.indicator = Math.min(this.files.length, this.indicator + 1);
33093         
33094         this.update();
33095     }
33096 });
33097 /*
33098  * - LGPL
33099  *
33100  * RadioSet
33101  *
33102  *
33103  */
33104
33105 /**
33106  * @class Roo.bootstrap.RadioSet
33107  * @extends Roo.bootstrap.Input
33108  * Bootstrap RadioSet class
33109  * @cfg {String} indicatorpos (left|right) default left
33110  * @cfg {Boolean} inline (true|false) inline the element (default true)
33111  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33112  * @constructor
33113  * Create a new RadioSet
33114  * @param {Object} config The config object
33115  */
33116
33117 Roo.bootstrap.RadioSet = function(config){
33118     
33119     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33120     
33121     this.radioes = [];
33122     
33123     Roo.bootstrap.RadioSet.register(this);
33124     
33125     this.addEvents({
33126         /**
33127         * @event check
33128         * Fires when the element is checked or unchecked.
33129         * @param {Roo.bootstrap.RadioSet} this This radio
33130         * @param {Roo.bootstrap.Radio} item The checked item
33131         */
33132        check : true
33133     });
33134     
33135 };
33136
33137 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33138
33139     radioes : false,
33140     
33141     inline : true,
33142     
33143     weight : '',
33144     
33145     indicatorpos : 'left',
33146     
33147     getAutoCreate : function()
33148     {
33149         var label = {
33150             tag : 'label',
33151             cls : 'roo-radio-set-label',
33152             cn : [
33153                 {
33154                     tag : 'span',
33155                     html : this.fieldLabel
33156                 }
33157             ]
33158         };
33159         
33160         if(this.indicatorpos == 'left'){
33161             label.cn.unshift({
33162                 tag : 'i',
33163                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33164                 tooltip : 'This field is required'
33165             });
33166         } else {
33167             label.cn.push({
33168                 tag : 'i',
33169                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33170                 tooltip : 'This field is required'
33171             });
33172         }
33173         
33174         var items = {
33175             tag : 'div',
33176             cls : 'roo-radio-set-items'
33177         };
33178         
33179         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33180         
33181         if (align === 'left' && this.fieldLabel.length) {
33182             
33183             items = {
33184                 cls : "roo-radio-set-right", 
33185                 cn: [
33186                     items
33187                 ]
33188             };
33189             
33190             if(this.labelWidth > 12){
33191                 label.style = "width: " + this.labelWidth + 'px';
33192             }
33193             
33194             if(this.labelWidth < 13 && this.labelmd == 0){
33195                 this.labelmd = this.labelWidth;
33196             }
33197             
33198             if(this.labellg > 0){
33199                 label.cls += ' col-lg-' + this.labellg;
33200                 items.cls += ' col-lg-' + (12 - this.labellg);
33201             }
33202             
33203             if(this.labelmd > 0){
33204                 label.cls += ' col-md-' + this.labelmd;
33205                 items.cls += ' col-md-' + (12 - this.labelmd);
33206             }
33207             
33208             if(this.labelsm > 0){
33209                 label.cls += ' col-sm-' + this.labelsm;
33210                 items.cls += ' col-sm-' + (12 - this.labelsm);
33211             }
33212             
33213             if(this.labelxs > 0){
33214                 label.cls += ' col-xs-' + this.labelxs;
33215                 items.cls += ' col-xs-' + (12 - this.labelxs);
33216             }
33217         }
33218         
33219         var cfg = {
33220             tag : 'div',
33221             cls : 'roo-radio-set',
33222             cn : [
33223                 {
33224                     tag : 'input',
33225                     cls : 'roo-radio-set-input',
33226                     type : 'hidden',
33227                     name : this.name,
33228                     value : this.value ? this.value :  ''
33229                 },
33230                 label,
33231                 items
33232             ]
33233         };
33234         
33235         if(this.weight.length){
33236             cfg.cls += ' roo-radio-' + this.weight;
33237         }
33238         
33239         if(this.inline) {
33240             cfg.cls += ' roo-radio-set-inline';
33241         }
33242         
33243         return cfg;
33244         
33245     },
33246
33247     initEvents : function()
33248     {
33249         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33250         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33251         
33252         if(!this.fieldLabel.length){
33253             this.labelEl.hide();
33254         }
33255         
33256         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33257         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33260         this.indicatorEl().hide();
33261         
33262         this.originalValue = this.getValue();
33263         
33264     },
33265     
33266     inputEl: function ()
33267     {
33268         return this.el.select('.roo-radio-set-input', true).first();
33269     },
33270     
33271     getChildContainer : function()
33272     {
33273         return this.itemsEl;
33274     },
33275     
33276     register : function(item)
33277     {
33278         this.radioes.push(item);
33279         
33280     },
33281     
33282     validate : function()
33283     {   
33284         var valid = false;
33285         
33286         Roo.each(this.radioes, function(i){
33287             if(!i.checked){
33288                 return;
33289             }
33290             
33291             valid = true;
33292             return false;
33293         });
33294         
33295         if(this.allowBlank) {
33296             return true;
33297         }
33298         
33299         if(this.disabled || valid){
33300             this.markValid();
33301             return true;
33302         }
33303         
33304         this.markInvalid();
33305         return false;
33306         
33307     },
33308     
33309     markValid : function()
33310     {
33311         if(this.labelEl.isVisible(true)){
33312             this.indicatorEl().hide();
33313         }
33314         
33315         this.el.removeClass([this.invalidClass, this.validClass]);
33316         this.el.addClass(this.validClass);
33317         
33318         this.fireEvent('valid', this);
33319     },
33320     
33321     markInvalid : function(msg)
33322     {
33323         if(this.allowBlank || this.disabled){
33324             return;
33325         }
33326         
33327         if(this.labelEl.isVisible(true)){
33328             this.indicatorEl().show();
33329         }
33330         
33331         this.el.removeClass([this.invalidClass, this.validClass]);
33332         this.el.addClass(this.invalidClass);
33333         
33334         this.fireEvent('invalid', this, msg);
33335         
33336     },
33337     
33338     setValue : function(v, suppressEvent)
33339     {   
33340         this.value = v;
33341         if(this.rendered){
33342             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33343         }
33344         
33345         Roo.each(this.radioes, function(i){
33346             
33347             i.checked = false;
33348             i.el.removeClass('checked');
33349             
33350             if(i.value === v || i.value.toString() === v.toString()){
33351                 i.checked = true;
33352                 i.el.addClass('checked');
33353                 
33354                 if(suppressEvent !== true){
33355                     this.fireEvent('check', this, i);
33356                 }
33357             }
33358             
33359         }, this);
33360         
33361         this.validate();
33362     },
33363     
33364     clearInvalid : function(){
33365         
33366         if(!this.el || this.preventMark){
33367             return;
33368         }
33369         
33370         this.el.removeClass([this.invalidClass]);
33371         
33372         this.fireEvent('valid', this);
33373     }
33374     
33375 });
33376
33377 Roo.apply(Roo.bootstrap.RadioSet, {
33378     
33379     groups: {},
33380     
33381     register : function(set)
33382     {
33383         this.groups[set.name] = set;
33384     },
33385     
33386     get: function(name) 
33387     {
33388         if (typeof(this.groups[name]) == 'undefined') {
33389             return false;
33390         }
33391         
33392         return this.groups[name] ;
33393     }
33394     
33395 });
33396 /*
33397  * Based on:
33398  * Ext JS Library 1.1.1
33399  * Copyright(c) 2006-2007, Ext JS, LLC.
33400  *
33401  * Originally Released Under LGPL - original licence link has changed is not relivant.
33402  *
33403  * Fork - LGPL
33404  * <script type="text/javascript">
33405  */
33406
33407
33408 /**
33409  * @class Roo.bootstrap.SplitBar
33410  * @extends Roo.util.Observable
33411  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33412  * <br><br>
33413  * Usage:
33414  * <pre><code>
33415 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33416                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33417 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33418 split.minSize = 100;
33419 split.maxSize = 600;
33420 split.animate = true;
33421 split.on('moved', splitterMoved);
33422 </code></pre>
33423  * @constructor
33424  * Create a new SplitBar
33425  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33426  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33427  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33428  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33429                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33430                         position of the SplitBar).
33431  */
33432 Roo.bootstrap.SplitBar = function(cfg){
33433     
33434     /** @private */
33435     
33436     //{
33437     //  dragElement : elm
33438     //  resizingElement: el,
33439         // optional..
33440     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33441     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33442         // existingProxy ???
33443     //}
33444     
33445     this.el = Roo.get(cfg.dragElement, true);
33446     this.el.dom.unselectable = "on";
33447     /** @private */
33448     this.resizingEl = Roo.get(cfg.resizingElement, true);
33449
33450     /**
33451      * @private
33452      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33453      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33454      * @type Number
33455      */
33456     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33457     
33458     /**
33459      * The minimum size of the resizing element. (Defaults to 0)
33460      * @type Number
33461      */
33462     this.minSize = 0;
33463     
33464     /**
33465      * The maximum size of the resizing element. (Defaults to 2000)
33466      * @type Number
33467      */
33468     this.maxSize = 2000;
33469     
33470     /**
33471      * Whether to animate the transition to the new size
33472      * @type Boolean
33473      */
33474     this.animate = false;
33475     
33476     /**
33477      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33478      * @type Boolean
33479      */
33480     this.useShim = false;
33481     
33482     /** @private */
33483     this.shim = null;
33484     
33485     if(!cfg.existingProxy){
33486         /** @private */
33487         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33488     }else{
33489         this.proxy = Roo.get(cfg.existingProxy).dom;
33490     }
33491     /** @private */
33492     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33493     
33494     /** @private */
33495     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33496     
33497     /** @private */
33498     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33499     
33500     /** @private */
33501     this.dragSpecs = {};
33502     
33503     /**
33504      * @private The adapter to use to positon and resize elements
33505      */
33506     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33507     this.adapter.init(this);
33508     
33509     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33510         /** @private */
33511         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33512         this.el.addClass("roo-splitbar-h");
33513     }else{
33514         /** @private */
33515         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33516         this.el.addClass("roo-splitbar-v");
33517     }
33518     
33519     this.addEvents({
33520         /**
33521          * @event resize
33522          * Fires when the splitter is moved (alias for {@link #event-moved})
33523          * @param {Roo.bootstrap.SplitBar} this
33524          * @param {Number} newSize the new width or height
33525          */
33526         "resize" : true,
33527         /**
33528          * @event moved
33529          * Fires when the splitter is moved
33530          * @param {Roo.bootstrap.SplitBar} this
33531          * @param {Number} newSize the new width or height
33532          */
33533         "moved" : true,
33534         /**
33535          * @event beforeresize
33536          * Fires before the splitter is dragged
33537          * @param {Roo.bootstrap.SplitBar} this
33538          */
33539         "beforeresize" : true,
33540
33541         "beforeapply" : true
33542     });
33543
33544     Roo.util.Observable.call(this);
33545 };
33546
33547 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33548     onStartProxyDrag : function(x, y){
33549         this.fireEvent("beforeresize", this);
33550         if(!this.overlay){
33551             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33552             o.unselectable();
33553             o.enableDisplayMode("block");
33554             // all splitbars share the same overlay
33555             Roo.bootstrap.SplitBar.prototype.overlay = o;
33556         }
33557         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33558         this.overlay.show();
33559         Roo.get(this.proxy).setDisplayed("block");
33560         var size = this.adapter.getElementSize(this);
33561         this.activeMinSize = this.getMinimumSize();;
33562         this.activeMaxSize = this.getMaximumSize();;
33563         var c1 = size - this.activeMinSize;
33564         var c2 = Math.max(this.activeMaxSize - size, 0);
33565         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33566             this.dd.resetConstraints();
33567             this.dd.setXConstraint(
33568                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33569                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33570             );
33571             this.dd.setYConstraint(0, 0);
33572         }else{
33573             this.dd.resetConstraints();
33574             this.dd.setXConstraint(0, 0);
33575             this.dd.setYConstraint(
33576                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33577                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33578             );
33579          }
33580         this.dragSpecs.startSize = size;
33581         this.dragSpecs.startPoint = [x, y];
33582         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33583     },
33584     
33585     /** 
33586      * @private Called after the drag operation by the DDProxy
33587      */
33588     onEndProxyDrag : function(e){
33589         Roo.get(this.proxy).setDisplayed(false);
33590         var endPoint = Roo.lib.Event.getXY(e);
33591         if(this.overlay){
33592             this.overlay.hide();
33593         }
33594         var newSize;
33595         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33596             newSize = this.dragSpecs.startSize + 
33597                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33598                     endPoint[0] - this.dragSpecs.startPoint[0] :
33599                     this.dragSpecs.startPoint[0] - endPoint[0]
33600                 );
33601         }else{
33602             newSize = this.dragSpecs.startSize + 
33603                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33604                     endPoint[1] - this.dragSpecs.startPoint[1] :
33605                     this.dragSpecs.startPoint[1] - endPoint[1]
33606                 );
33607         }
33608         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33609         if(newSize != this.dragSpecs.startSize){
33610             if(this.fireEvent('beforeapply', this, newSize) !== false){
33611                 this.adapter.setElementSize(this, newSize);
33612                 this.fireEvent("moved", this, newSize);
33613                 this.fireEvent("resize", this, newSize);
33614             }
33615         }
33616     },
33617     
33618     /**
33619      * Get the adapter this SplitBar uses
33620      * @return The adapter object
33621      */
33622     getAdapter : function(){
33623         return this.adapter;
33624     },
33625     
33626     /**
33627      * Set the adapter this SplitBar uses
33628      * @param {Object} adapter A SplitBar adapter object
33629      */
33630     setAdapter : function(adapter){
33631         this.adapter = adapter;
33632         this.adapter.init(this);
33633     },
33634     
33635     /**
33636      * Gets the minimum size for the resizing element
33637      * @return {Number} The minimum size
33638      */
33639     getMinimumSize : function(){
33640         return this.minSize;
33641     },
33642     
33643     /**
33644      * Sets the minimum size for the resizing element
33645      * @param {Number} minSize The minimum size
33646      */
33647     setMinimumSize : function(minSize){
33648         this.minSize = minSize;
33649     },
33650     
33651     /**
33652      * Gets the maximum size for the resizing element
33653      * @return {Number} The maximum size
33654      */
33655     getMaximumSize : function(){
33656         return this.maxSize;
33657     },
33658     
33659     /**
33660      * Sets the maximum size for the resizing element
33661      * @param {Number} maxSize The maximum size
33662      */
33663     setMaximumSize : function(maxSize){
33664         this.maxSize = maxSize;
33665     },
33666     
33667     /**
33668      * Sets the initialize size for the resizing element
33669      * @param {Number} size The initial size
33670      */
33671     setCurrentSize : function(size){
33672         var oldAnimate = this.animate;
33673         this.animate = false;
33674         this.adapter.setElementSize(this, size);
33675         this.animate = oldAnimate;
33676     },
33677     
33678     /**
33679      * Destroy this splitbar. 
33680      * @param {Boolean} removeEl True to remove the element
33681      */
33682     destroy : function(removeEl){
33683         if(this.shim){
33684             this.shim.remove();
33685         }
33686         this.dd.unreg();
33687         this.proxy.parentNode.removeChild(this.proxy);
33688         if(removeEl){
33689             this.el.remove();
33690         }
33691     }
33692 });
33693
33694 /**
33695  * @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.
33696  */
33697 Roo.bootstrap.SplitBar.createProxy = function(dir){
33698     var proxy = new Roo.Element(document.createElement("div"));
33699     proxy.unselectable();
33700     var cls = 'roo-splitbar-proxy';
33701     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33702     document.body.appendChild(proxy.dom);
33703     return proxy.dom;
33704 };
33705
33706 /** 
33707  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33708  * Default Adapter. It assumes the splitter and resizing element are not positioned
33709  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33710  */
33711 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33712 };
33713
33714 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33715     // do nothing for now
33716     init : function(s){
33717     
33718     },
33719     /**
33720      * Called before drag operations to get the current size of the resizing element. 
33721      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33722      */
33723      getElementSize : function(s){
33724         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33725             return s.resizingEl.getWidth();
33726         }else{
33727             return s.resizingEl.getHeight();
33728         }
33729     },
33730     
33731     /**
33732      * Called after drag operations to set the size of the resizing element.
33733      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33734      * @param {Number} newSize The new size to set
33735      * @param {Function} onComplete A function to be invoked when resizing is complete
33736      */
33737     setElementSize : function(s, newSize, onComplete){
33738         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33739             if(!s.animate){
33740                 s.resizingEl.setWidth(newSize);
33741                 if(onComplete){
33742                     onComplete(s, newSize);
33743                 }
33744             }else{
33745                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33746             }
33747         }else{
33748             
33749             if(!s.animate){
33750                 s.resizingEl.setHeight(newSize);
33751                 if(onComplete){
33752                     onComplete(s, newSize);
33753                 }
33754             }else{
33755                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33756             }
33757         }
33758     }
33759 };
33760
33761 /** 
33762  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33763  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33764  * Adapter that  moves the splitter element to align with the resized sizing element. 
33765  * Used with an absolute positioned SplitBar.
33766  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33767  * document.body, make sure you assign an id to the body element.
33768  */
33769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33770     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33771     this.container = Roo.get(container);
33772 };
33773
33774 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33775     init : function(s){
33776         this.basic.init(s);
33777     },
33778     
33779     getElementSize : function(s){
33780         return this.basic.getElementSize(s);
33781     },
33782     
33783     setElementSize : function(s, newSize, onComplete){
33784         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33785     },
33786     
33787     moveSplitter : function(s){
33788         var yes = Roo.bootstrap.SplitBar;
33789         switch(s.placement){
33790             case yes.LEFT:
33791                 s.el.setX(s.resizingEl.getRight());
33792                 break;
33793             case yes.RIGHT:
33794                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33795                 break;
33796             case yes.TOP:
33797                 s.el.setY(s.resizingEl.getBottom());
33798                 break;
33799             case yes.BOTTOM:
33800                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33801                 break;
33802         }
33803     }
33804 };
33805
33806 /**
33807  * Orientation constant - Create a vertical SplitBar
33808  * @static
33809  * @type Number
33810  */
33811 Roo.bootstrap.SplitBar.VERTICAL = 1;
33812
33813 /**
33814  * Orientation constant - Create a horizontal SplitBar
33815  * @static
33816  * @type Number
33817  */
33818 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33819
33820 /**
33821  * Placement constant - The resizing element is to the left of the splitter element
33822  * @static
33823  * @type Number
33824  */
33825 Roo.bootstrap.SplitBar.LEFT = 1;
33826
33827 /**
33828  * Placement constant - The resizing element is to the right of the splitter element
33829  * @static
33830  * @type Number
33831  */
33832 Roo.bootstrap.SplitBar.RIGHT = 2;
33833
33834 /**
33835  * Placement constant - The resizing element is positioned above the splitter element
33836  * @static
33837  * @type Number
33838  */
33839 Roo.bootstrap.SplitBar.TOP = 3;
33840
33841 /**
33842  * Placement constant - The resizing element is positioned under splitter element
33843  * @static
33844  * @type Number
33845  */
33846 Roo.bootstrap.SplitBar.BOTTOM = 4;
33847 Roo.namespace("Roo.bootstrap.layout");/*
33848  * Based on:
33849  * Ext JS Library 1.1.1
33850  * Copyright(c) 2006-2007, Ext JS, LLC.
33851  *
33852  * Originally Released Under LGPL - original licence link has changed is not relivant.
33853  *
33854  * Fork - LGPL
33855  * <script type="text/javascript">
33856  */
33857
33858 /**
33859  * @class Roo.bootstrap.layout.Manager
33860  * @extends Roo.bootstrap.Component
33861  * Base class for layout managers.
33862  */
33863 Roo.bootstrap.layout.Manager = function(config)
33864 {
33865     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33866
33867
33868
33869
33870
33871     /** false to disable window resize monitoring @type Boolean */
33872     this.monitorWindowResize = true;
33873     this.regions = {};
33874     this.addEvents({
33875         /**
33876          * @event layout
33877          * Fires when a layout is performed.
33878          * @param {Roo.LayoutManager} this
33879          */
33880         "layout" : true,
33881         /**
33882          * @event regionresized
33883          * Fires when the user resizes a region.
33884          * @param {Roo.LayoutRegion} region The resized region
33885          * @param {Number} newSize The new size (width for east/west, height for north/south)
33886          */
33887         "regionresized" : true,
33888         /**
33889          * @event regioncollapsed
33890          * Fires when a region is collapsed.
33891          * @param {Roo.LayoutRegion} region The collapsed region
33892          */
33893         "regioncollapsed" : true,
33894         /**
33895          * @event regionexpanded
33896          * Fires when a region is expanded.
33897          * @param {Roo.LayoutRegion} region The expanded region
33898          */
33899         "regionexpanded" : true
33900     });
33901     this.updating = false;
33902
33903     if (config.el) {
33904         this.el = Roo.get(config.el);
33905         this.initEvents();
33906     }
33907
33908 };
33909
33910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33911
33912
33913     regions : null,
33914
33915     monitorWindowResize : true,
33916
33917
33918     updating : false,
33919
33920
33921     onRender : function(ct, position)
33922     {
33923         if(!this.el){
33924             this.el = Roo.get(ct);
33925             this.initEvents();
33926         }
33927         //this.fireEvent('render',this);
33928     },
33929
33930
33931     initEvents: function()
33932     {
33933
33934
33935         // ie scrollbar fix
33936         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33937             document.body.scroll = "no";
33938         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33939             this.el.position('relative');
33940         }
33941         this.id = this.el.id;
33942         this.el.addClass("roo-layout-container");
33943         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33944         if(this.el.dom != document.body ) {
33945             this.el.on('resize', this.layout,this);
33946             this.el.on('show', this.layout,this);
33947         }
33948
33949     },
33950
33951     /**
33952      * Returns true if this layout is currently being updated
33953      * @return {Boolean}
33954      */
33955     isUpdating : function(){
33956         return this.updating;
33957     },
33958
33959     /**
33960      * Suspend the LayoutManager from doing auto-layouts while
33961      * making multiple add or remove calls
33962      */
33963     beginUpdate : function(){
33964         this.updating = true;
33965     },
33966
33967     /**
33968      * Restore auto-layouts and optionally disable the manager from performing a layout
33969      * @param {Boolean} noLayout true to disable a layout update
33970      */
33971     endUpdate : function(noLayout){
33972         this.updating = false;
33973         if(!noLayout){
33974             this.layout();
33975         }
33976     },
33977
33978     layout: function(){
33979         // abstract...
33980     },
33981
33982     onRegionResized : function(region, newSize){
33983         this.fireEvent("regionresized", region, newSize);
33984         this.layout();
33985     },
33986
33987     onRegionCollapsed : function(region){
33988         this.fireEvent("regioncollapsed", region);
33989     },
33990
33991     onRegionExpanded : function(region){
33992         this.fireEvent("regionexpanded", region);
33993     },
33994
33995     /**
33996      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33997      * performs box-model adjustments.
33998      * @return {Object} The size as an object {width: (the width), height: (the height)}
33999      */
34000     getViewSize : function()
34001     {
34002         var size;
34003         if(this.el.dom != document.body){
34004             size = this.el.getSize();
34005         }else{
34006             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34007         }
34008         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34009         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34010         return size;
34011     },
34012
34013     /**
34014      * Returns the Element this layout is bound to.
34015      * @return {Roo.Element}
34016      */
34017     getEl : function(){
34018         return this.el;
34019     },
34020
34021     /**
34022      * Returns the specified region.
34023      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34024      * @return {Roo.LayoutRegion}
34025      */
34026     getRegion : function(target){
34027         return this.regions[target.toLowerCase()];
34028     },
34029
34030     onWindowResize : function(){
34031         if(this.monitorWindowResize){
34032             this.layout();
34033         }
34034     }
34035 });
34036 /*
34037  * Based on:
34038  * Ext JS Library 1.1.1
34039  * Copyright(c) 2006-2007, Ext JS, LLC.
34040  *
34041  * Originally Released Under LGPL - original licence link has changed is not relivant.
34042  *
34043  * Fork - LGPL
34044  * <script type="text/javascript">
34045  */
34046 /**
34047  * @class Roo.bootstrap.layout.Border
34048  * @extends Roo.bootstrap.layout.Manager
34049  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34050  * please see: examples/bootstrap/nested.html<br><br>
34051  
34052 <b>The container the layout is rendered into can be either the body element or any other element.
34053 If it is not the body element, the container needs to either be an absolute positioned element,
34054 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34055 the container size if it is not the body element.</b>
34056
34057 * @constructor
34058 * Create a new Border
34059 * @param {Object} config Configuration options
34060  */
34061 Roo.bootstrap.layout.Border = function(config){
34062     config = config || {};
34063     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34064     
34065     
34066     
34067     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34068         if(config[region]){
34069             config[region].region = region;
34070             this.addRegion(config[region]);
34071         }
34072     },this);
34073     
34074 };
34075
34076 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34077
34078 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34079     /**
34080      * Creates and adds a new region if it doesn't already exist.
34081      * @param {String} target The target region key (north, south, east, west or center).
34082      * @param {Object} config The regions config object
34083      * @return {BorderLayoutRegion} The new region
34084      */
34085     addRegion : function(config)
34086     {
34087         if(!this.regions[config.region]){
34088             var r = this.factory(config);
34089             this.bindRegion(r);
34090         }
34091         return this.regions[config.region];
34092     },
34093
34094     // private (kinda)
34095     bindRegion : function(r){
34096         this.regions[r.config.region] = r;
34097         
34098         r.on("visibilitychange",    this.layout, this);
34099         r.on("paneladded",          this.layout, this);
34100         r.on("panelremoved",        this.layout, this);
34101         r.on("invalidated",         this.layout, this);
34102         r.on("resized",             this.onRegionResized, this);
34103         r.on("collapsed",           this.onRegionCollapsed, this);
34104         r.on("expanded",            this.onRegionExpanded, this);
34105     },
34106
34107     /**
34108      * Performs a layout update.
34109      */
34110     layout : function()
34111     {
34112         if(this.updating) {
34113             return;
34114         }
34115         
34116         // render all the rebions if they have not been done alreayd?
34117         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34118             if(this.regions[region] && !this.regions[region].bodyEl){
34119                 this.regions[region].onRender(this.el)
34120             }
34121         },this);
34122         
34123         var size = this.getViewSize();
34124         var w = size.width;
34125         var h = size.height;
34126         var centerW = w;
34127         var centerH = h;
34128         var centerY = 0;
34129         var centerX = 0;
34130         //var x = 0, y = 0;
34131
34132         var rs = this.regions;
34133         var north = rs["north"];
34134         var south = rs["south"]; 
34135         var west = rs["west"];
34136         var east = rs["east"];
34137         var center = rs["center"];
34138         //if(this.hideOnLayout){ // not supported anymore
34139             //c.el.setStyle("display", "none");
34140         //}
34141         if(north && north.isVisible()){
34142             var b = north.getBox();
34143             var m = north.getMargins();
34144             b.width = w - (m.left+m.right);
34145             b.x = m.left;
34146             b.y = m.top;
34147             centerY = b.height + b.y + m.bottom;
34148             centerH -= centerY;
34149             north.updateBox(this.safeBox(b));
34150         }
34151         if(south && south.isVisible()){
34152             var b = south.getBox();
34153             var m = south.getMargins();
34154             b.width = w - (m.left+m.right);
34155             b.x = m.left;
34156             var totalHeight = (b.height + m.top + m.bottom);
34157             b.y = h - totalHeight + m.top;
34158             centerH -= totalHeight;
34159             south.updateBox(this.safeBox(b));
34160         }
34161         if(west && west.isVisible()){
34162             var b = west.getBox();
34163             var m = west.getMargins();
34164             b.height = centerH - (m.top+m.bottom);
34165             b.x = m.left;
34166             b.y = centerY + m.top;
34167             var totalWidth = (b.width + m.left + m.right);
34168             centerX += totalWidth;
34169             centerW -= totalWidth;
34170             west.updateBox(this.safeBox(b));
34171         }
34172         if(east && east.isVisible()){
34173             var b = east.getBox();
34174             var m = east.getMargins();
34175             b.height = centerH - (m.top+m.bottom);
34176             var totalWidth = (b.width + m.left + m.right);
34177             b.x = w - totalWidth + m.left;
34178             b.y = centerY + m.top;
34179             centerW -= totalWidth;
34180             east.updateBox(this.safeBox(b));
34181         }
34182         if(center){
34183             var m = center.getMargins();
34184             var centerBox = {
34185                 x: centerX + m.left,
34186                 y: centerY + m.top,
34187                 width: centerW - (m.left+m.right),
34188                 height: centerH - (m.top+m.bottom)
34189             };
34190             //if(this.hideOnLayout){
34191                 //center.el.setStyle("display", "block");
34192             //}
34193             center.updateBox(this.safeBox(centerBox));
34194         }
34195         this.el.repaint();
34196         this.fireEvent("layout", this);
34197     },
34198
34199     // private
34200     safeBox : function(box){
34201         box.width = Math.max(0, box.width);
34202         box.height = Math.max(0, box.height);
34203         return box;
34204     },
34205
34206     /**
34207      * Adds a ContentPanel (or subclass) to this layout.
34208      * @param {String} target The target region key (north, south, east, west or center).
34209      * @param {Roo.ContentPanel} panel The panel to add
34210      * @return {Roo.ContentPanel} The added panel
34211      */
34212     add : function(target, panel){
34213          
34214         target = target.toLowerCase();
34215         return this.regions[target].add(panel);
34216     },
34217
34218     /**
34219      * Remove a ContentPanel (or subclass) to this layout.
34220      * @param {String} target The target region key (north, south, east, west or center).
34221      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34222      * @return {Roo.ContentPanel} The removed panel
34223      */
34224     remove : function(target, panel){
34225         target = target.toLowerCase();
34226         return this.regions[target].remove(panel);
34227     },
34228
34229     /**
34230      * Searches all regions for a panel with the specified id
34231      * @param {String} panelId
34232      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34233      */
34234     findPanel : function(panelId){
34235         var rs = this.regions;
34236         for(var target in rs){
34237             if(typeof rs[target] != "function"){
34238                 var p = rs[target].getPanel(panelId);
34239                 if(p){
34240                     return p;
34241                 }
34242             }
34243         }
34244         return null;
34245     },
34246
34247     /**
34248      * Searches all regions for a panel with the specified id and activates (shows) it.
34249      * @param {String/ContentPanel} panelId The panels id or the panel itself
34250      * @return {Roo.ContentPanel} The shown panel or null
34251      */
34252     showPanel : function(panelId) {
34253       var rs = this.regions;
34254       for(var target in rs){
34255          var r = rs[target];
34256          if(typeof r != "function"){
34257             if(r.hasPanel(panelId)){
34258                return r.showPanel(panelId);
34259             }
34260          }
34261       }
34262       return null;
34263    },
34264
34265    /**
34266      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34267      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34268      */
34269    /*
34270     restoreState : function(provider){
34271         if(!provider){
34272             provider = Roo.state.Manager;
34273         }
34274         var sm = new Roo.LayoutStateManager();
34275         sm.init(this, provider);
34276     },
34277 */
34278  
34279  
34280     /**
34281      * Adds a xtype elements to the layout.
34282      * <pre><code>
34283
34284 layout.addxtype({
34285        xtype : 'ContentPanel',
34286        region: 'west',
34287        items: [ .... ]
34288    }
34289 );
34290
34291 layout.addxtype({
34292         xtype : 'NestedLayoutPanel',
34293         region: 'west',
34294         layout: {
34295            center: { },
34296            west: { }   
34297         },
34298         items : [ ... list of content panels or nested layout panels.. ]
34299    }
34300 );
34301 </code></pre>
34302      * @param {Object} cfg Xtype definition of item to add.
34303      */
34304     addxtype : function(cfg)
34305     {
34306         // basically accepts a pannel...
34307         // can accept a layout region..!?!?
34308         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34309         
34310         
34311         // theory?  children can only be panels??
34312         
34313         //if (!cfg.xtype.match(/Panel$/)) {
34314         //    return false;
34315         //}
34316         var ret = false;
34317         
34318         if (typeof(cfg.region) == 'undefined') {
34319             Roo.log("Failed to add Panel, region was not set");
34320             Roo.log(cfg);
34321             return false;
34322         }
34323         var region = cfg.region;
34324         delete cfg.region;
34325         
34326           
34327         var xitems = [];
34328         if (cfg.items) {
34329             xitems = cfg.items;
34330             delete cfg.items;
34331         }
34332         var nb = false;
34333         
34334         switch(cfg.xtype) 
34335         {
34336             case 'Content':  // ContentPanel (el, cfg)
34337             case 'Scroll':  // ContentPanel (el, cfg)
34338             case 'View': 
34339                 cfg.autoCreate = true;
34340                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34341                 //} else {
34342                 //    var el = this.el.createChild();
34343                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34344                 //}
34345                 
34346                 this.add(region, ret);
34347                 break;
34348             
34349             /*
34350             case 'TreePanel': // our new panel!
34351                 cfg.el = this.el.createChild();
34352                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34353                 this.add(region, ret);
34354                 break;
34355             */
34356             
34357             case 'Nest': 
34358                 // create a new Layout (which is  a Border Layout...
34359                 
34360                 var clayout = cfg.layout;
34361                 clayout.el  = this.el.createChild();
34362                 clayout.items   = clayout.items  || [];
34363                 
34364                 delete cfg.layout;
34365                 
34366                 // replace this exitems with the clayout ones..
34367                 xitems = clayout.items;
34368                  
34369                 // force background off if it's in center...
34370                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34371                     cfg.background = false;
34372                 }
34373                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34374                 
34375                 
34376                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34377                 //console.log('adding nested layout panel '  + cfg.toSource());
34378                 this.add(region, ret);
34379                 nb = {}; /// find first...
34380                 break;
34381             
34382             case 'Grid':
34383                 
34384                 // needs grid and region
34385                 
34386                 //var el = this.getRegion(region).el.createChild();
34387                 /*
34388                  *var el = this.el.createChild();
34389                 // create the grid first...
34390                 cfg.grid.container = el;
34391                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34392                 */
34393                 
34394                 if (region == 'center' && this.active ) {
34395                     cfg.background = false;
34396                 }
34397                 
34398                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34399                 
34400                 this.add(region, ret);
34401                 /*
34402                 if (cfg.background) {
34403                     // render grid on panel activation (if panel background)
34404                     ret.on('activate', function(gp) {
34405                         if (!gp.grid.rendered) {
34406                     //        gp.grid.render(el);
34407                         }
34408                     });
34409                 } else {
34410                   //  cfg.grid.render(el);
34411                 }
34412                 */
34413                 break;
34414            
34415            
34416             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34417                 // it was the old xcomponent building that caused this before.
34418                 // espeically if border is the top element in the tree.
34419                 ret = this;
34420                 break; 
34421                 
34422                     
34423                 
34424                 
34425                 
34426             default:
34427                 /*
34428                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34429                     
34430                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34431                     this.add(region, ret);
34432                 } else {
34433                 */
34434                     Roo.log(cfg);
34435                     throw "Can not add '" + cfg.xtype + "' to Border";
34436                     return null;
34437              
34438                                 
34439              
34440         }
34441         this.beginUpdate();
34442         // add children..
34443         var region = '';
34444         var abn = {};
34445         Roo.each(xitems, function(i)  {
34446             region = nb && i.region ? i.region : false;
34447             
34448             var add = ret.addxtype(i);
34449            
34450             if (region) {
34451                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34452                 if (!i.background) {
34453                     abn[region] = nb[region] ;
34454                 }
34455             }
34456             
34457         });
34458         this.endUpdate();
34459
34460         // make the last non-background panel active..
34461         //if (nb) { Roo.log(abn); }
34462         if (nb) {
34463             
34464             for(var r in abn) {
34465                 region = this.getRegion(r);
34466                 if (region) {
34467                     // tried using nb[r], but it does not work..
34468                      
34469                     region.showPanel(abn[r]);
34470                    
34471                 }
34472             }
34473         }
34474         return ret;
34475         
34476     },
34477     
34478     
34479 // private
34480     factory : function(cfg)
34481     {
34482         
34483         var validRegions = Roo.bootstrap.layout.Border.regions;
34484
34485         var target = cfg.region;
34486         cfg.mgr = this;
34487         
34488         var r = Roo.bootstrap.layout;
34489         Roo.log(target);
34490         switch(target){
34491             case "north":
34492                 return new r.North(cfg);
34493             case "south":
34494                 return new r.South(cfg);
34495             case "east":
34496                 return new r.East(cfg);
34497             case "west":
34498                 return new r.West(cfg);
34499             case "center":
34500                 return new r.Center(cfg);
34501         }
34502         throw 'Layout region "'+target+'" not supported.';
34503     }
34504     
34505     
34506 });
34507  /*
34508  * Based on:
34509  * Ext JS Library 1.1.1
34510  * Copyright(c) 2006-2007, Ext JS, LLC.
34511  *
34512  * Originally Released Under LGPL - original licence link has changed is not relivant.
34513  *
34514  * Fork - LGPL
34515  * <script type="text/javascript">
34516  */
34517  
34518 /**
34519  * @class Roo.bootstrap.layout.Basic
34520  * @extends Roo.util.Observable
34521  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34522  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34523  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34524  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34525  * @cfg {string}   region  the region that it inhabits..
34526  * @cfg {bool}   skipConfig skip config?
34527  * 
34528
34529  */
34530 Roo.bootstrap.layout.Basic = function(config){
34531     
34532     this.mgr = config.mgr;
34533     
34534     this.position = config.region;
34535     
34536     var skipConfig = config.skipConfig;
34537     
34538     this.events = {
34539         /**
34540          * @scope Roo.BasicLayoutRegion
34541          */
34542         
34543         /**
34544          * @event beforeremove
34545          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34546          * @param {Roo.LayoutRegion} this
34547          * @param {Roo.ContentPanel} panel The panel
34548          * @param {Object} e The cancel event object
34549          */
34550         "beforeremove" : true,
34551         /**
34552          * @event invalidated
34553          * Fires when the layout for this region is changed.
34554          * @param {Roo.LayoutRegion} this
34555          */
34556         "invalidated" : true,
34557         /**
34558          * @event visibilitychange
34559          * Fires when this region is shown or hidden 
34560          * @param {Roo.LayoutRegion} this
34561          * @param {Boolean} visibility true or false
34562          */
34563         "visibilitychange" : true,
34564         /**
34565          * @event paneladded
34566          * Fires when a panel is added. 
34567          * @param {Roo.LayoutRegion} this
34568          * @param {Roo.ContentPanel} panel The panel
34569          */
34570         "paneladded" : true,
34571         /**
34572          * @event panelremoved
34573          * Fires when a panel is removed. 
34574          * @param {Roo.LayoutRegion} this
34575          * @param {Roo.ContentPanel} panel The panel
34576          */
34577         "panelremoved" : true,
34578         /**
34579          * @event beforecollapse
34580          * Fires when this region before collapse.
34581          * @param {Roo.LayoutRegion} this
34582          */
34583         "beforecollapse" : true,
34584         /**
34585          * @event collapsed
34586          * Fires when this region is collapsed.
34587          * @param {Roo.LayoutRegion} this
34588          */
34589         "collapsed" : true,
34590         /**
34591          * @event expanded
34592          * Fires when this region is expanded.
34593          * @param {Roo.LayoutRegion} this
34594          */
34595         "expanded" : true,
34596         /**
34597          * @event slideshow
34598          * Fires when this region is slid into view.
34599          * @param {Roo.LayoutRegion} this
34600          */
34601         "slideshow" : true,
34602         /**
34603          * @event slidehide
34604          * Fires when this region slides out of view. 
34605          * @param {Roo.LayoutRegion} this
34606          */
34607         "slidehide" : true,
34608         /**
34609          * @event panelactivated
34610          * Fires when a panel is activated. 
34611          * @param {Roo.LayoutRegion} this
34612          * @param {Roo.ContentPanel} panel The activated panel
34613          */
34614         "panelactivated" : true,
34615         /**
34616          * @event resized
34617          * Fires when the user resizes this region. 
34618          * @param {Roo.LayoutRegion} this
34619          * @param {Number} newSize The new size (width for east/west, height for north/south)
34620          */
34621         "resized" : true
34622     };
34623     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34624     this.panels = new Roo.util.MixedCollection();
34625     this.panels.getKey = this.getPanelId.createDelegate(this);
34626     this.box = null;
34627     this.activePanel = null;
34628     // ensure listeners are added...
34629     
34630     if (config.listeners || config.events) {
34631         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34632             listeners : config.listeners || {},
34633             events : config.events || {}
34634         });
34635     }
34636     
34637     if(skipConfig !== true){
34638         this.applyConfig(config);
34639     }
34640 };
34641
34642 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34643 {
34644     getPanelId : function(p){
34645         return p.getId();
34646     },
34647     
34648     applyConfig : function(config){
34649         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34650         this.config = config;
34651         
34652     },
34653     
34654     /**
34655      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34656      * the width, for horizontal (north, south) the height.
34657      * @param {Number} newSize The new width or height
34658      */
34659     resizeTo : function(newSize){
34660         var el = this.el ? this.el :
34661                  (this.activePanel ? this.activePanel.getEl() : null);
34662         if(el){
34663             switch(this.position){
34664                 case "east":
34665                 case "west":
34666                     el.setWidth(newSize);
34667                     this.fireEvent("resized", this, newSize);
34668                 break;
34669                 case "north":
34670                 case "south":
34671                     el.setHeight(newSize);
34672                     this.fireEvent("resized", this, newSize);
34673                 break;                
34674             }
34675         }
34676     },
34677     
34678     getBox : function(){
34679         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34680     },
34681     
34682     getMargins : function(){
34683         return this.margins;
34684     },
34685     
34686     updateBox : function(box){
34687         this.box = box;
34688         var el = this.activePanel.getEl();
34689         el.dom.style.left = box.x + "px";
34690         el.dom.style.top = box.y + "px";
34691         this.activePanel.setSize(box.width, box.height);
34692     },
34693     
34694     /**
34695      * Returns the container element for this region.
34696      * @return {Roo.Element}
34697      */
34698     getEl : function(){
34699         return this.activePanel;
34700     },
34701     
34702     /**
34703      * Returns true if this region is currently visible.
34704      * @return {Boolean}
34705      */
34706     isVisible : function(){
34707         return this.activePanel ? true : false;
34708     },
34709     
34710     setActivePanel : function(panel){
34711         panel = this.getPanel(panel);
34712         if(this.activePanel && this.activePanel != panel){
34713             this.activePanel.setActiveState(false);
34714             this.activePanel.getEl().setLeftTop(-10000,-10000);
34715         }
34716         this.activePanel = panel;
34717         panel.setActiveState(true);
34718         if(this.box){
34719             panel.setSize(this.box.width, this.box.height);
34720         }
34721         this.fireEvent("panelactivated", this, panel);
34722         this.fireEvent("invalidated");
34723     },
34724     
34725     /**
34726      * Show the specified panel.
34727      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34728      * @return {Roo.ContentPanel} The shown panel or null
34729      */
34730     showPanel : function(panel){
34731         panel = this.getPanel(panel);
34732         if(panel){
34733             this.setActivePanel(panel);
34734         }
34735         return panel;
34736     },
34737     
34738     /**
34739      * Get the active panel for this region.
34740      * @return {Roo.ContentPanel} The active panel or null
34741      */
34742     getActivePanel : function(){
34743         return this.activePanel;
34744     },
34745     
34746     /**
34747      * Add the passed ContentPanel(s)
34748      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34749      * @return {Roo.ContentPanel} The panel added (if only one was added)
34750      */
34751     add : function(panel){
34752         if(arguments.length > 1){
34753             for(var i = 0, len = arguments.length; i < len; i++) {
34754                 this.add(arguments[i]);
34755             }
34756             return null;
34757         }
34758         if(this.hasPanel(panel)){
34759             this.showPanel(panel);
34760             return panel;
34761         }
34762         var el = panel.getEl();
34763         if(el.dom.parentNode != this.mgr.el.dom){
34764             this.mgr.el.dom.appendChild(el.dom);
34765         }
34766         if(panel.setRegion){
34767             panel.setRegion(this);
34768         }
34769         this.panels.add(panel);
34770         el.setStyle("position", "absolute");
34771         if(!panel.background){
34772             this.setActivePanel(panel);
34773             if(this.config.initialSize && this.panels.getCount()==1){
34774                 this.resizeTo(this.config.initialSize);
34775             }
34776         }
34777         this.fireEvent("paneladded", this, panel);
34778         return panel;
34779     },
34780     
34781     /**
34782      * Returns true if the panel is in this region.
34783      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34784      * @return {Boolean}
34785      */
34786     hasPanel : function(panel){
34787         if(typeof panel == "object"){ // must be panel obj
34788             panel = panel.getId();
34789         }
34790         return this.getPanel(panel) ? true : false;
34791     },
34792     
34793     /**
34794      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34795      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34796      * @param {Boolean} preservePanel Overrides the config preservePanel option
34797      * @return {Roo.ContentPanel} The panel that was removed
34798      */
34799     remove : function(panel, preservePanel){
34800         panel = this.getPanel(panel);
34801         if(!panel){
34802             return null;
34803         }
34804         var e = {};
34805         this.fireEvent("beforeremove", this, panel, e);
34806         if(e.cancel === true){
34807             return null;
34808         }
34809         var panelId = panel.getId();
34810         this.panels.removeKey(panelId);
34811         return panel;
34812     },
34813     
34814     /**
34815      * Returns the panel specified or null if it's not in this region.
34816      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34817      * @return {Roo.ContentPanel}
34818      */
34819     getPanel : function(id){
34820         if(typeof id == "object"){ // must be panel obj
34821             return id;
34822         }
34823         return this.panels.get(id);
34824     },
34825     
34826     /**
34827      * Returns this regions position (north/south/east/west/center).
34828      * @return {String} 
34829      */
34830     getPosition: function(){
34831         return this.position;    
34832     }
34833 });/*
34834  * Based on:
34835  * Ext JS Library 1.1.1
34836  * Copyright(c) 2006-2007, Ext JS, LLC.
34837  *
34838  * Originally Released Under LGPL - original licence link has changed is not relivant.
34839  *
34840  * Fork - LGPL
34841  * <script type="text/javascript">
34842  */
34843  
34844 /**
34845  * @class Roo.bootstrap.layout.Region
34846  * @extends Roo.bootstrap.layout.Basic
34847  * This class represents a region in a layout manager.
34848  
34849  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34850  * @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})
34851  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34852  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34853  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34854  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34855  * @cfg {String}    title           The title for the region (overrides panel titles)
34856  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34857  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34858  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34859  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34860  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34861  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34862  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34863  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34864  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34865  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34866
34867  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34868  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34869  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34870  * @cfg {Number}    width           For East/West panels
34871  * @cfg {Number}    height          For North/South panels
34872  * @cfg {Boolean}   split           To show the splitter
34873  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34874  * 
34875  * @cfg {string}   cls             Extra CSS classes to add to region
34876  * 
34877  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34878  * @cfg {string}   region  the region that it inhabits..
34879  *
34880
34881  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34882  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34883
34884  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34885  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34886  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34887  */
34888 Roo.bootstrap.layout.Region = function(config)
34889 {
34890     this.applyConfig(config);
34891
34892     var mgr = config.mgr;
34893     var pos = config.region;
34894     config.skipConfig = true;
34895     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34896     
34897     if (mgr.el) {
34898         this.onRender(mgr.el);   
34899     }
34900      
34901     this.visible = true;
34902     this.collapsed = false;
34903     this.unrendered_panels = [];
34904 };
34905
34906 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34907
34908     position: '', // set by wrapper (eg. north/south etc..)
34909     unrendered_panels : null,  // unrendered panels.
34910     createBody : function(){
34911         /** This region's body element 
34912         * @type Roo.Element */
34913         this.bodyEl = this.el.createChild({
34914                 tag: "div",
34915                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34916         });
34917     },
34918
34919     onRender: function(ctr, pos)
34920     {
34921         var dh = Roo.DomHelper;
34922         /** This region's container element 
34923         * @type Roo.Element */
34924         this.el = dh.append(ctr.dom, {
34925                 tag: "div",
34926                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34927             }, true);
34928         /** This region's title element 
34929         * @type Roo.Element */
34930     
34931         this.titleEl = dh.append(this.el.dom,
34932             {
34933                     tag: "div",
34934                     unselectable: "on",
34935                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34936                     children:[
34937                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34938                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34939                     ]}, true);
34940         
34941         this.titleEl.enableDisplayMode();
34942         /** This region's title text element 
34943         * @type HTMLElement */
34944         this.titleTextEl = this.titleEl.dom.firstChild;
34945         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34946         /*
34947         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34948         this.closeBtn.enableDisplayMode();
34949         this.closeBtn.on("click", this.closeClicked, this);
34950         this.closeBtn.hide();
34951     */
34952         this.createBody(this.config);
34953         if(this.config.hideWhenEmpty){
34954             this.hide();
34955             this.on("paneladded", this.validateVisibility, this);
34956             this.on("panelremoved", this.validateVisibility, this);
34957         }
34958         if(this.autoScroll){
34959             this.bodyEl.setStyle("overflow", "auto");
34960         }else{
34961             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34962         }
34963         //if(c.titlebar !== false){
34964             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34965                 this.titleEl.hide();
34966             }else{
34967                 this.titleEl.show();
34968                 if(this.config.title){
34969                     this.titleTextEl.innerHTML = this.config.title;
34970                 }
34971             }
34972         //}
34973         if(this.config.collapsed){
34974             this.collapse(true);
34975         }
34976         if(this.config.hidden){
34977             this.hide();
34978         }
34979         
34980         if (this.unrendered_panels && this.unrendered_panels.length) {
34981             for (var i =0;i< this.unrendered_panels.length; i++) {
34982                 this.add(this.unrendered_panels[i]);
34983             }
34984             this.unrendered_panels = null;
34985             
34986         }
34987         
34988     },
34989     
34990     applyConfig : function(c)
34991     {
34992         /*
34993          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34994             var dh = Roo.DomHelper;
34995             if(c.titlebar !== false){
34996                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34997                 this.collapseBtn.on("click", this.collapse, this);
34998                 this.collapseBtn.enableDisplayMode();
34999                 /*
35000                 if(c.showPin === true || this.showPin){
35001                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35002                     this.stickBtn.enableDisplayMode();
35003                     this.stickBtn.on("click", this.expand, this);
35004                     this.stickBtn.hide();
35005                 }
35006                 
35007             }
35008             */
35009             /** This region's collapsed element
35010             * @type Roo.Element */
35011             /*
35012              *
35013             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35014                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35015             ]}, true);
35016             
35017             if(c.floatable !== false){
35018                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35019                this.collapsedEl.on("click", this.collapseClick, this);
35020             }
35021
35022             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35023                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35024                    id: "message", unselectable: "on", style:{"float":"left"}});
35025                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35026              }
35027             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35028             this.expandBtn.on("click", this.expand, this);
35029             
35030         }
35031         
35032         if(this.collapseBtn){
35033             this.collapseBtn.setVisible(c.collapsible == true);
35034         }
35035         
35036         this.cmargins = c.cmargins || this.cmargins ||
35037                          (this.position == "west" || this.position == "east" ?
35038                              {top: 0, left: 2, right:2, bottom: 0} :
35039                              {top: 2, left: 0, right:0, bottom: 2});
35040         */
35041         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35042         
35043         
35044         this.bottomTabs = c.tabPosition != "top";
35045         
35046         this.autoScroll = c.autoScroll || false;
35047         
35048         
35049        
35050         
35051         this.duration = c.duration || .30;
35052         this.slideDuration = c.slideDuration || .45;
35053         this.config = c;
35054        
35055     },
35056     /**
35057      * Returns true if this region is currently visible.
35058      * @return {Boolean}
35059      */
35060     isVisible : function(){
35061         return this.visible;
35062     },
35063
35064     /**
35065      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35066      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35067      */
35068     //setCollapsedTitle : function(title){
35069     //    title = title || "&#160;";
35070      //   if(this.collapsedTitleTextEl){
35071       //      this.collapsedTitleTextEl.innerHTML = title;
35072        // }
35073     //},
35074
35075     getBox : function(){
35076         var b;
35077       //  if(!this.collapsed){
35078             b = this.el.getBox(false, true);
35079        // }else{
35080           //  b = this.collapsedEl.getBox(false, true);
35081         //}
35082         return b;
35083     },
35084
35085     getMargins : function(){
35086         return this.margins;
35087         //return this.collapsed ? this.cmargins : this.margins;
35088     },
35089 /*
35090     highlight : function(){
35091         this.el.addClass("x-layout-panel-dragover");
35092     },
35093
35094     unhighlight : function(){
35095         this.el.removeClass("x-layout-panel-dragover");
35096     },
35097 */
35098     updateBox : function(box)
35099     {
35100         if (!this.bodyEl) {
35101             return; // not rendered yet..
35102         }
35103         
35104         this.box = box;
35105         if(!this.collapsed){
35106             this.el.dom.style.left = box.x + "px";
35107             this.el.dom.style.top = box.y + "px";
35108             this.updateBody(box.width, box.height);
35109         }else{
35110             this.collapsedEl.dom.style.left = box.x + "px";
35111             this.collapsedEl.dom.style.top = box.y + "px";
35112             this.collapsedEl.setSize(box.width, box.height);
35113         }
35114         if(this.tabs){
35115             this.tabs.autoSizeTabs();
35116         }
35117     },
35118
35119     updateBody : function(w, h)
35120     {
35121         if(w !== null){
35122             this.el.setWidth(w);
35123             w -= this.el.getBorderWidth("rl");
35124             if(this.config.adjustments){
35125                 w += this.config.adjustments[0];
35126             }
35127         }
35128         if(h !== null && h > 0){
35129             this.el.setHeight(h);
35130             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35131             h -= this.el.getBorderWidth("tb");
35132             if(this.config.adjustments){
35133                 h += this.config.adjustments[1];
35134             }
35135             this.bodyEl.setHeight(h);
35136             if(this.tabs){
35137                 h = this.tabs.syncHeight(h);
35138             }
35139         }
35140         if(this.panelSize){
35141             w = w !== null ? w : this.panelSize.width;
35142             h = h !== null ? h : this.panelSize.height;
35143         }
35144         if(this.activePanel){
35145             var el = this.activePanel.getEl();
35146             w = w !== null ? w : el.getWidth();
35147             h = h !== null ? h : el.getHeight();
35148             this.panelSize = {width: w, height: h};
35149             this.activePanel.setSize(w, h);
35150         }
35151         if(Roo.isIE && this.tabs){
35152             this.tabs.el.repaint();
35153         }
35154     },
35155
35156     /**
35157      * Returns the container element for this region.
35158      * @return {Roo.Element}
35159      */
35160     getEl : function(){
35161         return this.el;
35162     },
35163
35164     /**
35165      * Hides this region.
35166      */
35167     hide : function(){
35168         //if(!this.collapsed){
35169             this.el.dom.style.left = "-2000px";
35170             this.el.hide();
35171         //}else{
35172          //   this.collapsedEl.dom.style.left = "-2000px";
35173          //   this.collapsedEl.hide();
35174        // }
35175         this.visible = false;
35176         this.fireEvent("visibilitychange", this, false);
35177     },
35178
35179     /**
35180      * Shows this region if it was previously hidden.
35181      */
35182     show : function(){
35183         //if(!this.collapsed){
35184             this.el.show();
35185         //}else{
35186         //    this.collapsedEl.show();
35187        // }
35188         this.visible = true;
35189         this.fireEvent("visibilitychange", this, true);
35190     },
35191 /*
35192     closeClicked : function(){
35193         if(this.activePanel){
35194             this.remove(this.activePanel);
35195         }
35196     },
35197
35198     collapseClick : function(e){
35199         if(this.isSlid){
35200            e.stopPropagation();
35201            this.slideIn();
35202         }else{
35203            e.stopPropagation();
35204            this.slideOut();
35205         }
35206     },
35207 */
35208     /**
35209      * Collapses this region.
35210      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35211      */
35212     /*
35213     collapse : function(skipAnim, skipCheck = false){
35214         if(this.collapsed) {
35215             return;
35216         }
35217         
35218         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35219             
35220             this.collapsed = true;
35221             if(this.split){
35222                 this.split.el.hide();
35223             }
35224             if(this.config.animate && skipAnim !== true){
35225                 this.fireEvent("invalidated", this);
35226                 this.animateCollapse();
35227             }else{
35228                 this.el.setLocation(-20000,-20000);
35229                 this.el.hide();
35230                 this.collapsedEl.show();
35231                 this.fireEvent("collapsed", this);
35232                 this.fireEvent("invalidated", this);
35233             }
35234         }
35235         
35236     },
35237 */
35238     animateCollapse : function(){
35239         // overridden
35240     },
35241
35242     /**
35243      * Expands this region if it was previously collapsed.
35244      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35245      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35246      */
35247     /*
35248     expand : function(e, skipAnim){
35249         if(e) {
35250             e.stopPropagation();
35251         }
35252         if(!this.collapsed || this.el.hasActiveFx()) {
35253             return;
35254         }
35255         if(this.isSlid){
35256             this.afterSlideIn();
35257             skipAnim = true;
35258         }
35259         this.collapsed = false;
35260         if(this.config.animate && skipAnim !== true){
35261             this.animateExpand();
35262         }else{
35263             this.el.show();
35264             if(this.split){
35265                 this.split.el.show();
35266             }
35267             this.collapsedEl.setLocation(-2000,-2000);
35268             this.collapsedEl.hide();
35269             this.fireEvent("invalidated", this);
35270             this.fireEvent("expanded", this);
35271         }
35272     },
35273 */
35274     animateExpand : function(){
35275         // overridden
35276     },
35277
35278     initTabs : function()
35279     {
35280         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35281         
35282         var ts = new Roo.bootstrap.panel.Tabs({
35283                 el: this.bodyEl.dom,
35284                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35285                 disableTooltips: this.config.disableTabTips,
35286                 toolbar : this.config.toolbar
35287             });
35288         
35289         if(this.config.hideTabs){
35290             ts.stripWrap.setDisplayed(false);
35291         }
35292         this.tabs = ts;
35293         ts.resizeTabs = this.config.resizeTabs === true;
35294         ts.minTabWidth = this.config.minTabWidth || 40;
35295         ts.maxTabWidth = this.config.maxTabWidth || 250;
35296         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35297         ts.monitorResize = false;
35298         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35299         ts.bodyEl.addClass('roo-layout-tabs-body');
35300         this.panels.each(this.initPanelAsTab, this);
35301     },
35302
35303     initPanelAsTab : function(panel){
35304         var ti = this.tabs.addTab(
35305             panel.getEl().id,
35306             panel.getTitle(),
35307             null,
35308             this.config.closeOnTab && panel.isClosable(),
35309             panel.tpl
35310         );
35311         if(panel.tabTip !== undefined){
35312             ti.setTooltip(panel.tabTip);
35313         }
35314         ti.on("activate", function(){
35315               this.setActivePanel(panel);
35316         }, this);
35317         
35318         if(this.config.closeOnTab){
35319             ti.on("beforeclose", function(t, e){
35320                 e.cancel = true;
35321                 this.remove(panel);
35322             }, this);
35323         }
35324         
35325         panel.tabItem = ti;
35326         
35327         return ti;
35328     },
35329
35330     updatePanelTitle : function(panel, title)
35331     {
35332         if(this.activePanel == panel){
35333             this.updateTitle(title);
35334         }
35335         if(this.tabs){
35336             var ti = this.tabs.getTab(panel.getEl().id);
35337             ti.setText(title);
35338             if(panel.tabTip !== undefined){
35339                 ti.setTooltip(panel.tabTip);
35340             }
35341         }
35342     },
35343
35344     updateTitle : function(title){
35345         if(this.titleTextEl && !this.config.title){
35346             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35347         }
35348     },
35349
35350     setActivePanel : function(panel)
35351     {
35352         panel = this.getPanel(panel);
35353         if(this.activePanel && this.activePanel != panel){
35354             this.activePanel.setActiveState(false);
35355         }
35356         this.activePanel = panel;
35357         panel.setActiveState(true);
35358         if(this.panelSize){
35359             panel.setSize(this.panelSize.width, this.panelSize.height);
35360         }
35361         if(this.closeBtn){
35362             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35363         }
35364         this.updateTitle(panel.getTitle());
35365         if(this.tabs){
35366             this.fireEvent("invalidated", this);
35367         }
35368         this.fireEvent("panelactivated", this, panel);
35369     },
35370
35371     /**
35372      * Shows the specified panel.
35373      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35374      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35375      */
35376     showPanel : function(panel)
35377     {
35378         panel = this.getPanel(panel);
35379         if(panel){
35380             if(this.tabs){
35381                 var tab = this.tabs.getTab(panel.getEl().id);
35382                 if(tab.isHidden()){
35383                     this.tabs.unhideTab(tab.id);
35384                 }
35385                 tab.activate();
35386             }else{
35387                 this.setActivePanel(panel);
35388             }
35389         }
35390         return panel;
35391     },
35392
35393     /**
35394      * Get the active panel for this region.
35395      * @return {Roo.ContentPanel} The active panel or null
35396      */
35397     getActivePanel : function(){
35398         return this.activePanel;
35399     },
35400
35401     validateVisibility : function(){
35402         if(this.panels.getCount() < 1){
35403             this.updateTitle("&#160;");
35404             this.closeBtn.hide();
35405             this.hide();
35406         }else{
35407             if(!this.isVisible()){
35408                 this.show();
35409             }
35410         }
35411     },
35412
35413     /**
35414      * Adds the passed ContentPanel(s) to this region.
35415      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35416      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35417      */
35418     add : function(panel)
35419     {
35420         if(arguments.length > 1){
35421             for(var i = 0, len = arguments.length; i < len; i++) {
35422                 this.add(arguments[i]);
35423             }
35424             return null;
35425         }
35426         
35427         // if we have not been rendered yet, then we can not really do much of this..
35428         if (!this.bodyEl) {
35429             this.unrendered_panels.push(panel);
35430             return panel;
35431         }
35432         
35433         
35434         
35435         
35436         if(this.hasPanel(panel)){
35437             this.showPanel(panel);
35438             return panel;
35439         }
35440         panel.setRegion(this);
35441         this.panels.add(panel);
35442        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35443             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35444             // and hide them... ???
35445             this.bodyEl.dom.appendChild(panel.getEl().dom);
35446             if(panel.background !== true){
35447                 this.setActivePanel(panel);
35448             }
35449             this.fireEvent("paneladded", this, panel);
35450             return panel;
35451         }
35452         */
35453         if(!this.tabs){
35454             this.initTabs();
35455         }else{
35456             this.initPanelAsTab(panel);
35457         }
35458         
35459         
35460         if(panel.background !== true){
35461             this.tabs.activate(panel.getEl().id);
35462         }
35463         this.fireEvent("paneladded", this, panel);
35464         return panel;
35465     },
35466
35467     /**
35468      * Hides the tab for the specified panel.
35469      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35470      */
35471     hidePanel : function(panel){
35472         if(this.tabs && (panel = this.getPanel(panel))){
35473             this.tabs.hideTab(panel.getEl().id);
35474         }
35475     },
35476
35477     /**
35478      * Unhides the tab for a previously hidden panel.
35479      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35480      */
35481     unhidePanel : function(panel){
35482         if(this.tabs && (panel = this.getPanel(panel))){
35483             this.tabs.unhideTab(panel.getEl().id);
35484         }
35485     },
35486
35487     clearPanels : function(){
35488         while(this.panels.getCount() > 0){
35489              this.remove(this.panels.first());
35490         }
35491     },
35492
35493     /**
35494      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35495      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35496      * @param {Boolean} preservePanel Overrides the config preservePanel option
35497      * @return {Roo.ContentPanel} The panel that was removed
35498      */
35499     remove : function(panel, preservePanel)
35500     {
35501         panel = this.getPanel(panel);
35502         if(!panel){
35503             return null;
35504         }
35505         var e = {};
35506         this.fireEvent("beforeremove", this, panel, e);
35507         if(e.cancel === true){
35508             return null;
35509         }
35510         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35511         var panelId = panel.getId();
35512         this.panels.removeKey(panelId);
35513         if(preservePanel){
35514             document.body.appendChild(panel.getEl().dom);
35515         }
35516         if(this.tabs){
35517             this.tabs.removeTab(panel.getEl().id);
35518         }else if (!preservePanel){
35519             this.bodyEl.dom.removeChild(panel.getEl().dom);
35520         }
35521         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35522             var p = this.panels.first();
35523             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35524             tempEl.appendChild(p.getEl().dom);
35525             this.bodyEl.update("");
35526             this.bodyEl.dom.appendChild(p.getEl().dom);
35527             tempEl = null;
35528             this.updateTitle(p.getTitle());
35529             this.tabs = null;
35530             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35531             this.setActivePanel(p);
35532         }
35533         panel.setRegion(null);
35534         if(this.activePanel == panel){
35535             this.activePanel = null;
35536         }
35537         if(this.config.autoDestroy !== false && preservePanel !== true){
35538             try{panel.destroy();}catch(e){}
35539         }
35540         this.fireEvent("panelremoved", this, panel);
35541         return panel;
35542     },
35543
35544     /**
35545      * Returns the TabPanel component used by this region
35546      * @return {Roo.TabPanel}
35547      */
35548     getTabs : function(){
35549         return this.tabs;
35550     },
35551
35552     createTool : function(parentEl, className){
35553         var btn = Roo.DomHelper.append(parentEl, {
35554             tag: "div",
35555             cls: "x-layout-tools-button",
35556             children: [ {
35557                 tag: "div",
35558                 cls: "roo-layout-tools-button-inner " + className,
35559                 html: "&#160;"
35560             }]
35561         }, true);
35562         btn.addClassOnOver("roo-layout-tools-button-over");
35563         return btn;
35564     }
35565 });/*
35566  * Based on:
35567  * Ext JS Library 1.1.1
35568  * Copyright(c) 2006-2007, Ext JS, LLC.
35569  *
35570  * Originally Released Under LGPL - original licence link has changed is not relivant.
35571  *
35572  * Fork - LGPL
35573  * <script type="text/javascript">
35574  */
35575  
35576
35577
35578 /**
35579  * @class Roo.SplitLayoutRegion
35580  * @extends Roo.LayoutRegion
35581  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35582  */
35583 Roo.bootstrap.layout.Split = function(config){
35584     this.cursor = config.cursor;
35585     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35586 };
35587
35588 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35589 {
35590     splitTip : "Drag to resize.",
35591     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35592     useSplitTips : false,
35593
35594     applyConfig : function(config){
35595         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35596     },
35597     
35598     onRender : function(ctr,pos) {
35599         
35600         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35601         if(!this.config.split){
35602             return;
35603         }
35604         if(!this.split){
35605             
35606             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35607                             tag: "div",
35608                             id: this.el.id + "-split",
35609                             cls: "roo-layout-split roo-layout-split-"+this.position,
35610                             html: "&#160;"
35611             });
35612             /** The SplitBar for this region 
35613             * @type Roo.SplitBar */
35614             // does not exist yet...
35615             Roo.log([this.position, this.orientation]);
35616             
35617             this.split = new Roo.bootstrap.SplitBar({
35618                 dragElement : splitEl,
35619                 resizingElement: this.el,
35620                 orientation : this.orientation
35621             });
35622             
35623             this.split.on("moved", this.onSplitMove, this);
35624             this.split.useShim = this.config.useShim === true;
35625             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35626             if(this.useSplitTips){
35627                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35628             }
35629             //if(config.collapsible){
35630             //    this.split.el.on("dblclick", this.collapse,  this);
35631             //}
35632         }
35633         if(typeof this.config.minSize != "undefined"){
35634             this.split.minSize = this.config.minSize;
35635         }
35636         if(typeof this.config.maxSize != "undefined"){
35637             this.split.maxSize = this.config.maxSize;
35638         }
35639         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35640             this.hideSplitter();
35641         }
35642         
35643     },
35644
35645     getHMaxSize : function(){
35646          var cmax = this.config.maxSize || 10000;
35647          var center = this.mgr.getRegion("center");
35648          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35649     },
35650
35651     getVMaxSize : function(){
35652          var cmax = this.config.maxSize || 10000;
35653          var center = this.mgr.getRegion("center");
35654          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35655     },
35656
35657     onSplitMove : function(split, newSize){
35658         this.fireEvent("resized", this, newSize);
35659     },
35660     
35661     /** 
35662      * Returns the {@link Roo.SplitBar} for this region.
35663      * @return {Roo.SplitBar}
35664      */
35665     getSplitBar : function(){
35666         return this.split;
35667     },
35668     
35669     hide : function(){
35670         this.hideSplitter();
35671         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35672     },
35673
35674     hideSplitter : function(){
35675         if(this.split){
35676             this.split.el.setLocation(-2000,-2000);
35677             this.split.el.hide();
35678         }
35679     },
35680
35681     show : function(){
35682         if(this.split){
35683             this.split.el.show();
35684         }
35685         Roo.bootstrap.layout.Split.superclass.show.call(this);
35686     },
35687     
35688     beforeSlide: function(){
35689         if(Roo.isGecko){// firefox overflow auto bug workaround
35690             this.bodyEl.clip();
35691             if(this.tabs) {
35692                 this.tabs.bodyEl.clip();
35693             }
35694             if(this.activePanel){
35695                 this.activePanel.getEl().clip();
35696                 
35697                 if(this.activePanel.beforeSlide){
35698                     this.activePanel.beforeSlide();
35699                 }
35700             }
35701         }
35702     },
35703     
35704     afterSlide : function(){
35705         if(Roo.isGecko){// firefox overflow auto bug workaround
35706             this.bodyEl.unclip();
35707             if(this.tabs) {
35708                 this.tabs.bodyEl.unclip();
35709             }
35710             if(this.activePanel){
35711                 this.activePanel.getEl().unclip();
35712                 if(this.activePanel.afterSlide){
35713                     this.activePanel.afterSlide();
35714                 }
35715             }
35716         }
35717     },
35718
35719     initAutoHide : function(){
35720         if(this.autoHide !== false){
35721             if(!this.autoHideHd){
35722                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35723                 this.autoHideHd = {
35724                     "mouseout": function(e){
35725                         if(!e.within(this.el, true)){
35726                             st.delay(500);
35727                         }
35728                     },
35729                     "mouseover" : function(e){
35730                         st.cancel();
35731                     },
35732                     scope : this
35733                 };
35734             }
35735             this.el.on(this.autoHideHd);
35736         }
35737     },
35738
35739     clearAutoHide : function(){
35740         if(this.autoHide !== false){
35741             this.el.un("mouseout", this.autoHideHd.mouseout);
35742             this.el.un("mouseover", this.autoHideHd.mouseover);
35743         }
35744     },
35745
35746     clearMonitor : function(){
35747         Roo.get(document).un("click", this.slideInIf, this);
35748     },
35749
35750     // these names are backwards but not changed for compat
35751     slideOut : function(){
35752         if(this.isSlid || this.el.hasActiveFx()){
35753             return;
35754         }
35755         this.isSlid = true;
35756         if(this.collapseBtn){
35757             this.collapseBtn.hide();
35758         }
35759         this.closeBtnState = this.closeBtn.getStyle('display');
35760         this.closeBtn.hide();
35761         if(this.stickBtn){
35762             this.stickBtn.show();
35763         }
35764         this.el.show();
35765         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35766         this.beforeSlide();
35767         this.el.setStyle("z-index", 10001);
35768         this.el.slideIn(this.getSlideAnchor(), {
35769             callback: function(){
35770                 this.afterSlide();
35771                 this.initAutoHide();
35772                 Roo.get(document).on("click", this.slideInIf, this);
35773                 this.fireEvent("slideshow", this);
35774             },
35775             scope: this,
35776             block: true
35777         });
35778     },
35779
35780     afterSlideIn : function(){
35781         this.clearAutoHide();
35782         this.isSlid = false;
35783         this.clearMonitor();
35784         this.el.setStyle("z-index", "");
35785         if(this.collapseBtn){
35786             this.collapseBtn.show();
35787         }
35788         this.closeBtn.setStyle('display', this.closeBtnState);
35789         if(this.stickBtn){
35790             this.stickBtn.hide();
35791         }
35792         this.fireEvent("slidehide", this);
35793     },
35794
35795     slideIn : function(cb){
35796         if(!this.isSlid || this.el.hasActiveFx()){
35797             Roo.callback(cb);
35798             return;
35799         }
35800         this.isSlid = false;
35801         this.beforeSlide();
35802         this.el.slideOut(this.getSlideAnchor(), {
35803             callback: function(){
35804                 this.el.setLeftTop(-10000, -10000);
35805                 this.afterSlide();
35806                 this.afterSlideIn();
35807                 Roo.callback(cb);
35808             },
35809             scope: this,
35810             block: true
35811         });
35812     },
35813     
35814     slideInIf : function(e){
35815         if(!e.within(this.el)){
35816             this.slideIn();
35817         }
35818     },
35819
35820     animateCollapse : function(){
35821         this.beforeSlide();
35822         this.el.setStyle("z-index", 20000);
35823         var anchor = this.getSlideAnchor();
35824         this.el.slideOut(anchor, {
35825             callback : function(){
35826                 this.el.setStyle("z-index", "");
35827                 this.collapsedEl.slideIn(anchor, {duration:.3});
35828                 this.afterSlide();
35829                 this.el.setLocation(-10000,-10000);
35830                 this.el.hide();
35831                 this.fireEvent("collapsed", this);
35832             },
35833             scope: this,
35834             block: true
35835         });
35836     },
35837
35838     animateExpand : function(){
35839         this.beforeSlide();
35840         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35841         this.el.setStyle("z-index", 20000);
35842         this.collapsedEl.hide({
35843             duration:.1
35844         });
35845         this.el.slideIn(this.getSlideAnchor(), {
35846             callback : function(){
35847                 this.el.setStyle("z-index", "");
35848                 this.afterSlide();
35849                 if(this.split){
35850                     this.split.el.show();
35851                 }
35852                 this.fireEvent("invalidated", this);
35853                 this.fireEvent("expanded", this);
35854             },
35855             scope: this,
35856             block: true
35857         });
35858     },
35859
35860     anchors : {
35861         "west" : "left",
35862         "east" : "right",
35863         "north" : "top",
35864         "south" : "bottom"
35865     },
35866
35867     sanchors : {
35868         "west" : "l",
35869         "east" : "r",
35870         "north" : "t",
35871         "south" : "b"
35872     },
35873
35874     canchors : {
35875         "west" : "tl-tr",
35876         "east" : "tr-tl",
35877         "north" : "tl-bl",
35878         "south" : "bl-tl"
35879     },
35880
35881     getAnchor : function(){
35882         return this.anchors[this.position];
35883     },
35884
35885     getCollapseAnchor : function(){
35886         return this.canchors[this.position];
35887     },
35888
35889     getSlideAnchor : function(){
35890         return this.sanchors[this.position];
35891     },
35892
35893     getAlignAdj : function(){
35894         var cm = this.cmargins;
35895         switch(this.position){
35896             case "west":
35897                 return [0, 0];
35898             break;
35899             case "east":
35900                 return [0, 0];
35901             break;
35902             case "north":
35903                 return [0, 0];
35904             break;
35905             case "south":
35906                 return [0, 0];
35907             break;
35908         }
35909     },
35910
35911     getExpandAdj : function(){
35912         var c = this.collapsedEl, cm = this.cmargins;
35913         switch(this.position){
35914             case "west":
35915                 return [-(cm.right+c.getWidth()+cm.left), 0];
35916             break;
35917             case "east":
35918                 return [cm.right+c.getWidth()+cm.left, 0];
35919             break;
35920             case "north":
35921                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35922             break;
35923             case "south":
35924                 return [0, cm.top+cm.bottom+c.getHeight()];
35925             break;
35926         }
35927     }
35928 });/*
35929  * Based on:
35930  * Ext JS Library 1.1.1
35931  * Copyright(c) 2006-2007, Ext JS, LLC.
35932  *
35933  * Originally Released Under LGPL - original licence link has changed is not relivant.
35934  *
35935  * Fork - LGPL
35936  * <script type="text/javascript">
35937  */
35938 /*
35939  * These classes are private internal classes
35940  */
35941 Roo.bootstrap.layout.Center = function(config){
35942     config.region = "center";
35943     Roo.bootstrap.layout.Region.call(this, config);
35944     this.visible = true;
35945     this.minWidth = config.minWidth || 20;
35946     this.minHeight = config.minHeight || 20;
35947 };
35948
35949 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35950     hide : function(){
35951         // center panel can't be hidden
35952     },
35953     
35954     show : function(){
35955         // center panel can't be hidden
35956     },
35957     
35958     getMinWidth: function(){
35959         return this.minWidth;
35960     },
35961     
35962     getMinHeight: function(){
35963         return this.minHeight;
35964     }
35965 });
35966
35967
35968
35969
35970  
35971
35972
35973
35974
35975
35976 Roo.bootstrap.layout.North = function(config)
35977 {
35978     config.region = 'north';
35979     config.cursor = 'n-resize';
35980     
35981     Roo.bootstrap.layout.Split.call(this, config);
35982     
35983     
35984     if(this.split){
35985         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35986         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35987         this.split.el.addClass("roo-layout-split-v");
35988     }
35989     var size = config.initialSize || config.height;
35990     if(typeof size != "undefined"){
35991         this.el.setHeight(size);
35992     }
35993 };
35994 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35995 {
35996     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35997     
35998     
35999     
36000     getBox : function(){
36001         if(this.collapsed){
36002             return this.collapsedEl.getBox();
36003         }
36004         var box = this.el.getBox();
36005         if(this.split){
36006             box.height += this.split.el.getHeight();
36007         }
36008         return box;
36009     },
36010     
36011     updateBox : function(box){
36012         if(this.split && !this.collapsed){
36013             box.height -= this.split.el.getHeight();
36014             this.split.el.setLeft(box.x);
36015             this.split.el.setTop(box.y+box.height);
36016             this.split.el.setWidth(box.width);
36017         }
36018         if(this.collapsed){
36019             this.updateBody(box.width, null);
36020         }
36021         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36022     }
36023 });
36024
36025
36026
36027
36028
36029 Roo.bootstrap.layout.South = function(config){
36030     config.region = 'south';
36031     config.cursor = 's-resize';
36032     Roo.bootstrap.layout.Split.call(this, config);
36033     if(this.split){
36034         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36035         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36036         this.split.el.addClass("roo-layout-split-v");
36037     }
36038     var size = config.initialSize || config.height;
36039     if(typeof size != "undefined"){
36040         this.el.setHeight(size);
36041     }
36042 };
36043
36044 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36045     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36046     getBox : function(){
36047         if(this.collapsed){
36048             return this.collapsedEl.getBox();
36049         }
36050         var box = this.el.getBox();
36051         if(this.split){
36052             var sh = this.split.el.getHeight();
36053             box.height += sh;
36054             box.y -= sh;
36055         }
36056         return box;
36057     },
36058     
36059     updateBox : function(box){
36060         if(this.split && !this.collapsed){
36061             var sh = this.split.el.getHeight();
36062             box.height -= sh;
36063             box.y += sh;
36064             this.split.el.setLeft(box.x);
36065             this.split.el.setTop(box.y-sh);
36066             this.split.el.setWidth(box.width);
36067         }
36068         if(this.collapsed){
36069             this.updateBody(box.width, null);
36070         }
36071         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36072     }
36073 });
36074
36075 Roo.bootstrap.layout.East = function(config){
36076     config.region = "east";
36077     config.cursor = "e-resize";
36078     Roo.bootstrap.layout.Split.call(this, config);
36079     if(this.split){
36080         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36081         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36082         this.split.el.addClass("roo-layout-split-h");
36083     }
36084     var size = config.initialSize || config.width;
36085     if(typeof size != "undefined"){
36086         this.el.setWidth(size);
36087     }
36088 };
36089 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36090     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36091     getBox : function(){
36092         if(this.collapsed){
36093             return this.collapsedEl.getBox();
36094         }
36095         var box = this.el.getBox();
36096         if(this.split){
36097             var sw = this.split.el.getWidth();
36098             box.width += sw;
36099             box.x -= sw;
36100         }
36101         return box;
36102     },
36103
36104     updateBox : function(box){
36105         if(this.split && !this.collapsed){
36106             var sw = this.split.el.getWidth();
36107             box.width -= sw;
36108             this.split.el.setLeft(box.x);
36109             this.split.el.setTop(box.y);
36110             this.split.el.setHeight(box.height);
36111             box.x += sw;
36112         }
36113         if(this.collapsed){
36114             this.updateBody(null, box.height);
36115         }
36116         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36117     }
36118 });
36119
36120 Roo.bootstrap.layout.West = function(config){
36121     config.region = "west";
36122     config.cursor = "w-resize";
36123     
36124     Roo.bootstrap.layout.Split.call(this, config);
36125     if(this.split){
36126         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36127         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36128         this.split.el.addClass("roo-layout-split-h");
36129     }
36130     
36131 };
36132 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36133     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36134     
36135     onRender: function(ctr, pos)
36136     {
36137         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36138         var size = this.config.initialSize || this.config.width;
36139         if(typeof size != "undefined"){
36140             this.el.setWidth(size);
36141         }
36142     },
36143     
36144     getBox : function(){
36145         if(this.collapsed){
36146             return this.collapsedEl.getBox();
36147         }
36148         var box = this.el.getBox();
36149         if(this.split){
36150             box.width += this.split.el.getWidth();
36151         }
36152         return box;
36153     },
36154     
36155     updateBox : function(box){
36156         if(this.split && !this.collapsed){
36157             var sw = this.split.el.getWidth();
36158             box.width -= sw;
36159             this.split.el.setLeft(box.x+box.width);
36160             this.split.el.setTop(box.y);
36161             this.split.el.setHeight(box.height);
36162         }
36163         if(this.collapsed){
36164             this.updateBody(null, box.height);
36165         }
36166         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36167     }
36168 });
36169 Roo.namespace("Roo.bootstrap.panel");/*
36170  * Based on:
36171  * Ext JS Library 1.1.1
36172  * Copyright(c) 2006-2007, Ext JS, LLC.
36173  *
36174  * Originally Released Under LGPL - original licence link has changed is not relivant.
36175  *
36176  * Fork - LGPL
36177  * <script type="text/javascript">
36178  */
36179 /**
36180  * @class Roo.ContentPanel
36181  * @extends Roo.util.Observable
36182  * A basic ContentPanel element.
36183  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36184  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36185  * @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
36186  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36187  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36188  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36189  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36190  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36191  * @cfg {String} title          The title for this panel
36192  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36193  * @cfg {String} url            Calls {@link #setUrl} with this value
36194  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36195  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36196  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36197  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36198  * @cfg {Boolean} badges render the badges
36199
36200  * @constructor
36201  * Create a new ContentPanel.
36202  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36203  * @param {String/Object} config A string to set only the title or a config object
36204  * @param {String} content (optional) Set the HTML content for this panel
36205  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36206  */
36207 Roo.bootstrap.panel.Content = function( config){
36208     
36209     this.tpl = config.tpl || false;
36210     
36211     var el = config.el;
36212     var content = config.content;
36213
36214     if(config.autoCreate){ // xtype is available if this is called from factory
36215         el = Roo.id();
36216     }
36217     this.el = Roo.get(el);
36218     if(!this.el && config && config.autoCreate){
36219         if(typeof config.autoCreate == "object"){
36220             if(!config.autoCreate.id){
36221                 config.autoCreate.id = config.id||el;
36222             }
36223             this.el = Roo.DomHelper.append(document.body,
36224                         config.autoCreate, true);
36225         }else{
36226             var elcfg =  {   tag: "div",
36227                             cls: "roo-layout-inactive-content",
36228                             id: config.id||el
36229                             };
36230             if (config.html) {
36231                 elcfg.html = config.html;
36232                 
36233             }
36234                         
36235             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36236         }
36237     } 
36238     this.closable = false;
36239     this.loaded = false;
36240     this.active = false;
36241    
36242       
36243     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36244         
36245         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36246         
36247         this.wrapEl = this.el; //this.el.wrap();
36248         var ti = [];
36249         if (config.toolbar.items) {
36250             ti = config.toolbar.items ;
36251             delete config.toolbar.items ;
36252         }
36253         
36254         var nitems = [];
36255         this.toolbar.render(this.wrapEl, 'before');
36256         for(var i =0;i < ti.length;i++) {
36257           //  Roo.log(['add child', items[i]]);
36258             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36259         }
36260         this.toolbar.items = nitems;
36261         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36262         delete config.toolbar;
36263         
36264     }
36265     /*
36266     // xtype created footer. - not sure if will work as we normally have to render first..
36267     if (this.footer && !this.footer.el && this.footer.xtype) {
36268         if (!this.wrapEl) {
36269             this.wrapEl = this.el.wrap();
36270         }
36271     
36272         this.footer.container = this.wrapEl.createChild();
36273          
36274         this.footer = Roo.factory(this.footer, Roo);
36275         
36276     }
36277     */
36278     
36279      if(typeof config == "string"){
36280         this.title = config;
36281     }else{
36282         Roo.apply(this, config);
36283     }
36284     
36285     if(this.resizeEl){
36286         this.resizeEl = Roo.get(this.resizeEl, true);
36287     }else{
36288         this.resizeEl = this.el;
36289     }
36290     // handle view.xtype
36291     
36292  
36293     
36294     
36295     this.addEvents({
36296         /**
36297          * @event activate
36298          * Fires when this panel is activated. 
36299          * @param {Roo.ContentPanel} this
36300          */
36301         "activate" : true,
36302         /**
36303          * @event deactivate
36304          * Fires when this panel is activated. 
36305          * @param {Roo.ContentPanel} this
36306          */
36307         "deactivate" : true,
36308
36309         /**
36310          * @event resize
36311          * Fires when this panel is resized if fitToFrame is true.
36312          * @param {Roo.ContentPanel} this
36313          * @param {Number} width The width after any component adjustments
36314          * @param {Number} height The height after any component adjustments
36315          */
36316         "resize" : true,
36317         
36318          /**
36319          * @event render
36320          * Fires when this tab is created
36321          * @param {Roo.ContentPanel} this
36322          */
36323         "render" : true
36324         
36325         
36326         
36327     });
36328     
36329
36330     
36331     
36332     if(this.autoScroll){
36333         this.resizeEl.setStyle("overflow", "auto");
36334     } else {
36335         // fix randome scrolling
36336         //this.el.on('scroll', function() {
36337         //    Roo.log('fix random scolling');
36338         //    this.scrollTo('top',0); 
36339         //});
36340     }
36341     content = content || this.content;
36342     if(content){
36343         this.setContent(content);
36344     }
36345     if(config && config.url){
36346         this.setUrl(this.url, this.params, this.loadOnce);
36347     }
36348     
36349     
36350     
36351     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36352     
36353     if (this.view && typeof(this.view.xtype) != 'undefined') {
36354         this.view.el = this.el.appendChild(document.createElement("div"));
36355         this.view = Roo.factory(this.view); 
36356         this.view.render  &&  this.view.render(false, '');  
36357     }
36358     
36359     
36360     this.fireEvent('render', this);
36361 };
36362
36363 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36364     
36365     tabTip : '',
36366     
36367     setRegion : function(region){
36368         this.region = region;
36369         this.setActiveClass(region && !this.background);
36370     },
36371     
36372     
36373     setActiveClass: function(state)
36374     {
36375         if(state){
36376            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36377            this.el.setStyle('position','relative');
36378         }else{
36379            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36380            this.el.setStyle('position', 'absolute');
36381         } 
36382     },
36383     
36384     /**
36385      * Returns the toolbar for this Panel if one was configured. 
36386      * @return {Roo.Toolbar} 
36387      */
36388     getToolbar : function(){
36389         return this.toolbar;
36390     },
36391     
36392     setActiveState : function(active)
36393     {
36394         this.active = active;
36395         this.setActiveClass(active);
36396         if(!active){
36397             this.fireEvent("deactivate", this);
36398         }else{
36399             this.fireEvent("activate", this);
36400         }
36401     },
36402     /**
36403      * Updates this panel's element
36404      * @param {String} content The new content
36405      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36406     */
36407     setContent : function(content, loadScripts){
36408         this.el.update(content, loadScripts);
36409     },
36410
36411     ignoreResize : function(w, h){
36412         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36413             return true;
36414         }else{
36415             this.lastSize = {width: w, height: h};
36416             return false;
36417         }
36418     },
36419     /**
36420      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36421      * @return {Roo.UpdateManager} The UpdateManager
36422      */
36423     getUpdateManager : function(){
36424         return this.el.getUpdateManager();
36425     },
36426      /**
36427      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36428      * @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:
36429 <pre><code>
36430 panel.load({
36431     url: "your-url.php",
36432     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36433     callback: yourFunction,
36434     scope: yourObject, //(optional scope)
36435     discardUrl: false,
36436     nocache: false,
36437     text: "Loading...",
36438     timeout: 30,
36439     scripts: false
36440 });
36441 </code></pre>
36442      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36443      * 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.
36444      * @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}
36445      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36446      * @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.
36447      * @return {Roo.ContentPanel} this
36448      */
36449     load : function(){
36450         var um = this.el.getUpdateManager();
36451         um.update.apply(um, arguments);
36452         return this;
36453     },
36454
36455
36456     /**
36457      * 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.
36458      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36459      * @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)
36460      * @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)
36461      * @return {Roo.UpdateManager} The UpdateManager
36462      */
36463     setUrl : function(url, params, loadOnce){
36464         if(this.refreshDelegate){
36465             this.removeListener("activate", this.refreshDelegate);
36466         }
36467         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36468         this.on("activate", this.refreshDelegate);
36469         return this.el.getUpdateManager();
36470     },
36471     
36472     _handleRefresh : function(url, params, loadOnce){
36473         if(!loadOnce || !this.loaded){
36474             var updater = this.el.getUpdateManager();
36475             updater.update(url, params, this._setLoaded.createDelegate(this));
36476         }
36477     },
36478     
36479     _setLoaded : function(){
36480         this.loaded = true;
36481     }, 
36482     
36483     /**
36484      * Returns this panel's id
36485      * @return {String} 
36486      */
36487     getId : function(){
36488         return this.el.id;
36489     },
36490     
36491     /** 
36492      * Returns this panel's element - used by regiosn to add.
36493      * @return {Roo.Element} 
36494      */
36495     getEl : function(){
36496         return this.wrapEl || this.el;
36497     },
36498     
36499    
36500     
36501     adjustForComponents : function(width, height)
36502     {
36503         //Roo.log('adjustForComponents ');
36504         if(this.resizeEl != this.el){
36505             width -= this.el.getFrameWidth('lr');
36506             height -= this.el.getFrameWidth('tb');
36507         }
36508         if(this.toolbar){
36509             var te = this.toolbar.getEl();
36510             te.setWidth(width);
36511             height -= te.getHeight();
36512         }
36513         if(this.footer){
36514             var te = this.footer.getEl();
36515             te.setWidth(width);
36516             height -= te.getHeight();
36517         }
36518         
36519         
36520         if(this.adjustments){
36521             width += this.adjustments[0];
36522             height += this.adjustments[1];
36523         }
36524         return {"width": width, "height": height};
36525     },
36526     
36527     setSize : function(width, height){
36528         if(this.fitToFrame && !this.ignoreResize(width, height)){
36529             if(this.fitContainer && this.resizeEl != this.el){
36530                 this.el.setSize(width, height);
36531             }
36532             var size = this.adjustForComponents(width, height);
36533             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36534             this.fireEvent('resize', this, size.width, size.height);
36535         }
36536     },
36537     
36538     /**
36539      * Returns this panel's title
36540      * @return {String} 
36541      */
36542     getTitle : function(){
36543         
36544         if (typeof(this.title) != 'object') {
36545             return this.title;
36546         }
36547         
36548         var t = '';
36549         for (var k in this.title) {
36550             if (!this.title.hasOwnProperty(k)) {
36551                 continue;
36552             }
36553             
36554             if (k.indexOf('-') >= 0) {
36555                 var s = k.split('-');
36556                 for (var i = 0; i<s.length; i++) {
36557                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36558                 }
36559             } else {
36560                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36561             }
36562         }
36563         return t;
36564     },
36565     
36566     /**
36567      * Set this panel's title
36568      * @param {String} title
36569      */
36570     setTitle : function(title){
36571         this.title = title;
36572         if(this.region){
36573             this.region.updatePanelTitle(this, title);
36574         }
36575     },
36576     
36577     /**
36578      * Returns true is this panel was configured to be closable
36579      * @return {Boolean} 
36580      */
36581     isClosable : function(){
36582         return this.closable;
36583     },
36584     
36585     beforeSlide : function(){
36586         this.el.clip();
36587         this.resizeEl.clip();
36588     },
36589     
36590     afterSlide : function(){
36591         this.el.unclip();
36592         this.resizeEl.unclip();
36593     },
36594     
36595     /**
36596      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36597      *   Will fail silently if the {@link #setUrl} method has not been called.
36598      *   This does not activate the panel, just updates its content.
36599      */
36600     refresh : function(){
36601         if(this.refreshDelegate){
36602            this.loaded = false;
36603            this.refreshDelegate();
36604         }
36605     },
36606     
36607     /**
36608      * Destroys this panel
36609      */
36610     destroy : function(){
36611         this.el.removeAllListeners();
36612         var tempEl = document.createElement("span");
36613         tempEl.appendChild(this.el.dom);
36614         tempEl.innerHTML = "";
36615         this.el.remove();
36616         this.el = null;
36617     },
36618     
36619     /**
36620      * form - if the content panel contains a form - this is a reference to it.
36621      * @type {Roo.form.Form}
36622      */
36623     form : false,
36624     /**
36625      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36626      *    This contains a reference to it.
36627      * @type {Roo.View}
36628      */
36629     view : false,
36630     
36631       /**
36632      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36633      * <pre><code>
36634
36635 layout.addxtype({
36636        xtype : 'Form',
36637        items: [ .... ]
36638    }
36639 );
36640
36641 </code></pre>
36642      * @param {Object} cfg Xtype definition of item to add.
36643      */
36644     
36645     
36646     getChildContainer: function () {
36647         return this.getEl();
36648     }
36649     
36650     
36651     /*
36652         var  ret = new Roo.factory(cfg);
36653         return ret;
36654         
36655         
36656         // add form..
36657         if (cfg.xtype.match(/^Form$/)) {
36658             
36659             var el;
36660             //if (this.footer) {
36661             //    el = this.footer.container.insertSibling(false, 'before');
36662             //} else {
36663                 el = this.el.createChild();
36664             //}
36665
36666             this.form = new  Roo.form.Form(cfg);
36667             
36668             
36669             if ( this.form.allItems.length) {
36670                 this.form.render(el.dom);
36671             }
36672             return this.form;
36673         }
36674         // should only have one of theses..
36675         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36676             // views.. should not be just added - used named prop 'view''
36677             
36678             cfg.el = this.el.appendChild(document.createElement("div"));
36679             // factory?
36680             
36681             var ret = new Roo.factory(cfg);
36682              
36683              ret.render && ret.render(false, ''); // render blank..
36684             this.view = ret;
36685             return ret;
36686         }
36687         return false;
36688     }
36689     \*/
36690 });
36691  
36692 /**
36693  * @class Roo.bootstrap.panel.Grid
36694  * @extends Roo.bootstrap.panel.Content
36695  * @constructor
36696  * Create a new GridPanel.
36697  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36698  * @param {Object} config A the config object
36699   
36700  */
36701
36702
36703
36704 Roo.bootstrap.panel.Grid = function(config)
36705 {
36706     
36707       
36708     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36709         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36710
36711     config.el = this.wrapper;
36712     //this.el = this.wrapper;
36713     
36714       if (config.container) {
36715         // ctor'ed from a Border/panel.grid
36716         
36717         
36718         this.wrapper.setStyle("overflow", "hidden");
36719         this.wrapper.addClass('roo-grid-container');
36720
36721     }
36722     
36723     
36724     if(config.toolbar){
36725         var tool_el = this.wrapper.createChild();    
36726         this.toolbar = Roo.factory(config.toolbar);
36727         var ti = [];
36728         if (config.toolbar.items) {
36729             ti = config.toolbar.items ;
36730             delete config.toolbar.items ;
36731         }
36732         
36733         var nitems = [];
36734         this.toolbar.render(tool_el);
36735         for(var i =0;i < ti.length;i++) {
36736           //  Roo.log(['add child', items[i]]);
36737             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36738         }
36739         this.toolbar.items = nitems;
36740         
36741         delete config.toolbar;
36742     }
36743     
36744     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36745     config.grid.scrollBody = true;;
36746     config.grid.monitorWindowResize = false; // turn off autosizing
36747     config.grid.autoHeight = false;
36748     config.grid.autoWidth = false;
36749     
36750     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36751     
36752     if (config.background) {
36753         // render grid on panel activation (if panel background)
36754         this.on('activate', function(gp) {
36755             if (!gp.grid.rendered) {
36756                 gp.grid.render(this.wrapper);
36757                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36758             }
36759         });
36760             
36761     } else {
36762         this.grid.render(this.wrapper);
36763         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36764
36765     }
36766     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36767     // ??? needed ??? config.el = this.wrapper;
36768     
36769     
36770     
36771   
36772     // xtype created footer. - not sure if will work as we normally have to render first..
36773     if (this.footer && !this.footer.el && this.footer.xtype) {
36774         
36775         var ctr = this.grid.getView().getFooterPanel(true);
36776         this.footer.dataSource = this.grid.dataSource;
36777         this.footer = Roo.factory(this.footer, Roo);
36778         this.footer.render(ctr);
36779         
36780     }
36781     
36782     
36783     
36784     
36785      
36786 };
36787
36788 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36789     getId : function(){
36790         return this.grid.id;
36791     },
36792     
36793     /**
36794      * Returns the grid for this panel
36795      * @return {Roo.bootstrap.Table} 
36796      */
36797     getGrid : function(){
36798         return this.grid;    
36799     },
36800     
36801     setSize : function(width, height){
36802         if(!this.ignoreResize(width, height)){
36803             var grid = this.grid;
36804             var size = this.adjustForComponents(width, height);
36805             var gridel = grid.getGridEl();
36806             gridel.setSize(size.width, size.height);
36807             /*
36808             var thd = grid.getGridEl().select('thead',true).first();
36809             var tbd = grid.getGridEl().select('tbody', true).first();
36810             if (tbd) {
36811                 tbd.setSize(width, height - thd.getHeight());
36812             }
36813             */
36814             grid.autoSize();
36815         }
36816     },
36817      
36818     
36819     
36820     beforeSlide : function(){
36821         this.grid.getView().scroller.clip();
36822     },
36823     
36824     afterSlide : function(){
36825         this.grid.getView().scroller.unclip();
36826     },
36827     
36828     destroy : function(){
36829         this.grid.destroy();
36830         delete this.grid;
36831         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36832     }
36833 });
36834
36835 /**
36836  * @class Roo.bootstrap.panel.Nest
36837  * @extends Roo.bootstrap.panel.Content
36838  * @constructor
36839  * Create a new Panel, that can contain a layout.Border.
36840  * 
36841  * 
36842  * @param {Roo.BorderLayout} layout The layout for this panel
36843  * @param {String/Object} config A string to set only the title or a config object
36844  */
36845 Roo.bootstrap.panel.Nest = function(config)
36846 {
36847     // construct with only one argument..
36848     /* FIXME - implement nicer consturctors
36849     if (layout.layout) {
36850         config = layout;
36851         layout = config.layout;
36852         delete config.layout;
36853     }
36854     if (layout.xtype && !layout.getEl) {
36855         // then layout needs constructing..
36856         layout = Roo.factory(layout, Roo);
36857     }
36858     */
36859     
36860     config.el =  config.layout.getEl();
36861     
36862     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36863     
36864     config.layout.monitorWindowResize = false; // turn off autosizing
36865     this.layout = config.layout;
36866     this.layout.getEl().addClass("roo-layout-nested-layout");
36867     
36868     
36869     
36870     
36871 };
36872
36873 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36874
36875     setSize : function(width, height){
36876         if(!this.ignoreResize(width, height)){
36877             var size = this.adjustForComponents(width, height);
36878             var el = this.layout.getEl();
36879             if (size.height < 1) {
36880                 el.setWidth(size.width);   
36881             } else {
36882                 el.setSize(size.width, size.height);
36883             }
36884             var touch = el.dom.offsetWidth;
36885             this.layout.layout();
36886             // ie requires a double layout on the first pass
36887             if(Roo.isIE && !this.initialized){
36888                 this.initialized = true;
36889                 this.layout.layout();
36890             }
36891         }
36892     },
36893     
36894     // activate all subpanels if not currently active..
36895     
36896     setActiveState : function(active){
36897         this.active = active;
36898         this.setActiveClass(active);
36899         
36900         if(!active){
36901             this.fireEvent("deactivate", this);
36902             return;
36903         }
36904         
36905         this.fireEvent("activate", this);
36906         // not sure if this should happen before or after..
36907         if (!this.layout) {
36908             return; // should not happen..
36909         }
36910         var reg = false;
36911         for (var r in this.layout.regions) {
36912             reg = this.layout.getRegion(r);
36913             if (reg.getActivePanel()) {
36914                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36915                 reg.setActivePanel(reg.getActivePanel());
36916                 continue;
36917             }
36918             if (!reg.panels.length) {
36919                 continue;
36920             }
36921             reg.showPanel(reg.getPanel(0));
36922         }
36923         
36924         
36925         
36926         
36927     },
36928     
36929     /**
36930      * Returns the nested BorderLayout for this panel
36931      * @return {Roo.BorderLayout} 
36932      */
36933     getLayout : function(){
36934         return this.layout;
36935     },
36936     
36937      /**
36938      * Adds a xtype elements to the layout of the nested panel
36939      * <pre><code>
36940
36941 panel.addxtype({
36942        xtype : 'ContentPanel',
36943        region: 'west',
36944        items: [ .... ]
36945    }
36946 );
36947
36948 panel.addxtype({
36949         xtype : 'NestedLayoutPanel',
36950         region: 'west',
36951         layout: {
36952            center: { },
36953            west: { }   
36954         },
36955         items : [ ... list of content panels or nested layout panels.. ]
36956    }
36957 );
36958 </code></pre>
36959      * @param {Object} cfg Xtype definition of item to add.
36960      */
36961     addxtype : function(cfg) {
36962         return this.layout.addxtype(cfg);
36963     
36964     }
36965 });        /*
36966  * Based on:
36967  * Ext JS Library 1.1.1
36968  * Copyright(c) 2006-2007, Ext JS, LLC.
36969  *
36970  * Originally Released Under LGPL - original licence link has changed is not relivant.
36971  *
36972  * Fork - LGPL
36973  * <script type="text/javascript">
36974  */
36975 /**
36976  * @class Roo.TabPanel
36977  * @extends Roo.util.Observable
36978  * A lightweight tab container.
36979  * <br><br>
36980  * Usage:
36981  * <pre><code>
36982 // basic tabs 1, built from existing content
36983 var tabs = new Roo.TabPanel("tabs1");
36984 tabs.addTab("script", "View Script");
36985 tabs.addTab("markup", "View Markup");
36986 tabs.activate("script");
36987
36988 // more advanced tabs, built from javascript
36989 var jtabs = new Roo.TabPanel("jtabs");
36990 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36991
36992 // set up the UpdateManager
36993 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36994 var updater = tab2.getUpdateManager();
36995 updater.setDefaultUrl("ajax1.htm");
36996 tab2.on('activate', updater.refresh, updater, true);
36997
36998 // Use setUrl for Ajax loading
36999 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37000 tab3.setUrl("ajax2.htm", null, true);
37001
37002 // Disabled tab
37003 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37004 tab4.disable();
37005
37006 jtabs.activate("jtabs-1");
37007  * </code></pre>
37008  * @constructor
37009  * Create a new TabPanel.
37010  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37011  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37012  */
37013 Roo.bootstrap.panel.Tabs = function(config){
37014     /**
37015     * The container element for this TabPanel.
37016     * @type Roo.Element
37017     */
37018     this.el = Roo.get(config.el);
37019     delete config.el;
37020     if(config){
37021         if(typeof config == "boolean"){
37022             this.tabPosition = config ? "bottom" : "top";
37023         }else{
37024             Roo.apply(this, config);
37025         }
37026     }
37027     
37028     if(this.tabPosition == "bottom"){
37029         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37030         this.el.addClass("roo-tabs-bottom");
37031     }
37032     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37033     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37034     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37035     if(Roo.isIE){
37036         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37037     }
37038     if(this.tabPosition != "bottom"){
37039         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37040          * @type Roo.Element
37041          */
37042         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37043         this.el.addClass("roo-tabs-top");
37044     }
37045     this.items = [];
37046
37047     this.bodyEl.setStyle("position", "relative");
37048
37049     this.active = null;
37050     this.activateDelegate = this.activate.createDelegate(this);
37051
37052     this.addEvents({
37053         /**
37054          * @event tabchange
37055          * Fires when the active tab changes
37056          * @param {Roo.TabPanel} this
37057          * @param {Roo.TabPanelItem} activePanel The new active tab
37058          */
37059         "tabchange": true,
37060         /**
37061          * @event beforetabchange
37062          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37063          * @param {Roo.TabPanel} this
37064          * @param {Object} e Set cancel to true on this object to cancel the tab change
37065          * @param {Roo.TabPanelItem} tab The tab being changed to
37066          */
37067         "beforetabchange" : true
37068     });
37069
37070     Roo.EventManager.onWindowResize(this.onResize, this);
37071     this.cpad = this.el.getPadding("lr");
37072     this.hiddenCount = 0;
37073
37074
37075     // toolbar on the tabbar support...
37076     if (this.toolbar) {
37077         alert("no toolbar support yet");
37078         this.toolbar  = false;
37079         /*
37080         var tcfg = this.toolbar;
37081         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37082         this.toolbar = new Roo.Toolbar(tcfg);
37083         if (Roo.isSafari) {
37084             var tbl = tcfg.container.child('table', true);
37085             tbl.setAttribute('width', '100%');
37086         }
37087         */
37088         
37089     }
37090    
37091
37092
37093     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37094 };
37095
37096 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37097     /*
37098      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37099      */
37100     tabPosition : "top",
37101     /*
37102      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37103      */
37104     currentTabWidth : 0,
37105     /*
37106      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37107      */
37108     minTabWidth : 40,
37109     /*
37110      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37111      */
37112     maxTabWidth : 250,
37113     /*
37114      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37115      */
37116     preferredTabWidth : 175,
37117     /*
37118      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37119      */
37120     resizeTabs : false,
37121     /*
37122      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37123      */
37124     monitorResize : true,
37125     /*
37126      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37127      */
37128     toolbar : false,
37129
37130     /**
37131      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37132      * @param {String} id The id of the div to use <b>or create</b>
37133      * @param {String} text The text for the tab
37134      * @param {String} content (optional) Content to put in the TabPanelItem body
37135      * @param {Boolean} closable (optional) True to create a close icon on the tab
37136      * @return {Roo.TabPanelItem} The created TabPanelItem
37137      */
37138     addTab : function(id, text, content, closable, tpl)
37139     {
37140         var item = new Roo.bootstrap.panel.TabItem({
37141             panel: this,
37142             id : id,
37143             text : text,
37144             closable : closable,
37145             tpl : tpl
37146         });
37147         this.addTabItem(item);
37148         if(content){
37149             item.setContent(content);
37150         }
37151         return item;
37152     },
37153
37154     /**
37155      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37156      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37157      * @return {Roo.TabPanelItem}
37158      */
37159     getTab : function(id){
37160         return this.items[id];
37161     },
37162
37163     /**
37164      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37165      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37166      */
37167     hideTab : function(id){
37168         var t = this.items[id];
37169         if(!t.isHidden()){
37170            t.setHidden(true);
37171            this.hiddenCount++;
37172            this.autoSizeTabs();
37173         }
37174     },
37175
37176     /**
37177      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37178      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37179      */
37180     unhideTab : function(id){
37181         var t = this.items[id];
37182         if(t.isHidden()){
37183            t.setHidden(false);
37184            this.hiddenCount--;
37185            this.autoSizeTabs();
37186         }
37187     },
37188
37189     /**
37190      * Adds an existing {@link Roo.TabPanelItem}.
37191      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37192      */
37193     addTabItem : function(item){
37194         this.items[item.id] = item;
37195         this.items.push(item);
37196       //  if(this.resizeTabs){
37197     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37198   //         this.autoSizeTabs();
37199 //        }else{
37200 //            item.autoSize();
37201        // }
37202     },
37203
37204     /**
37205      * Removes a {@link Roo.TabPanelItem}.
37206      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37207      */
37208     removeTab : function(id){
37209         var items = this.items;
37210         var tab = items[id];
37211         if(!tab) { return; }
37212         var index = items.indexOf(tab);
37213         if(this.active == tab && items.length > 1){
37214             var newTab = this.getNextAvailable(index);
37215             if(newTab) {
37216                 newTab.activate();
37217             }
37218         }
37219         this.stripEl.dom.removeChild(tab.pnode.dom);
37220         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37221             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37222         }
37223         items.splice(index, 1);
37224         delete this.items[tab.id];
37225         tab.fireEvent("close", tab);
37226         tab.purgeListeners();
37227         this.autoSizeTabs();
37228     },
37229
37230     getNextAvailable : function(start){
37231         var items = this.items;
37232         var index = start;
37233         // look for a next tab that will slide over to
37234         // replace the one being removed
37235         while(index < items.length){
37236             var item = items[++index];
37237             if(item && !item.isHidden()){
37238                 return item;
37239             }
37240         }
37241         // if one isn't found select the previous tab (on the left)
37242         index = start;
37243         while(index >= 0){
37244             var item = items[--index];
37245             if(item && !item.isHidden()){
37246                 return item;
37247             }
37248         }
37249         return null;
37250     },
37251
37252     /**
37253      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37254      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37255      */
37256     disableTab : function(id){
37257         var tab = this.items[id];
37258         if(tab && this.active != tab){
37259             tab.disable();
37260         }
37261     },
37262
37263     /**
37264      * Enables a {@link Roo.TabPanelItem} that is disabled.
37265      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37266      */
37267     enableTab : function(id){
37268         var tab = this.items[id];
37269         tab.enable();
37270     },
37271
37272     /**
37273      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37274      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37275      * @return {Roo.TabPanelItem} The TabPanelItem.
37276      */
37277     activate : function(id){
37278         var tab = this.items[id];
37279         if(!tab){
37280             return null;
37281         }
37282         if(tab == this.active || tab.disabled){
37283             return tab;
37284         }
37285         var e = {};
37286         this.fireEvent("beforetabchange", this, e, tab);
37287         if(e.cancel !== true && !tab.disabled){
37288             if(this.active){
37289                 this.active.hide();
37290             }
37291             this.active = this.items[id];
37292             this.active.show();
37293             this.fireEvent("tabchange", this, this.active);
37294         }
37295         return tab;
37296     },
37297
37298     /**
37299      * Gets the active {@link Roo.TabPanelItem}.
37300      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37301      */
37302     getActiveTab : function(){
37303         return this.active;
37304     },
37305
37306     /**
37307      * Updates the tab body element to fit the height of the container element
37308      * for overflow scrolling
37309      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37310      */
37311     syncHeight : function(targetHeight){
37312         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37313         var bm = this.bodyEl.getMargins();
37314         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37315         this.bodyEl.setHeight(newHeight);
37316         return newHeight;
37317     },
37318
37319     onResize : function(){
37320         if(this.monitorResize){
37321             this.autoSizeTabs();
37322         }
37323     },
37324
37325     /**
37326      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37327      */
37328     beginUpdate : function(){
37329         this.updating = true;
37330     },
37331
37332     /**
37333      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37334      */
37335     endUpdate : function(){
37336         this.updating = false;
37337         this.autoSizeTabs();
37338     },
37339
37340     /**
37341      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37342      */
37343     autoSizeTabs : function(){
37344         var count = this.items.length;
37345         var vcount = count - this.hiddenCount;
37346         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37347             return;
37348         }
37349         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37350         var availWidth = Math.floor(w / vcount);
37351         var b = this.stripBody;
37352         if(b.getWidth() > w){
37353             var tabs = this.items;
37354             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37355             if(availWidth < this.minTabWidth){
37356                 /*if(!this.sleft){    // incomplete scrolling code
37357                     this.createScrollButtons();
37358                 }
37359                 this.showScroll();
37360                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37361             }
37362         }else{
37363             if(this.currentTabWidth < this.preferredTabWidth){
37364                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37365             }
37366         }
37367     },
37368
37369     /**
37370      * Returns the number of tabs in this TabPanel.
37371      * @return {Number}
37372      */
37373      getCount : function(){
37374          return this.items.length;
37375      },
37376
37377     /**
37378      * Resizes all the tabs to the passed width
37379      * @param {Number} The new width
37380      */
37381     setTabWidth : function(width){
37382         this.currentTabWidth = width;
37383         for(var i = 0, len = this.items.length; i < len; i++) {
37384                 if(!this.items[i].isHidden()) {
37385                 this.items[i].setWidth(width);
37386             }
37387         }
37388     },
37389
37390     /**
37391      * Destroys this TabPanel
37392      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37393      */
37394     destroy : function(removeEl){
37395         Roo.EventManager.removeResizeListener(this.onResize, this);
37396         for(var i = 0, len = this.items.length; i < len; i++){
37397             this.items[i].purgeListeners();
37398         }
37399         if(removeEl === true){
37400             this.el.update("");
37401             this.el.remove();
37402         }
37403     },
37404     
37405     createStrip : function(container)
37406     {
37407         var strip = document.createElement("nav");
37408         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37409         container.appendChild(strip);
37410         return strip;
37411     },
37412     
37413     createStripList : function(strip)
37414     {
37415         // div wrapper for retard IE
37416         // returns the "tr" element.
37417         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37418         //'<div class="x-tabs-strip-wrap">'+
37419           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37420           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37421         return strip.firstChild; //.firstChild.firstChild.firstChild;
37422     },
37423     createBody : function(container)
37424     {
37425         var body = document.createElement("div");
37426         Roo.id(body, "tab-body");
37427         //Roo.fly(body).addClass("x-tabs-body");
37428         Roo.fly(body).addClass("tab-content");
37429         container.appendChild(body);
37430         return body;
37431     },
37432     createItemBody :function(bodyEl, id){
37433         var body = Roo.getDom(id);
37434         if(!body){
37435             body = document.createElement("div");
37436             body.id = id;
37437         }
37438         //Roo.fly(body).addClass("x-tabs-item-body");
37439         Roo.fly(body).addClass("tab-pane");
37440          bodyEl.insertBefore(body, bodyEl.firstChild);
37441         return body;
37442     },
37443     /** @private */
37444     createStripElements :  function(stripEl, text, closable, tpl)
37445     {
37446         var td = document.createElement("li"); // was td..
37447         
37448         
37449         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37450         
37451         
37452         stripEl.appendChild(td);
37453         /*if(closable){
37454             td.className = "x-tabs-closable";
37455             if(!this.closeTpl){
37456                 this.closeTpl = new Roo.Template(
37457                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37458                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37459                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37460                 );
37461             }
37462             var el = this.closeTpl.overwrite(td, {"text": text});
37463             var close = el.getElementsByTagName("div")[0];
37464             var inner = el.getElementsByTagName("em")[0];
37465             return {"el": el, "close": close, "inner": inner};
37466         } else {
37467         */
37468         // not sure what this is..
37469 //            if(!this.tabTpl){
37470                 //this.tabTpl = new Roo.Template(
37471                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37472                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37473                 //);
37474 //                this.tabTpl = new Roo.Template(
37475 //                   '<a href="#">' +
37476 //                   '<span unselectable="on"' +
37477 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37478 //                            ' >{text}</span></a>'
37479 //                );
37480 //                
37481 //            }
37482
37483
37484             var template = tpl || this.tabTpl || false;
37485             
37486             if(!template){
37487                 
37488                 template = new Roo.Template(
37489                    '<a href="#">' +
37490                    '<span unselectable="on"' +
37491                             (this.disableTooltips ? '' : ' title="{text}"') +
37492                             ' >{text}</span></a>'
37493                 );
37494             }
37495             
37496             switch (typeof(template)) {
37497                 case 'object' :
37498                     break;
37499                 case 'string' :
37500                     template = new Roo.Template(template);
37501                     break;
37502                 default :
37503                     break;
37504             }
37505             
37506             var el = template.overwrite(td, {"text": text});
37507             
37508             var inner = el.getElementsByTagName("span")[0];
37509             
37510             return {"el": el, "inner": inner};
37511             
37512     }
37513         
37514     
37515 });
37516
37517 /**
37518  * @class Roo.TabPanelItem
37519  * @extends Roo.util.Observable
37520  * Represents an individual item (tab plus body) in a TabPanel.
37521  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37522  * @param {String} id The id of this TabPanelItem
37523  * @param {String} text The text for the tab of this TabPanelItem
37524  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37525  */
37526 Roo.bootstrap.panel.TabItem = function(config){
37527     /**
37528      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37529      * @type Roo.TabPanel
37530      */
37531     this.tabPanel = config.panel;
37532     /**
37533      * The id for this TabPanelItem
37534      * @type String
37535      */
37536     this.id = config.id;
37537     /** @private */
37538     this.disabled = false;
37539     /** @private */
37540     this.text = config.text;
37541     /** @private */
37542     this.loaded = false;
37543     this.closable = config.closable;
37544
37545     /**
37546      * The body element for this TabPanelItem.
37547      * @type Roo.Element
37548      */
37549     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37550     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37551     this.bodyEl.setStyle("display", "block");
37552     this.bodyEl.setStyle("zoom", "1");
37553     //this.hideAction();
37554
37555     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37556     /** @private */
37557     this.el = Roo.get(els.el);
37558     this.inner = Roo.get(els.inner, true);
37559     this.textEl = Roo.get(this.el.dom.firstChild, true);
37560     this.pnode = Roo.get(els.el.parentNode, true);
37561     this.el.on("mousedown", this.onTabMouseDown, this);
37562     this.el.on("click", this.onTabClick, this);
37563     /** @private */
37564     if(config.closable){
37565         var c = Roo.get(els.close, true);
37566         c.dom.title = this.closeText;
37567         c.addClassOnOver("close-over");
37568         c.on("click", this.closeClick, this);
37569      }
37570
37571     this.addEvents({
37572          /**
37573          * @event activate
37574          * Fires when this tab becomes the active tab.
37575          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37576          * @param {Roo.TabPanelItem} this
37577          */
37578         "activate": true,
37579         /**
37580          * @event beforeclose
37581          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37582          * @param {Roo.TabPanelItem} this
37583          * @param {Object} e Set cancel to true on this object to cancel the close.
37584          */
37585         "beforeclose": true,
37586         /**
37587          * @event close
37588          * Fires when this tab is closed.
37589          * @param {Roo.TabPanelItem} this
37590          */
37591          "close": true,
37592         /**
37593          * @event deactivate
37594          * Fires when this tab is no longer the active tab.
37595          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37596          * @param {Roo.TabPanelItem} this
37597          */
37598          "deactivate" : true
37599     });
37600     this.hidden = false;
37601
37602     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37603 };
37604
37605 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37606            {
37607     purgeListeners : function(){
37608        Roo.util.Observable.prototype.purgeListeners.call(this);
37609        this.el.removeAllListeners();
37610     },
37611     /**
37612      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37613      */
37614     show : function(){
37615         this.pnode.addClass("active");
37616         this.showAction();
37617         if(Roo.isOpera){
37618             this.tabPanel.stripWrap.repaint();
37619         }
37620         this.fireEvent("activate", this.tabPanel, this);
37621     },
37622
37623     /**
37624      * Returns true if this tab is the active tab.
37625      * @return {Boolean}
37626      */
37627     isActive : function(){
37628         return this.tabPanel.getActiveTab() == this;
37629     },
37630
37631     /**
37632      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37633      */
37634     hide : function(){
37635         this.pnode.removeClass("active");
37636         this.hideAction();
37637         this.fireEvent("deactivate", this.tabPanel, this);
37638     },
37639
37640     hideAction : function(){
37641         this.bodyEl.hide();
37642         this.bodyEl.setStyle("position", "absolute");
37643         this.bodyEl.setLeft("-20000px");
37644         this.bodyEl.setTop("-20000px");
37645     },
37646
37647     showAction : function(){
37648         this.bodyEl.setStyle("position", "relative");
37649         this.bodyEl.setTop("");
37650         this.bodyEl.setLeft("");
37651         this.bodyEl.show();
37652     },
37653
37654     /**
37655      * Set the tooltip for the tab.
37656      * @param {String} tooltip The tab's tooltip
37657      */
37658     setTooltip : function(text){
37659         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37660             this.textEl.dom.qtip = text;
37661             this.textEl.dom.removeAttribute('title');
37662         }else{
37663             this.textEl.dom.title = text;
37664         }
37665     },
37666
37667     onTabClick : function(e){
37668         e.preventDefault();
37669         this.tabPanel.activate(this.id);
37670     },
37671
37672     onTabMouseDown : function(e){
37673         e.preventDefault();
37674         this.tabPanel.activate(this.id);
37675     },
37676 /*
37677     getWidth : function(){
37678         return this.inner.getWidth();
37679     },
37680
37681     setWidth : function(width){
37682         var iwidth = width - this.pnode.getPadding("lr");
37683         this.inner.setWidth(iwidth);
37684         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37685         this.pnode.setWidth(width);
37686     },
37687 */
37688     /**
37689      * Show or hide the tab
37690      * @param {Boolean} hidden True to hide or false to show.
37691      */
37692     setHidden : function(hidden){
37693         this.hidden = hidden;
37694         this.pnode.setStyle("display", hidden ? "none" : "");
37695     },
37696
37697     /**
37698      * Returns true if this tab is "hidden"
37699      * @return {Boolean}
37700      */
37701     isHidden : function(){
37702         return this.hidden;
37703     },
37704
37705     /**
37706      * Returns the text for this tab
37707      * @return {String}
37708      */
37709     getText : function(){
37710         return this.text;
37711     },
37712     /*
37713     autoSize : function(){
37714         //this.el.beginMeasure();
37715         this.textEl.setWidth(1);
37716         /*
37717          *  #2804 [new] Tabs in Roojs
37718          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37719          */
37720         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37721         //this.el.endMeasure();
37722     //},
37723
37724     /**
37725      * Sets the text for the tab (Note: this also sets the tooltip text)
37726      * @param {String} text The tab's text and tooltip
37727      */
37728     setText : function(text){
37729         this.text = text;
37730         this.textEl.update(text);
37731         this.setTooltip(text);
37732         //if(!this.tabPanel.resizeTabs){
37733         //    this.autoSize();
37734         //}
37735     },
37736     /**
37737      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37738      */
37739     activate : function(){
37740         this.tabPanel.activate(this.id);
37741     },
37742
37743     /**
37744      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37745      */
37746     disable : function(){
37747         if(this.tabPanel.active != this){
37748             this.disabled = true;
37749             this.pnode.addClass("disabled");
37750         }
37751     },
37752
37753     /**
37754      * Enables this TabPanelItem if it was previously disabled.
37755      */
37756     enable : function(){
37757         this.disabled = false;
37758         this.pnode.removeClass("disabled");
37759     },
37760
37761     /**
37762      * Sets the content for this TabPanelItem.
37763      * @param {String} content The content
37764      * @param {Boolean} loadScripts true to look for and load scripts
37765      */
37766     setContent : function(content, loadScripts){
37767         this.bodyEl.update(content, loadScripts);
37768     },
37769
37770     /**
37771      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37772      * @return {Roo.UpdateManager} The UpdateManager
37773      */
37774     getUpdateManager : function(){
37775         return this.bodyEl.getUpdateManager();
37776     },
37777
37778     /**
37779      * Set a URL to be used to load the content for this TabPanelItem.
37780      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37781      * @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)
37782      * @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)
37783      * @return {Roo.UpdateManager} The UpdateManager
37784      */
37785     setUrl : function(url, params, loadOnce){
37786         if(this.refreshDelegate){
37787             this.un('activate', this.refreshDelegate);
37788         }
37789         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37790         this.on("activate", this.refreshDelegate);
37791         return this.bodyEl.getUpdateManager();
37792     },
37793
37794     /** @private */
37795     _handleRefresh : function(url, params, loadOnce){
37796         if(!loadOnce || !this.loaded){
37797             var updater = this.bodyEl.getUpdateManager();
37798             updater.update(url, params, this._setLoaded.createDelegate(this));
37799         }
37800     },
37801
37802     /**
37803      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37804      *   Will fail silently if the setUrl method has not been called.
37805      *   This does not activate the panel, just updates its content.
37806      */
37807     refresh : function(){
37808         if(this.refreshDelegate){
37809            this.loaded = false;
37810            this.refreshDelegate();
37811         }
37812     },
37813
37814     /** @private */
37815     _setLoaded : function(){
37816         this.loaded = true;
37817     },
37818
37819     /** @private */
37820     closeClick : function(e){
37821         var o = {};
37822         e.stopEvent();
37823         this.fireEvent("beforeclose", this, o);
37824         if(o.cancel !== true){
37825             this.tabPanel.removeTab(this.id);
37826         }
37827     },
37828     /**
37829      * The text displayed in the tooltip for the close icon.
37830      * @type String
37831      */
37832     closeText : "Close this tab"
37833 });
37834 /*
37835  * - LGPL
37836  *
37837  * element
37838  * 
37839  */
37840
37841 /**
37842  * @class Roo.bootstrap.PhoneInput
37843  * @extends Roo.bootstrap.TriggerField
37844  * Bootstrap PhoneInput class
37845  * 
37846  * @constructor
37847  * Create a new PhoneInput
37848  * @param {Object} config The config object
37849 */
37850
37851 Roo.bootstrap.PhoneInput = function(config){
37852     
37853     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37854     
37855     this.addEvents({
37856         /**
37857          * @event expand
37858          * Fires when the dropdown list is expanded
37859              * @param {Roo.bootstrap.ComboBox} combo This combo box
37860              */
37861         'expand' : true,
37862         /**
37863          * @event collapse
37864          * Fires when the dropdown list is collapsed
37865              * @param {Roo.bootstrap.ComboBox} combo This combo box
37866              */
37867         'collapse' : true,
37868         /**
37869          * @event beforeselect
37870          * Fires before a list item is selected. Return false to cancel the selection.
37871              * @param {Roo.bootstrap.ComboBox} combo This combo box
37872              * @param {Roo.data.Record} record The data record returned from the underlying store
37873              * @param {Number} index The index of the selected item in the dropdown list
37874              */
37875         'beforeselect' : true,
37876         /**
37877          * @event select
37878          * Fires when a list item is selected
37879              * @param {Roo.bootstrap.ComboBox} combo This combo box
37880              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37881              * @param {Number} index The index of the selected item in the dropdown list
37882              */
37883         'select' : true,
37884         /**
37885          * @event beforequery
37886          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37887          * The event object passed has these properties:
37888              * @param {Roo.bootstrap.ComboBox} combo This combo box
37889              * @param {String} query The query
37890              * @param {Boolean} forceAll true to force "all" query
37891              * @param {Boolean} cancel true to cancel the query
37892              * @param {Object} e The query event object
37893              */
37894         'beforequery': true,
37895          /**
37896          * @event add
37897          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37898              * @param {Roo.bootstrap.ComboBox} combo This combo box
37899              */
37900         'add' : true,
37901         /**
37902          * @event edit
37903          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37904              * @param {Roo.bootstrap.ComboBox} combo This combo box
37905              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37906              */
37907         'edit' : true,
37908         /**
37909          * @event remove
37910          * Fires when the remove value from the combobox array
37911              * @param {Roo.bootstrap.ComboBox} combo This combo box
37912              */
37913         'remove' : true,
37914         /**
37915          * @event afterremove
37916          * Fires when the remove value from the combobox array
37917              * @param {Roo.bootstrap.ComboBox} combo This combo box
37918              */
37919         'afterremove' : true,
37920         /**
37921          * @event specialfilter
37922          * Fires when specialfilter
37923             * @param {Roo.bootstrap.ComboBox} combo This combo box
37924             */
37925         'touchviewdisplay' : true
37926     });
37927     
37928     this.country = []; //fetch country JSON
37929 };
37930
37931 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37932      
37933      listWidth: undefined,
37934      
37935      modalTitle : '', 
37936      
37937      selectedClass: 'active',
37938      
37939      maxHeight: 300,
37940      
37941      minListWidth : 70,
37942      
37943      triggerAction: 'query',
37944      
37945      validClass : "has-success",
37946      
37947      invalidClass: "has-warning",
37948      
37949      //new settings
37950      defaultCountry: 'hk',
37951      
37952      preferedCountries: undefined, //array
37953      
37954      filterCountries: undefined, //array
37955      
37956      displayMode: undefined, //string
37957      
37958      getAutoCreate : function(){
37959     
37960          this.list = Roo.bootstrap.PhoneInput.List;
37961     
37962         if(this.filterCountries) {
37963             for(var i = 0; i < this.filterCountries.length; i++) {
37964                 delete this.list[this.filterCountries[i]];
37965             }
37966         }
37967         
37968         if (this.preferedCountries) {
37969             //another list??
37970         }
37971         
37972          var align = this.labelAlign || this.parentLabelAlign();
37973          
37974          var id = Roo.id(); //all el??
37975          
37976          var cfg = {
37977              cls: 'form-group'
37978          };
37979          
37980          var input =  {
37981              tag: 'input',
37982              id : id,
37983              type : this.inputType,
37984              cls : 'form-control',
37985              style: 'padding-left: 60px;',
37986              placeholder : this.placeholder || ''
37987          };
37988          
37989          if (this.name) {
37990              input.name = this.name;
37991          }
37992          if (this.size) {
37993              input.cls += ' input-' + this.size;
37994          }
37995          
37996          if (this.disabled) {
37997              input.disabled=true;
37998          }
37999          
38000          var inputblock = input;
38001          
38002          if(this.hasFeedback && !this.allowBlank){
38003              var feedback = {
38004                  tag: 'span',
38005                  cls: 'glyphicon form-control-feedback'
38006              };
38007          }
38008          
38009          inputblock = {
38010              cn :  []
38011          };
38012          
38013          inputblock.cn.push(input);
38014          
38015          if(this.hasFeedback && !this.allowBlank){
38016              inputblock.cls += 'has-feedback';
38017              inputblock.cn.push(feedback);
38018          }
38019          
38020          var box = {
38021              tag: 'div',
38022              cn: [
38023                  {
38024                      tag: 'input',
38025                      type : 'hidden',
38026                      cls: 'form-hidden-field'
38027                  },
38028                  inputblock
38029              ]
38030          };
38031          
38032          var flag = {
38033              tag: 'span',
38034              html: 'flag',
38035              style: 'margin-right:5px',
38036              cls: 'roo-selected-region',
38037              cn: [] //flag position ... (iti-flag-us)
38038          };
38039          
38040          var caret = {
38041              tag: 'span',
38042              cls: 'caret'
38043           };
38044           
38045          if (this.caret != false) {
38046              caret = {
38047                   tag: 'i',
38048                   cls: 'fa fa-' + this.caret
38049              };
38050          }
38051          
38052          var combobox = {
38053              cls: 'roo-select2-container input-group',
38054              cn: []
38055          };
38056          
38057          combobox.cn.push({
38058              tag :'span',
38059              cls : 'input-group-addon btn dropdown-toggle',
38060              style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38061              cn : [
38062                  flag,
38063                  caret,
38064                  {
38065                      tag: 'span',
38066                      cls: 'combobox-clear',
38067                      cn  : [
38068                          {
38069                              tag : 'i',
38070                              cls: 'icon-remove'
38071                          }
38072                      ]
38073                  }
38074              ]
38075          });
38076          
38077          combobox.cn.push(box);
38078          
38079          if (align ==='left' && this.fieldLabel.length) {
38080              
38081              cfg.cls += ' roo-form-group-label-left';
38082
38083              cfg.cn = [
38084                  {
38085                      tag : 'i',
38086                      cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38087                      tooltip : 'This field is required'
38088                  },
38089                  {
38090                      tag: 'label',
38091                      'for' :  id,
38092                      cls : 'control-label',
38093                      html : this.fieldLabel
38094
38095                  },
38096                  {
38097                      cls : "", 
38098                      cn: [
38099                          combobox
38100                      ]
38101                  }
38102              ];
38103              
38104              var labelCfg = cfg.cn[1];
38105              var contentCfg = cfg.cn[2];
38106              
38107              if(this.indicatorpos == 'right'){
38108                  cfg.cn = [
38109                      {
38110                          tag: 'label',
38111                          'for' :  id,
38112                          cls : 'control-label',
38113                          cn : [
38114                              {
38115                                  tag : 'span',
38116                                  html : this.fieldLabel
38117                              },
38118                              {
38119                                  tag : 'i',
38120                                  cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38121                                  tooltip : 'This field is required'
38122                              }
38123                          ]
38124                      },
38125                      {
38126                          cls : "", 
38127                          cn: [
38128                              combobox
38129                          ]
38130                      }
38131
38132                  ];
38133                  
38134                  labelCfg = cfg.cn[0];
38135                  contentCfg = cfg.cn[1];
38136              }
38137              
38138              if(this.labelWidth > 12){
38139                  labelCfg.style = "width: " + this.labelWidth + 'px';
38140              }
38141              
38142              if(this.labelWidth < 13 && this.labelmd == 0){
38143                  this.labelmd = this.labelWidth;
38144              }
38145              
38146              if(this.labellg > 0){
38147                  labelCfg.cls += ' col-lg-' + this.labellg;
38148                  contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38149              }
38150              
38151              if(this.labelmd > 0){
38152                  labelCfg.cls += ' col-md-' + this.labelmd;
38153                  contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38154              }
38155              
38156              if(this.labelsm > 0){
38157                  labelCfg.cls += ' col-sm-' + this.labelsm;
38158                  contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38159              }
38160              
38161              if(this.labelxs > 0){
38162                  labelCfg.cls += ' col-xs-' + this.labelxs;
38163                  contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38164              }
38165              
38166          } else if ( this.fieldLabel.length) {
38167  //                Roo.log(" label");
38168              cfg.cn = [
38169                  {
38170                     tag : 'i',
38171                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38172                     tooltip : 'This field is required'
38173                 },
38174                 {
38175                     tag: 'label',
38176                     //cls : 'input-group-addon',
38177                     html : this.fieldLabel
38178
38179                 },
38180
38181                 combobox
38182
38183              ];
38184              
38185              if(this.indicatorpos == 'right'){
38186                  
38187                  cfg.cn = [
38188                      {
38189                         tag: 'label',
38190                         cn : [
38191                             {
38192                                 tag : 'span',
38193                                 html : this.fieldLabel
38194                             },
38195                             {
38196                                tag : 'i',
38197                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38198                                tooltip : 'This field is required'
38199                             }
38200                         ]
38201                      },
38202                      combobox
38203                  ];
38204              }
38205          } else {
38206                  cfg = combobox
38207          }
38208          
38209          var settings=this;
38210          ['xs','sm','md','lg'].map(function(size){
38211              if (settings[size]) {
38212                  cfg.cls += ' col-' + size + '-' + settings[size];
38213              }
38214          });
38215          
38216          return cfg;
38217      },
38218      
38219      _initEventsCalled : false,
38220      
38221      initEvents: function()
38222      {   
38223          if (this._initEventsCalled) {
38224              return;
38225          }
38226          
38227          this._initEventsCalled = true;
38228          
38229          this.store =  new Roo.data.SimpleStore({
38230              data : this.list,
38231              fields : ['name','iso','dial_code','order','area_code']
38232          });
38233          
38234          this.store = Roo.factory(this.store, Roo.data);
38235          this.store.parent = this;
38236          
38237          Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38238          
38239          var _this = this;
38240          
38241          (function(){
38242              var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38243              _this.list.setWidth(lw);
38244          }).defer(100);
38245          
38246          this.list.on('mouseover', this.onViewOver, this);
38247          this.list.on('mousemove', this.onViewMove, this);
38248          this.list.on('scroll', this.onViewScroll, this);
38249          
38250          if(!this.tpl){
38251              this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38252          }
38253
38254          this.view = new Roo.View(this.list, this.tpl, {
38255              singleSelect:true, store: this.store, selectedClass: this.selectedClass
38256          });
38257          
38258          this.view.on('click', this.onViewClick, this);
38259          
38260          this.store.on('beforeload', this.onBeforeLoad, this);
38261          this.store.on('load', this.onLoad, this);
38262          this.store.on('loadexception', this.onLoadException, this);
38263          
38264          this.keyNav = new Roo.KeyNav(this.inputEl(), {
38265              "up" : function(e){
38266                  this.inKeyMode = true;
38267                  this.selectPrev();
38268              },
38269
38270              "down" : function(e){
38271                  if(!this.isExpanded()){
38272                      this.onTriggerClick();
38273                  }else{
38274                      this.inKeyMode = true;
38275                      this.selectNext();
38276                  }
38277              },
38278
38279              "enter" : function(e){
38280  //                this.onViewClick();
38281                  //return true;
38282                  this.collapse();
38283                  
38284                  if(this.fireEvent("specialkey", this, e)){
38285                      this.onViewClick(false);
38286                  }
38287                  
38288                  return true;
38289              },
38290
38291              "esc" : function(e){
38292                  this.collapse();
38293              },
38294
38295              "tab" : function(e){
38296                  this.collapse();
38297                  
38298                  if(this.fireEvent("specialkey", this, e)){
38299                      this.onViewClick(false);
38300                  }
38301                  
38302                  return true;
38303              },
38304
38305              scope : this,
38306
38307              doRelay : function(foo, bar, hname){
38308                  if(hname == 'down' || this.scope.isExpanded()){
38309                     return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38310                  }
38311                  return true;
38312              },
38313
38314              forceKeyDown: true
38315          });
38316          
38317     },
38318     
38319     onViewOver : function(e, t){
38320         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38321             return;
38322         }
38323         var item = this.view.findItemFromChild(t);
38324         
38325         if(item){
38326             var index = this.view.indexOf(item);
38327             this.select(index, false);
38328         }
38329     },
38330     
38331     onViewMove : function(e, t){
38332         this.inKeyMode = false;
38333     },
38334     
38335     onViewScroll : function(e, t){
38336         
38337         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){
38338             return;
38339         }
38340         
38341         this.hasQuery = true;
38342         
38343         this.loading = this.list.select('.loading', true).first();
38344         
38345         if(this.loading === null){
38346             this.list.createChild({
38347                 tag: 'div',
38348                 cls: 'loading roo-select2-more-results roo-select2-active',
38349                 html: 'Loading more results...'
38350             });
38351             
38352             this.loading = this.list.select('.loading', true).first();
38353             
38354             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38355             
38356             this.loading.hide();
38357         }
38358         
38359         this.loading.show();
38360         
38361         var _combo = this;
38362         
38363         this.page++;
38364         this.loadNext = true;
38365         
38366         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38367         
38368         return;
38369     },
38370     
38371     onTriggerClick : function(e)
38372     {
38373         Roo.log('trigger click');
38374         
38375         if(this.disabled || !this.triggerList){
38376             return;
38377         }
38378         
38379         this.page = 0;
38380         this.loadNext = false;
38381         
38382         if(this.isExpanded()){
38383             this.collapse();
38384             if (!this.blockFocus) {
38385                 this.inputEl().focus();
38386             }
38387             
38388         }else {
38389             this.hasFocus = true;
38390             if(this.triggerAction == 'all') {
38391                 this.doQuery(this.allQuery, true);
38392             } else {
38393                 this.doQuery(this.getRawValue());
38394             }
38395             if (!this.blockFocus) {
38396                 this.inputEl().focus();
38397             }
38398         }
38399     }
38400     
38401  });
38402
38403  Roo.apply(Roo.bootstrap.PhoneInput, {
38404      
38405      /**
38406       * iso2 and abbr for all countries
38407       * @type Object
38408       */
38409      List : [
38410          ["Afghanistan (‫افغانستان‬‎)", "af", "93"],
38411          ["Albania (Shqipëri)", "al", "355"],
38412          ["Algeria (‫الجزائر‬‎)", "dz", "213"],
38413          ["American Samoa", "as", "1684"],
38414          ["Andorra", "ad", "376"],
38415          ["Angola", "ao", "244"],
38416          ["Anguilla", "ai", "1264"],
38417          ["Antigua and Barbuda", "ag", "1268"],
38418          ["Argentina", "ar", "54"],
38419          ["Armenia (Հայաստան)", "am", "374"],
38420          ["Aruba", "aw", "297"],
38421          ["Australia", "au", "61", 0],
38422          ["Austria (Österreich)", "at", "43"],
38423          ["Azerbaijan (Azərbaycan)", "az", "994"],
38424          ["Bahamas", "bs", "1242"],
38425          ["Bahrain (‫البحرين‬‎)", "bh", "973"],
38426          ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38427          ["Barbados", "bb", "1246"],
38428          ["Belarus (Беларусь)", "by", "375"],
38429          ["Belgium (België)", "be", "32"],
38430          ["Belize", "bz", "501"],
38431          ["Benin (Bénin)", "bj", "229"],
38432          ["Bermuda", "bm", "1441"],
38433          ["Bhutan (འབྲུག)", "bt", "975"],
38434          ["Bolivia", "bo", "591"],
38435          ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38436          ["Botswana", "bw", "267"],
38437          ["Brazil (Brasil)", "br", "55"],
38438          ["British Indian Ocean Territory", "io", "246"],
38439          ["British Virgin Islands", "vg", "1284"],
38440          ["Brunei", "bn", "673"],
38441          ["Bulgaria (България)", "bg", "359"],
38442          ["Burkina Faso", "bf", "226"],
38443          ["Burundi (Uburundi)", "bi", "257"],
38444          ["Cambodia (កម្ពុជា)", "kh", "855"],
38445          ["Cameroon (Cameroun)", "cm", "237"],
38446          ["Canada", "ca", "1", 1, ["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"]],
38447          ["Cape Verde (Kabu Verdi)", "cv", "238"],
38448          ["Caribbean Netherlands", "bq", "599", 1],
38449          ["Cayman Islands", "ky", "1345"],
38450          ["Central African Republic (République centrafricaine)", "cf", "236"],
38451          ["Chad (Tchad)", "td", "235"],
38452          ["Chile", "cl", "56"],
38453          ["China (中国)", "cn", "86"],
38454          ["Christmas Island", "cx", "61", 2],
38455          ["Cocos (Keeling) Islands", "cc", "61", 1],
38456          ["Colombia", "co", "57"],
38457          ["Comoros (‫جزر القمر‬‎)", "km", "269"],
38458          ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38459          ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38460          ["Cook Islands", "ck", "682"],
38461          ["Costa Rica", "cr", "506"],
38462          ["Côte d’Ivoire", "ci", "225"],
38463          ["Croatia (Hrvatska)", "hr", "385"],
38464          ["Cuba", "cu", "53"],
38465          ["Curaçao", "cw", "599", 0],
38466          ["Cyprus (Κύπρος)", "cy", "357"],
38467          ["Czech Republic (Česká republika)", "cz", "420"],
38468          ["Denmark (Danmark)", "dk", "45"],
38469          ["Djibouti", "dj", "253"],
38470          ["Dominica", "dm", "1767"],
38471          ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38472          ["Ecuador", "ec", "593"],
38473          ["Egypt (‫مصر‬‎)", "eg", "20"],
38474          ["El Salvador", "sv", "503"],
38475          ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38476          ["Eritrea", "er", "291"],
38477          ["Estonia (Eesti)", "ee", "372"],
38478          ["Ethiopia", "et", "251"],
38479          ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38480          ["Faroe Islands (Føroyar)", "fo", "298"],
38481          ["Fiji", "fj", "679"],
38482          ["Finland (Suomi)", "fi", "358", 0],
38483          ["France", "fr", "33"],
38484          ["French Guiana (Guyane française)", "gf", "594"],
38485          ["French Polynesia (Polynésie française)", "pf", "689"],
38486          ["Gabon", "ga", "241"],
38487          ["Gambia", "gm", "220"],
38488          ["Georgia (საქართველო)", "ge", "995"],
38489          ["Germany (Deutschland)", "de", "49"],
38490          ["Ghana (Gaana)", "gh", "233"],
38491          ["Gibraltar", "gi", "350"],
38492          ["Greece (Ελλάδα)", "gr", "30"],
38493          ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38494          ["Grenada", "gd", "1473"],
38495          ["Guadeloupe", "gp", "590", 0],
38496          ["Guam", "gu", "1671"],
38497          ["Guatemala", "gt", "502"],
38498          ["Guernsey", "gg", "44", 1],
38499          ["Guinea (Guinée)", "gn", "224"],
38500          ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38501          ["Guyana", "gy", "592"],
38502          ["Haiti", "ht", "509"],
38503          ["Honduras", "hn", "504"],
38504          ["Hong Kong (香港)", "hk", "852"],
38505          ["Hungary (Magyarország)", "hu", "36"],
38506          ["Iceland (Ísland)", "is", "354"],
38507          ["India (भारत)", "in", "91"],
38508          ["Indonesia", "id", "62"],
38509          ["Iran (‫ایران‬‎)", "ir", "98"],
38510          ["Iraq (‫العراق‬‎)", "iq", "964"],
38511          ["Ireland", "ie", "353"],
38512          ["Isle of Man", "im", "44", 2],
38513          ["Israel (‫ישראל‬‎)", "il", "972"],
38514          ["Italy (Italia)", "it", "39", 0],
38515          ["Jamaica", "jm", "1876"],
38516          ["Japan (日本)", "jp", "81"],
38517          ["Jersey", "je", "44", 3],
38518          ["Jordan (‫الأردن‬‎)", "jo", "962"],
38519          ["Kazakhstan (Казахстан)", "kz", "7", 1],
38520          ["Kenya", "ke", "254"],
38521          ["Kiribati", "ki", "686"],
38522          ["Kosovo", "xk", "383"],
38523          ["Kuwait (‫الكويت‬‎)", "kw", "965"],
38524          ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38525          ["Laos (ລາວ)", "la", "856"],
38526          ["Latvia (Latvija)", "lv", "371"],
38527          ["Lebanon (‫لبنان‬‎)", "lb", "961"],
38528          ["Lesotho", "ls", "266"],
38529          ["Liberia", "lr", "231"],
38530          ["Libya (‫ليبيا‬‎)", "ly", "218"],
38531          ["Liechtenstein", "li", "423"],
38532          ["Lithuania (Lietuva)", "lt", "370"],
38533          ["Luxembourg", "lu", "352"],
38534          ["Macau (澳門)", "mo", "853"],
38535          ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38536          ["Madagascar (Madagasikara)", "mg", "261"],
38537          ["Malawi", "mw", "265"],
38538          ["Malaysia", "my", "60"],
38539          ["Maldives", "mv", "960"],
38540          ["Mali", "ml", "223"],
38541          ["Malta", "mt", "356"],
38542          ["Marshall Islands", "mh", "692"],
38543          ["Martinique", "mq", "596"],
38544          ["Mauritania (‫موريتانيا‬‎)", "mr", "222"],
38545          ["Mauritius (Moris)", "mu", "230"],
38546          ["Mayotte", "yt", "262", 1],
38547          ["Mexico (México)", "mx", "52"],
38548          ["Micronesia", "fm", "691"],
38549          ["Moldova (Republica Moldova)", "md", "373"],
38550          ["Monaco", "mc", "377"],
38551          ["Mongolia (Монгол)", "mn", "976"],
38552          ["Montenegro (Crna Gora)", "me", "382"],
38553          ["Montserrat", "ms", "1664"],
38554          ["Morocco (‫المغرب‬‎)", "ma", "212", 0],
38555          ["Mozambique (Moçambique)", "mz", "258"],
38556          ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38557          ["Namibia (Namibië)", "na", "264"],
38558          ["Nauru", "nr", "674"],
38559          ["Nepal (नेपाल)", "np", "977"],
38560          ["Netherlands (Nederland)", "nl", "31"],
38561          ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38562          ["New Zealand", "nz", "64"],
38563          ["Nicaragua", "ni", "505"],
38564          ["Niger (Nijar)", "ne", "227"],
38565          ["Nigeria", "ng", "234"],
38566          ["Niue", "nu", "683"],
38567          ["Norfolk Island", "nf", "672"],
38568          ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38569          ["Northern Mariana Islands", "mp", "1670"],
38570          ["Norway (Norge)", "no", "47", 0],
38571          ["Oman (‫عُمان‬‎)", "om", "968"],
38572          ["Pakistan (‫پاکستان‬‎)", "pk", "92"],
38573          ["Palau", "pw", "680"],
38574          ["Palestine (‫فلسطين‬‎)", "ps", "970"],
38575          ["Panama (Panamá)", "pa", "507"],
38576          ["Papua New Guinea", "pg", "675"],
38577          ["Paraguay", "py", "595"],
38578          ["Peru (Perú)", "pe", "51"],
38579          ["Philippines", "ph", "63"],
38580          ["Poland (Polska)", "pl", "48"],
38581          ["Portugal", "pt", "351"],
38582          ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38583          ["Qatar (‫قطر‬‎)", "qa", "974"],
38584          ["Réunion (La Réunion)", "re", "262", 0],
38585          ["Romania (România)", "ro", "40"],
38586          ["Russia (Россия)", "ru", "7", 0],
38587          ["Rwanda", "rw", "250"],
38588          ["Saint Barthélemy", "bl", "590", 1],
38589          ["Saint Helena", "sh", "290"],
38590          ["Saint Kitts and Nevis", "kn", "1869"],
38591          ["Saint Lucia", "lc", "1758"],
38592          ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38593          ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38594          ["Saint Vincent and the Grenadines", "vc", "1784"],
38595          ["Samoa", "ws", "685"],
38596          ["San Marino", "sm", "378"],
38597          ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38598          ["Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966"],
38599          ["Senegal (Sénégal)", "sn", "221"],
38600          ["Serbia (Србија)", "rs", "381"],
38601          ["Seychelles", "sc", "248"],
38602          ["Sierra Leone", "sl", "232"],
38603          ["Singapore", "sg", "65"],
38604          ["Sint Maarten", "sx", "1721"],
38605          ["Slovakia (Slovensko)", "sk", "421"],
38606          ["Slovenia (Slovenija)", "si", "386"],
38607          ["Solomon Islands", "sb", "677"],
38608          ["Somalia (Soomaaliya)", "so", "252"],
38609          ["South Africa", "za", "27"],
38610          ["South Korea (대한민국)", "kr", "82"],
38611          ["South Sudan (‫جنوب السودان‬‎)", "ss", "211"],
38612          ["Spain (España)", "es", "34"],
38613          ["Sri Lanka (ශ්‍රී ලංකාව)", "lk", "94"],
38614          ["Sudan (‫السودان‬‎)", "sd", "249"],
38615          ["Suriname", "sr", "597"],
38616          ["Svalbard and Jan Mayen", "sj", "47", 1],
38617          ["Swaziland", "sz", "268"],
38618          ["Sweden (Sverige)", "se", "46"],
38619          ["Switzerland (Schweiz)", "ch", "41"],
38620          ["Syria (‫سوريا‬‎)", "sy", "963"],
38621          ["Taiwan (台灣)", "tw", "886"],
38622          ["Tajikistan", "tj", "992"],
38623          ["Tanzania", "tz", "255"],
38624          ["Thailand (ไทย)", "th", "66"],
38625          ["Timor-Leste", "tl", "670"],
38626          ["Togo", "tg", "228"],
38627          ["Tokelau", "tk", "690"],
38628          ["Tonga", "to", "676"],
38629          ["Trinidad and Tobago", "tt", "1868"],
38630          ["Tunisia (‫تونس‬‎)", "tn", "216"],
38631          ["Turkey (Türkiye)", "tr", "90"],
38632          ["Turkmenistan", "tm", "993"],
38633          ["Turks and Caicos Islands", "tc", "1649"],
38634          ["Tuvalu", "tv", "688"],
38635          ["U.S. Virgin Islands", "vi", "1340"],
38636          ["Uganda", "ug", "256"],
38637          ["Ukraine (Україна)", "ua", "380"],
38638          ["United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971"],
38639          ["United Kingdom", "gb", "44", 0],
38640          ["United States", "us", "1", 0],
38641          ["Uruguay", "uy", "598"],
38642          ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38643          ["Vanuatu", "vu", "678"],
38644          ["Vatican City (Città del Vaticano)", "va", "39", 1],
38645          ["Venezuela", "ve", "58"],
38646          ["Vietnam (Việt Nam)", "vn", "84"],
38647          ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38648          ["Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1],
38649          ["Yemen (‫اليمن‬‎)", "ye", "967"],
38650          ["Zambia", "zm", "260"],
38651          ["Zimbabwe", "zw", "263"],
38652          ["Åland Islands", "ax", "358", 1]
38653      ]
38654  });