roojs-core.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     
8042     getItems : function()
8043     {
8044         var r=new Roo.util.MixedCollection(false, function(o){
8045             return o.id || (o.id = Roo.id());
8046         });
8047         var iter = function(el) {
8048             if (el.inputEl) {
8049                 r.add(el);
8050             }
8051             if (!el.items) {
8052                 return;
8053             }
8054             Roo.each(el.items,function(e) {
8055                 iter(e);
8056             });
8057
8058
8059         };
8060
8061         iter(this);
8062         return r;
8063         
8064     }
8065
8066 });
8067
8068 Roo.apply(Roo.bootstrap.Form, {
8069     
8070     popover : {
8071         
8072         padding : 5,
8073         
8074         isApplied : false,
8075         
8076         isMasked : false,
8077         
8078         form : false,
8079         
8080         target : false,
8081         
8082         toolTip : false,
8083         
8084         intervalID : false,
8085         
8086         maskEl : false,
8087         
8088         apply : function()
8089         {
8090             if(this.isApplied){
8091                 return;
8092             }
8093             
8094             this.maskEl = {
8095                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8096                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8097                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8098                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8099             };
8100             
8101             this.maskEl.top.enableDisplayMode("block");
8102             this.maskEl.left.enableDisplayMode("block");
8103             this.maskEl.bottom.enableDisplayMode("block");
8104             this.maskEl.right.enableDisplayMode("block");
8105             
8106             this.toolTip = new Roo.bootstrap.Tooltip({
8107                 cls : 'roo-form-error-popover',
8108                 alignment : {
8109                     'left' : ['r-l', [-2,0], 'right'],
8110                     'right' : ['l-r', [2,0], 'left'],
8111                     'bottom' : ['tl-bl', [0,2], 'top'],
8112                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8113                 }
8114             });
8115             
8116             this.toolTip.render(Roo.get(document.body));
8117
8118             this.toolTip.el.enableDisplayMode("block");
8119             
8120             Roo.get(document.body).on('click', function(){
8121                 this.unmask();
8122             }, this);
8123             
8124             Roo.get(document.body).on('touchstart', function(){
8125                 this.unmask();
8126             }, this);
8127             
8128             this.isApplied = true
8129         },
8130         
8131         mask : function(form, target)
8132         {
8133             this.form = form;
8134             
8135             this.target = target;
8136             
8137             if(!this.form.errorMask || !target.el){
8138                 return;
8139             }
8140             
8141             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8142             
8143             Roo.log(scrollable);
8144             
8145             var ot = this.target.el.calcOffsetsTo(scrollable);
8146             
8147             var scrollTo = ot[1] - this.form.maskOffset;
8148             
8149             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8150             
8151             scrollable.scrollTo('top', scrollTo);
8152             
8153             var box = this.target.el.getBox();
8154             Roo.log(box);
8155             var zIndex = Roo.bootstrap.Modal.zIndex++;
8156
8157             
8158             this.maskEl.top.setStyle('position', 'absolute');
8159             this.maskEl.top.setStyle('z-index', zIndex);
8160             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8161             this.maskEl.top.setLeft(0);
8162             this.maskEl.top.setTop(0);
8163             this.maskEl.top.show();
8164             
8165             this.maskEl.left.setStyle('position', 'absolute');
8166             this.maskEl.left.setStyle('z-index', zIndex);
8167             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8168             this.maskEl.left.setLeft(0);
8169             this.maskEl.left.setTop(box.y - this.padding);
8170             this.maskEl.left.show();
8171
8172             this.maskEl.bottom.setStyle('position', 'absolute');
8173             this.maskEl.bottom.setStyle('z-index', zIndex);
8174             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8175             this.maskEl.bottom.setLeft(0);
8176             this.maskEl.bottom.setTop(box.bottom + this.padding);
8177             this.maskEl.bottom.show();
8178
8179             this.maskEl.right.setStyle('position', 'absolute');
8180             this.maskEl.right.setStyle('z-index', zIndex);
8181             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8182             this.maskEl.right.setLeft(box.right + this.padding);
8183             this.maskEl.right.setTop(box.y - this.padding);
8184             this.maskEl.right.show();
8185
8186             this.toolTip.bindEl = this.target.el;
8187
8188             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8189
8190             var tip = this.target.blankText;
8191
8192             if(this.target.getValue() !== '' ) {
8193                 
8194                 if (this.target.invalidText.length) {
8195                     tip = this.target.invalidText;
8196                 } else if (this.target.regexText.length){
8197                     tip = this.target.regexText;
8198                 }
8199             }
8200
8201             this.toolTip.show(tip);
8202
8203             this.intervalID = window.setInterval(function() {
8204                 Roo.bootstrap.Form.popover.unmask();
8205             }, 10000);
8206
8207             window.onwheel = function(){ return false;};
8208             
8209             (function(){ this.isMasked = true; }).defer(500, this);
8210             
8211         },
8212         
8213         unmask : function()
8214         {
8215             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8216                 return;
8217             }
8218             
8219             this.maskEl.top.setStyle('position', 'absolute');
8220             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.top.hide();
8222
8223             this.maskEl.left.setStyle('position', 'absolute');
8224             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.left.hide();
8226
8227             this.maskEl.bottom.setStyle('position', 'absolute');
8228             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8229             this.maskEl.bottom.hide();
8230
8231             this.maskEl.right.setStyle('position', 'absolute');
8232             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8233             this.maskEl.right.hide();
8234             
8235             this.toolTip.hide();
8236             
8237             this.toolTip.el.hide();
8238             
8239             window.onwheel = function(){ return true;};
8240             
8241             if(this.intervalID){
8242                 window.clearInterval(this.intervalID);
8243                 this.intervalID = false;
8244             }
8245             
8246             this.isMasked = false;
8247             
8248         }
8249         
8250     }
8251     
8252 });
8253
8254 /*
8255  * Based on:
8256  * Ext JS Library 1.1.1
8257  * Copyright(c) 2006-2007, Ext JS, LLC.
8258  *
8259  * Originally Released Under LGPL - original licence link has changed is not relivant.
8260  *
8261  * Fork - LGPL
8262  * <script type="text/javascript">
8263  */
8264 /**
8265  * @class Roo.form.VTypes
8266  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8267  * @singleton
8268  */
8269 Roo.form.VTypes = function(){
8270     // closure these in so they are only created once.
8271     var alpha = /^[a-zA-Z_]+$/;
8272     var alphanum = /^[a-zA-Z0-9_]+$/;
8273     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8274     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8275
8276     // All these messages and functions are configurable
8277     return {
8278         /**
8279          * The function used to validate email addresses
8280          * @param {String} value The email address
8281          */
8282         'email' : function(v){
8283             return email.test(v);
8284         },
8285         /**
8286          * The error text to display when the email validation function returns false
8287          * @type String
8288          */
8289         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8290         /**
8291          * The keystroke filter mask to be applied on email input
8292          * @type RegExp
8293          */
8294         'emailMask' : /[a-z0-9_\.\-@]/i,
8295
8296         /**
8297          * The function used to validate URLs
8298          * @param {String} value The URL
8299          */
8300         'url' : function(v){
8301             return url.test(v);
8302         },
8303         /**
8304          * The error text to display when the url validation function returns false
8305          * @type String
8306          */
8307         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8308         
8309         /**
8310          * The function used to validate alpha values
8311          * @param {String} value The value
8312          */
8313         'alpha' : function(v){
8314             return alpha.test(v);
8315         },
8316         /**
8317          * The error text to display when the alpha validation function returns false
8318          * @type String
8319          */
8320         'alphaText' : 'This field should only contain letters and _',
8321         /**
8322          * The keystroke filter mask to be applied on alpha input
8323          * @type RegExp
8324          */
8325         'alphaMask' : /[a-z_]/i,
8326
8327         /**
8328          * The function used to validate alphanumeric values
8329          * @param {String} value The value
8330          */
8331         'alphanum' : function(v){
8332             return alphanum.test(v);
8333         },
8334         /**
8335          * The error text to display when the alphanumeric validation function returns false
8336          * @type String
8337          */
8338         'alphanumText' : 'This field should only contain letters, numbers and _',
8339         /**
8340          * The keystroke filter mask to be applied on alphanumeric input
8341          * @type RegExp
8342          */
8343         'alphanumMask' : /[a-z0-9_]/i
8344     };
8345 }();/*
8346  * - LGPL
8347  *
8348  * Input
8349  * 
8350  */
8351
8352 /**
8353  * @class Roo.bootstrap.Input
8354  * @extends Roo.bootstrap.Component
8355  * Bootstrap Input class
8356  * @cfg {Boolean} disabled is it disabled
8357  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8358  * @cfg {String} name name of the input
8359  * @cfg {string} fieldLabel - the label associated
8360  * @cfg {string} placeholder - placeholder to put in text.
8361  * @cfg {string}  before - input group add on before
8362  * @cfg {string} after - input group add on after
8363  * @cfg {string} size - (lg|sm) or leave empty..
8364  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8365  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8366  * @cfg {Number} md colspan out of 12 for computer-sized screens
8367  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8368  * @cfg {string} value default value of the input
8369  * @cfg {Number} labelWidth set the width of label 
8370  * @cfg {Number} labellg set the width of label (1-12)
8371  * @cfg {Number} labelmd set the width of label (1-12)
8372  * @cfg {Number} labelsm set the width of label (1-12)
8373  * @cfg {Number} labelxs set the width of label (1-12)
8374  * @cfg {String} labelAlign (top|left)
8375  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8376  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8377  * @cfg {String} indicatorpos (left|right) default left
8378
8379  * @cfg {String} align (left|center|right) Default left
8380  * @cfg {Boolean} forceFeedback (true|false) Default false
8381  * 
8382  * 
8383  * 
8384  * 
8385  * @constructor
8386  * Create a new Input
8387  * @param {Object} config The config object
8388  */
8389
8390 Roo.bootstrap.Input = function(config){
8391     
8392     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8393     
8394     this.addEvents({
8395         /**
8396          * @event focus
8397          * Fires when this field receives input focus.
8398          * @param {Roo.form.Field} this
8399          */
8400         focus : true,
8401         /**
8402          * @event blur
8403          * Fires when this field loses input focus.
8404          * @param {Roo.form.Field} this
8405          */
8406         blur : true,
8407         /**
8408          * @event specialkey
8409          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8410          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8411          * @param {Roo.form.Field} this
8412          * @param {Roo.EventObject} e The event object
8413          */
8414         specialkey : true,
8415         /**
8416          * @event change
8417          * Fires just before the field blurs if the field value has changed.
8418          * @param {Roo.form.Field} this
8419          * @param {Mixed} newValue The new value
8420          * @param {Mixed} oldValue The original value
8421          */
8422         change : true,
8423         /**
8424          * @event invalid
8425          * Fires after the field has been marked as invalid.
8426          * @param {Roo.form.Field} this
8427          * @param {String} msg The validation message
8428          */
8429         invalid : true,
8430         /**
8431          * @event valid
8432          * Fires after the field has been validated with no errors.
8433          * @param {Roo.form.Field} this
8434          */
8435         valid : true,
8436          /**
8437          * @event keyup
8438          * Fires after the key up
8439          * @param {Roo.form.Field} this
8440          * @param {Roo.EventObject}  e The event Object
8441          */
8442         keyup : true
8443     });
8444 };
8445
8446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8447      /**
8448      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8449       automatic validation (defaults to "keyup").
8450      */
8451     validationEvent : "keyup",
8452      /**
8453      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8454      */
8455     validateOnBlur : true,
8456     /**
8457      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8458      */
8459     validationDelay : 250,
8460      /**
8461      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8462      */
8463     focusClass : "x-form-focus",  // not needed???
8464     
8465        
8466     /**
8467      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8468      */
8469     invalidClass : "has-warning",
8470     
8471     /**
8472      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8473      */
8474     validClass : "has-success",
8475     
8476     /**
8477      * @cfg {Boolean} hasFeedback (true|false) default true
8478      */
8479     hasFeedback : true,
8480     
8481     /**
8482      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8483      */
8484     invalidFeedbackClass : "glyphicon-warning-sign",
8485     
8486     /**
8487      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8488      */
8489     validFeedbackClass : "glyphicon-ok",
8490     
8491     /**
8492      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8493      */
8494     selectOnFocus : false,
8495     
8496      /**
8497      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8498      */
8499     maskRe : null,
8500        /**
8501      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8502      */
8503     vtype : null,
8504     
8505       /**
8506      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8507      */
8508     disableKeyFilter : false,
8509     
8510        /**
8511      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8512      */
8513     disabled : false,
8514      /**
8515      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8516      */
8517     allowBlank : true,
8518     /**
8519      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8520      */
8521     blankText : "Please complete this mandatory field",
8522     
8523      /**
8524      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8525      */
8526     minLength : 0,
8527     /**
8528      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8529      */
8530     maxLength : Number.MAX_VALUE,
8531     /**
8532      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8533      */
8534     minLengthText : "The minimum length for this field is {0}",
8535     /**
8536      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8537      */
8538     maxLengthText : "The maximum length for this field is {0}",
8539   
8540     
8541     /**
8542      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8543      * If available, this function will be called only after the basic validators all return true, and will be passed the
8544      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8545      */
8546     validator : null,
8547     /**
8548      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8549      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8550      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8551      */
8552     regex : null,
8553     /**
8554      * @cfg {String} regexText -- Depricated - use Invalid Text
8555      */
8556     regexText : "",
8557     
8558     /**
8559      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8560      */
8561     invalidText : "",
8562     
8563     
8564     
8565     autocomplete: false,
8566     
8567     
8568     fieldLabel : '',
8569     inputType : 'text',
8570     
8571     name : false,
8572     placeholder: false,
8573     before : false,
8574     after : false,
8575     size : false,
8576     hasFocus : false,
8577     preventMark: false,
8578     isFormField : true,
8579     value : '',
8580     labelWidth : 2,
8581     labelAlign : false,
8582     readOnly : false,
8583     align : false,
8584     formatedValue : false,
8585     forceFeedback : false,
8586     
8587     indicatorpos : 'left',
8588     
8589     labellg : 0,
8590     labelmd : 0,
8591     labelsm : 0,
8592     labelxs : 0,
8593     
8594     parentLabelAlign : function()
8595     {
8596         var parent = this;
8597         while (parent.parent()) {
8598             parent = parent.parent();
8599             if (typeof(parent.labelAlign) !='undefined') {
8600                 return parent.labelAlign;
8601             }
8602         }
8603         return 'left';
8604         
8605     },
8606     
8607     getAutoCreate : function()
8608     {
8609         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8610         
8611         var id = Roo.id();
8612         
8613         var cfg = {};
8614         
8615         if(this.inputType != 'hidden'){
8616             cfg.cls = 'form-group' //input-group
8617         }
8618         
8619         var input =  {
8620             tag: 'input',
8621             id : id,
8622             type : this.inputType,
8623             value : this.value,
8624             cls : 'form-control',
8625             placeholder : this.placeholder || '',
8626             autocomplete : this.autocomplete || 'new-password'
8627         };
8628         
8629         if(this.align){
8630             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8631         }
8632         
8633         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8634             input.maxLength = this.maxLength;
8635         }
8636         
8637         if (this.disabled) {
8638             input.disabled=true;
8639         }
8640         
8641         if (this.readOnly) {
8642             input.readonly=true;
8643         }
8644         
8645         if (this.name) {
8646             input.name = this.name;
8647         }
8648         
8649         if (this.size) {
8650             input.cls += ' input-' + this.size;
8651         }
8652         
8653         var settings=this;
8654         ['xs','sm','md','lg'].map(function(size){
8655             if (settings[size]) {
8656                 cfg.cls += ' col-' + size + '-' + settings[size];
8657             }
8658         });
8659         
8660         var inputblock = input;
8661         
8662         var feedback = {
8663             tag: 'span',
8664             cls: 'glyphicon form-control-feedback'
8665         };
8666             
8667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8668             
8669             inputblock = {
8670                 cls : 'has-feedback',
8671                 cn :  [
8672                     input,
8673                     feedback
8674                 ] 
8675             };  
8676         }
8677         
8678         if (this.before || this.after) {
8679             
8680             inputblock = {
8681                 cls : 'input-group',
8682                 cn :  [] 
8683             };
8684             
8685             if (this.before && typeof(this.before) == 'string') {
8686                 
8687                 inputblock.cn.push({
8688                     tag :'span',
8689                     cls : 'roo-input-before input-group-addon',
8690                     html : this.before
8691                 });
8692             }
8693             if (this.before && typeof(this.before) == 'object') {
8694                 this.before = Roo.factory(this.before);
8695                 
8696                 inputblock.cn.push({
8697                     tag :'span',
8698                     cls : 'roo-input-before input-group-' +
8699                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8700                 });
8701             }
8702             
8703             inputblock.cn.push(input);
8704             
8705             if (this.after && typeof(this.after) == 'string') {
8706                 inputblock.cn.push({
8707                     tag :'span',
8708                     cls : 'roo-input-after input-group-addon',
8709                     html : this.after
8710                 });
8711             }
8712             if (this.after && typeof(this.after) == 'object') {
8713                 this.after = Roo.factory(this.after);
8714                 
8715                 inputblock.cn.push({
8716                     tag :'span',
8717                     cls : 'roo-input-after input-group-' +
8718                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8719                 });
8720             }
8721             
8722             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8723                 inputblock.cls += ' has-feedback';
8724                 inputblock.cn.push(feedback);
8725             }
8726         };
8727         
8728         if (align ==='left' && this.fieldLabel.length) {
8729             
8730             cfg.cls += ' roo-form-group-label-left';
8731             
8732             cfg.cn = [
8733                 {
8734                     tag : 'i',
8735                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8736                     tooltip : 'This field is required'
8737                 },
8738                 {
8739                     tag: 'label',
8740                     'for' :  id,
8741                     cls : 'control-label',
8742                     html : this.fieldLabel
8743
8744                 },
8745                 {
8746                     cls : "", 
8747                     cn: [
8748                         inputblock
8749                     ]
8750                 }
8751             ];
8752             
8753             var labelCfg = cfg.cn[1];
8754             var contentCfg = cfg.cn[2];
8755             
8756             if(this.indicatorpos == 'right'){
8757                 cfg.cn = [
8758                     {
8759                         tag: 'label',
8760                         'for' :  id,
8761                         cls : 'control-label',
8762                         cn : [
8763                             {
8764                                 tag : 'span',
8765                                 html : this.fieldLabel
8766                             },
8767                             {
8768                                 tag : 'i',
8769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8770                                 tooltip : 'This field is required'
8771                             }
8772                         ]
8773                     },
8774                     {
8775                         cls : "",
8776                         cn: [
8777                             inputblock
8778                         ]
8779                     }
8780
8781                 ];
8782                 
8783                 labelCfg = cfg.cn[0];
8784                 contentCfg = cfg.cn[1];
8785             
8786             }
8787             
8788             if(this.labelWidth > 12){
8789                 labelCfg.style = "width: " + this.labelWidth + 'px';
8790             }
8791             
8792             if(this.labelWidth < 13 && this.labelmd == 0){
8793                 this.labelmd = this.labelWidth;
8794             }
8795             
8796             if(this.labellg > 0){
8797                 labelCfg.cls += ' col-lg-' + this.labellg;
8798                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8799             }
8800             
8801             if(this.labelmd > 0){
8802                 labelCfg.cls += ' col-md-' + this.labelmd;
8803                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8804             }
8805             
8806             if(this.labelsm > 0){
8807                 labelCfg.cls += ' col-sm-' + this.labelsm;
8808                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8809             }
8810             
8811             if(this.labelxs > 0){
8812                 labelCfg.cls += ' col-xs-' + this.labelxs;
8813                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8814             }
8815             
8816             
8817         } else if ( this.fieldLabel.length) {
8818                 
8819             cfg.cn = [
8820                 {
8821                     tag : 'i',
8822                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8823                     tooltip : 'This field is required'
8824                 },
8825                 {
8826                     tag: 'label',
8827                    //cls : 'input-group-addon',
8828                     html : this.fieldLabel
8829
8830                 },
8831
8832                inputblock
8833
8834            ];
8835            
8836            if(this.indicatorpos == 'right'){
8837                 
8838                 cfg.cn = [
8839                     {
8840                         tag: 'label',
8841                        //cls : 'input-group-addon',
8842                         html : this.fieldLabel
8843
8844                     },
8845                     {
8846                         tag : 'i',
8847                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8848                         tooltip : 'This field is required'
8849                     },
8850
8851                    inputblock
8852
8853                ];
8854
8855             }
8856
8857         } else {
8858             
8859             cfg.cn = [
8860
8861                     inputblock
8862
8863             ];
8864                 
8865                 
8866         };
8867         
8868         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8869            cfg.cls += ' navbar-form';
8870         }
8871         
8872         if (this.parentType === 'NavGroup') {
8873            cfg.cls += ' navbar-form';
8874            cfg.tag = 'li';
8875         }
8876         
8877         return cfg;
8878         
8879     },
8880     /**
8881      * return the real input element.
8882      */
8883     inputEl: function ()
8884     {
8885         return this.el.select('input.form-control',true).first();
8886     },
8887     
8888     tooltipEl : function()
8889     {
8890         return this.inputEl();
8891     },
8892     
8893     indicatorEl : function()
8894     {
8895         var indicator = this.el.select('i.roo-required-indicator',true).first();
8896         
8897         if(!indicator){
8898             return false;
8899         }
8900         
8901         return indicator;
8902         
8903     },
8904     
8905     setDisabled : function(v)
8906     {
8907         var i  = this.inputEl().dom;
8908         if (!v) {
8909             i.removeAttribute('disabled');
8910             return;
8911             
8912         }
8913         i.setAttribute('disabled','true');
8914     },
8915     initEvents : function()
8916     {
8917           
8918         this.inputEl().on("keydown" , this.fireKey,  this);
8919         this.inputEl().on("focus", this.onFocus,  this);
8920         this.inputEl().on("blur", this.onBlur,  this);
8921         
8922         this.inputEl().relayEvent('keyup', this);
8923         
8924         this.indicator = this.indicatorEl();
8925         
8926         if(this.indicator){
8927             this.indicator.addClass('invisible');
8928             
8929         }
8930  
8931         // reference to original value for reset
8932         this.originalValue = this.getValue();
8933         //Roo.form.TextField.superclass.initEvents.call(this);
8934         if(this.validationEvent == 'keyup'){
8935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8936             this.inputEl().on('keyup', this.filterValidation, this);
8937         }
8938         else if(this.validationEvent !== false){
8939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8940         }
8941         
8942         if(this.selectOnFocus){
8943             this.on("focus", this.preFocus, this);
8944             
8945         }
8946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8947             this.inputEl().on("keypress", this.filterKeys, this);
8948         } else {
8949             this.inputEl().relayEvent('keypress', this);
8950         }
8951        /* if(this.grow){
8952             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8953             this.el.on("click", this.autoSize,  this);
8954         }
8955         */
8956         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8957             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8958         }
8959         
8960         if (typeof(this.before) == 'object') {
8961             this.before.render(this.el.select('.roo-input-before',true).first());
8962         }
8963         if (typeof(this.after) == 'object') {
8964             this.after.render(this.el.select('.roo-input-after',true).first());
8965         }
8966         
8967         
8968     },
8969     filterValidation : function(e){
8970         if(!e.isNavKeyPress()){
8971             this.validationTask.delay(this.validationDelay);
8972         }
8973     },
8974      /**
8975      * Validates the field value
8976      * @return {Boolean} True if the value is valid, else false
8977      */
8978     validate : function(){
8979         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8980         if(this.disabled || this.validateValue(this.getRawValue())){
8981             this.markValid();
8982             return true;
8983         }
8984         
8985         this.markInvalid();
8986         return false;
8987     },
8988     
8989     
8990     /**
8991      * Validates a value according to the field's validation rules and marks the field as invalid
8992      * if the validation fails
8993      * @param {Mixed} value The value to validate
8994      * @return {Boolean} True if the value is valid, else false
8995      */
8996     validateValue : function(value){
8997         if(value.length < 1)  { // if it's blank
8998             if(this.allowBlank){
8999                 return true;
9000             }            
9001             return this.inputEl().hasClass('hide') ? true : false;
9002         }
9003         
9004         if(value.length < this.minLength){
9005             return false;
9006         }
9007         if(value.length > this.maxLength){
9008             return false;
9009         }
9010         if(this.vtype){
9011             var vt = Roo.form.VTypes;
9012             if(!vt[this.vtype](value, this)){
9013                 return false;
9014             }
9015         }
9016         if(typeof this.validator == "function"){
9017             var msg = this.validator(value);
9018             if(msg !== true){
9019                 return false;
9020             }
9021             if (typeof(msg) == 'string') {
9022                 this.invalidText = msg;
9023             }
9024         }
9025         
9026         if(this.regex && !this.regex.test(value)){
9027             return false;
9028         }
9029         
9030         return true;
9031     },
9032
9033     
9034     
9035      // private
9036     fireKey : function(e){
9037         //Roo.log('field ' + e.getKey());
9038         if(e.isNavKeyPress()){
9039             this.fireEvent("specialkey", this, e);
9040         }
9041     },
9042     focus : function (selectText){
9043         if(this.rendered){
9044             this.inputEl().focus();
9045             if(selectText === true){
9046                 this.inputEl().dom.select();
9047             }
9048         }
9049         return this;
9050     } ,
9051     
9052     onFocus : function(){
9053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054            // this.el.addClass(this.focusClass);
9055         }
9056         if(!this.hasFocus){
9057             this.hasFocus = true;
9058             this.startValue = this.getValue();
9059             this.fireEvent("focus", this);
9060         }
9061     },
9062     
9063     beforeBlur : Roo.emptyFn,
9064
9065     
9066     // private
9067     onBlur : function(){
9068         this.beforeBlur();
9069         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9070             //this.el.removeClass(this.focusClass);
9071         }
9072         this.hasFocus = false;
9073         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9074             this.validate();
9075         }
9076         var v = this.getValue();
9077         if(String(v) !== String(this.startValue)){
9078             this.fireEvent('change', this, v, this.startValue);
9079         }
9080         this.fireEvent("blur", this);
9081     },
9082     
9083     /**
9084      * Resets the current field value to the originally loaded value and clears any validation messages
9085      */
9086     reset : function(){
9087         this.setValue(this.originalValue);
9088         this.validate();
9089     },
9090      /**
9091      * Returns the name of the field
9092      * @return {Mixed} name The name field
9093      */
9094     getName: function(){
9095         return this.name;
9096     },
9097      /**
9098      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9099      * @return {Mixed} value The field value
9100      */
9101     getValue : function(){
9102         
9103         var v = this.inputEl().getValue();
9104         
9105         return v;
9106     },
9107     /**
9108      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9109      * @return {Mixed} value The field value
9110      */
9111     getRawValue : function(){
9112         var v = this.inputEl().getValue();
9113         
9114         return v;
9115     },
9116     
9117     /**
9118      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9119      * @param {Mixed} value The value to set
9120      */
9121     setRawValue : function(v){
9122         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9123     },
9124     
9125     selectText : function(start, end){
9126         var v = this.getRawValue();
9127         if(v.length > 0){
9128             start = start === undefined ? 0 : start;
9129             end = end === undefined ? v.length : end;
9130             var d = this.inputEl().dom;
9131             if(d.setSelectionRange){
9132                 d.setSelectionRange(start, end);
9133             }else if(d.createTextRange){
9134                 var range = d.createTextRange();
9135                 range.moveStart("character", start);
9136                 range.moveEnd("character", v.length-end);
9137                 range.select();
9138             }
9139         }
9140     },
9141     
9142     /**
9143      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9144      * @param {Mixed} value The value to set
9145      */
9146     setValue : function(v){
9147         this.value = v;
9148         if(this.rendered){
9149             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9150             this.validate();
9151         }
9152     },
9153     
9154     /*
9155     processValue : function(value){
9156         if(this.stripCharsRe){
9157             var newValue = value.replace(this.stripCharsRe, '');
9158             if(newValue !== value){
9159                 this.setRawValue(newValue);
9160                 return newValue;
9161             }
9162         }
9163         return value;
9164     },
9165   */
9166     preFocus : function(){
9167         
9168         if(this.selectOnFocus){
9169             this.inputEl().dom.select();
9170         }
9171     },
9172     filterKeys : function(e){
9173         var k = e.getKey();
9174         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9175             return;
9176         }
9177         var c = e.getCharCode(), cc = String.fromCharCode(c);
9178         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9179             return;
9180         }
9181         if(!this.maskRe.test(cc)){
9182             e.stopEvent();
9183         }
9184     },
9185      /**
9186      * Clear any invalid styles/messages for this field
9187      */
9188     clearInvalid : function(){
9189         
9190         if(!this.el || this.preventMark){ // not rendered
9191             return;
9192         }
9193         
9194      
9195         this.el.removeClass(this.invalidClass);
9196         
9197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9198             
9199             var feedback = this.el.select('.form-control-feedback', true).first();
9200             
9201             if(feedback){
9202                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as valid
9212      */
9213     markValid : function()
9214     {
9215         if(!this.el  || this.preventMark){ // not rendered...
9216             return;
9217         }
9218         
9219         this.el.removeClass([this.invalidClass, this.validClass]);
9220         
9221         var feedback = this.el.select('.form-control-feedback', true).first();
9222             
9223         if(feedback){
9224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9225         }
9226
9227         if(this.disabled){
9228             return;
9229         }
9230         
9231         if(this.allowBlank && !this.getRawValue().length){
9232             return;
9233         }
9234         
9235         if(this.indicator){
9236             this.indicator.removeClass('visible');
9237             this.indicator.addClass('invisible');
9238         }
9239         
9240         this.el.addClass(this.validClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9249             }
9250             
9251         }
9252         
9253         this.fireEvent('valid', this);
9254     },
9255     
9256      /**
9257      * Mark this field as invalid
9258      * @param {String} msg The validation message
9259      */
9260     markInvalid : function(msg)
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273
9274         if(this.disabled){
9275             return;
9276         }
9277         
9278         if(this.allowBlank && !this.getRawValue().length){
9279             return;
9280         }
9281         
9282         if(this.indicator){
9283             this.indicator.removeClass('invisible');
9284             this.indicator.addClass('visible');
9285         }
9286         
9287         this.el.addClass(this.invalidClass);
9288         
9289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9290             
9291             var feedback = this.el.select('.form-control-feedback', true).first();
9292             
9293             if(feedback){
9294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9295                 
9296                 if(this.getValue().length || this.forceFeedback){
9297                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9298                 }
9299                 
9300             }
9301             
9302         }
9303         
9304         this.fireEvent('invalid', this, msg);
9305     },
9306     // private
9307     SafariOnKeyDown : function(event)
9308     {
9309         // this is a workaround for a password hang bug on chrome/ webkit.
9310         if (this.inputEl().dom.type != 'password') {
9311             return;
9312         }
9313         
9314         var isSelectAll = false;
9315         
9316         if(this.inputEl().dom.selectionEnd > 0){
9317             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9318         }
9319         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9320             event.preventDefault();
9321             this.setValue('');
9322             return;
9323         }
9324         
9325         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9326             
9327             event.preventDefault();
9328             // this is very hacky as keydown always get's upper case.
9329             //
9330             var cc = String.fromCharCode(event.getCharCode());
9331             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9332             
9333         }
9334     },
9335     adjustWidth : function(tag, w){
9336         tag = tag.toLowerCase();
9337         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9338             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9339                 if(tag == 'input'){
9340                     return w + 2;
9341                 }
9342                 if(tag == 'textarea'){
9343                     return w-2;
9344                 }
9345             }else if(Roo.isOpera){
9346                 if(tag == 'input'){
9347                     return w + 2;
9348                 }
9349                 if(tag == 'textarea'){
9350                     return w-2;
9351                 }
9352             }
9353         }
9354         return w;
9355     },
9356     
9357     setFieldLabel : function(v)
9358     {
9359         if(!this.rendered){
9360             return;
9361         }
9362         
9363         this.fieldLabel = v;
9364         
9365         if(this.indicator){
9366             var ar = this.el.select('label > span',true);
9367             if (!ar.length) {
9368                 Roo.log("could not find label > span on element");
9369                 Roo.log(this);
9370                 return;
9371             }
9372             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9373             return;
9374         }
9375         
9376         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9377     }
9378 });
9379
9380  
9381 /*
9382  * - LGPL
9383  *
9384  * Input
9385  * 
9386  */
9387
9388 /**
9389  * @class Roo.bootstrap.TextArea
9390  * @extends Roo.bootstrap.Input
9391  * Bootstrap TextArea class
9392  * @cfg {Number} cols Specifies the visible width of a text area
9393  * @cfg {Number} rows Specifies the visible number of lines in a text area
9394  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9395  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9396  * @cfg {string} html text
9397  * 
9398  * @constructor
9399  * Create a new TextArea
9400  * @param {Object} config The config object
9401  */
9402
9403 Roo.bootstrap.TextArea = function(config){
9404     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9405    
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9409      
9410     cols : false,
9411     rows : 5,
9412     readOnly : false,
9413     warp : 'soft',
9414     resize : false,
9415     value: false,
9416     html: false,
9417     
9418     getAutoCreate : function(){
9419         
9420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9421         
9422         var id = Roo.id();
9423         
9424         var cfg = {};
9425         
9426         if(this.inputType != 'hidden'){
9427             cfg.cls = 'form-group' //input-group
9428         }
9429         
9430         var input =  {
9431             tag: 'textarea',
9432             id : id,
9433             warp : this.warp,
9434             rows : this.rows,
9435             value : this.value || '',
9436             html: this.html || '',
9437             cls : 'form-control',
9438             placeholder : this.placeholder || '' 
9439             
9440         };
9441         
9442         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9443             input.maxLength = this.maxLength;
9444         }
9445         
9446         if(this.resize){
9447             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9448         }
9449         
9450         if(this.cols){
9451             input.cols = this.cols;
9452         }
9453         
9454         if (this.readOnly) {
9455             input.readonly = true;
9456         }
9457         
9458         if (this.name) {
9459             input.name = this.name;
9460         }
9461         
9462         if (this.size) {
9463             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9464         }
9465         
9466         var settings=this;
9467         ['xs','sm','md','lg'].map(function(size){
9468             if (settings[size]) {
9469                 cfg.cls += ' col-' + size + '-' + settings[size];
9470             }
9471         });
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481
9482             inputblock = {
9483                 cls : 'has-feedback',
9484                 cn :  [
9485                     input,
9486                     feedback
9487                 ] 
9488             };  
9489         }
9490         
9491         
9492         if (this.before || this.after) {
9493             
9494             inputblock = {
9495                 cls : 'input-group',
9496                 cn :  [] 
9497             };
9498             if (this.before) {
9499                 inputblock.cn.push({
9500                     tag :'span',
9501                     cls : 'input-group-addon',
9502                     html : this.before
9503                 });
9504             }
9505             
9506             inputblock.cn.push(input);
9507             
9508             if(this.hasFeedback && !this.allowBlank){
9509                 inputblock.cls += ' has-feedback';
9510                 inputblock.cn.push(feedback);
9511             }
9512             
9513             if (this.after) {
9514                 inputblock.cn.push({
9515                     tag :'span',
9516                     cls : 'input-group-addon',
9517                     html : this.after
9518                 });
9519             }
9520             
9521         }
9522         
9523         if (align ==='left' && this.fieldLabel.length) {
9524             cfg.cn = [
9525                 {
9526                     tag: 'label',
9527                     'for' :  id,
9528                     cls : 'control-label',
9529                     html : this.fieldLabel
9530                 },
9531                 {
9532                     cls : "",
9533                     cn: [
9534                         inputblock
9535                     ]
9536                 }
9537
9538             ];
9539             
9540             if(this.labelWidth > 12){
9541                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9542             }
9543
9544             if(this.labelWidth < 13 && this.labelmd == 0){
9545                 this.labelmd = this.labelWidth;
9546             }
9547
9548             if(this.labellg > 0){
9549                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9550                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9551             }
9552
9553             if(this.labelmd > 0){
9554                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9555                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9556             }
9557
9558             if(this.labelsm > 0){
9559                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9560                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9561             }
9562
9563             if(this.labelxs > 0){
9564                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9565                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9566             }
9567             
9568         } else if ( this.fieldLabel.length) {
9569             cfg.cn = [
9570
9571                {
9572                    tag: 'label',
9573                    //cls : 'input-group-addon',
9574                    html : this.fieldLabel
9575
9576                },
9577
9578                inputblock
9579
9580            ];
9581
9582         } else {
9583
9584             cfg.cn = [
9585
9586                 inputblock
9587
9588             ];
9589                 
9590         }
9591         
9592         if (this.disabled) {
9593             input.disabled=true;
9594         }
9595         
9596         return cfg;
9597         
9598     },
9599     /**
9600      * return the real textarea element.
9601      */
9602     inputEl: function ()
9603     {
9604         return this.el.select('textarea.form-control',true).first();
9605     },
9606     
9607     /**
9608      * Clear any invalid styles/messages for this field
9609      */
9610     clearInvalid : function()
9611     {
9612         
9613         if(!this.el || this.preventMark){ // not rendered
9614             return;
9615         }
9616         
9617         var label = this.el.select('label', true).first();
9618         var icon = this.el.select('i.fa-star', true).first();
9619         
9620         if(label && icon){
9621             icon.remove();
9622         }
9623         
9624         this.el.removeClass(this.invalidClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9632             }
9633             
9634         }
9635         
9636         this.fireEvent('valid', this);
9637     },
9638     
9639      /**
9640      * Mark this field as valid
9641      */
9642     markValid : function()
9643     {
9644         if(!this.el  || this.preventMark){ // not rendered
9645             return;
9646         }
9647         
9648         this.el.removeClass([this.invalidClass, this.validClass]);
9649         
9650         var feedback = this.el.select('.form-control-feedback', true).first();
9651             
9652         if(feedback){
9653             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9654         }
9655
9656         if(this.disabled || this.allowBlank){
9657             return;
9658         }
9659         
9660         var label = this.el.select('label', true).first();
9661         var icon = this.el.select('i.fa-star', true).first();
9662         
9663         if(label && icon){
9664             icon.remove();
9665         }
9666         
9667         this.el.addClass(this.validClass);
9668         
9669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9670             
9671             var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673             if(feedback){
9674                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9676             }
9677             
9678         }
9679         
9680         this.fireEvent('valid', this);
9681     },
9682     
9683      /**
9684      * Mark this field as invalid
9685      * @param {String} msg The validation message
9686      */
9687     markInvalid : function(msg)
9688     {
9689         if(!this.el  || this.preventMark){ // not rendered
9690             return;
9691         }
9692         
9693         this.el.removeClass([this.invalidClass, this.validClass]);
9694         
9695         var feedback = this.el.select('.form-control-feedback', true).first();
9696             
9697         if(feedback){
9698             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9699         }
9700
9701         if(this.disabled || this.allowBlank){
9702             return;
9703         }
9704         
9705         var label = this.el.select('label', true).first();
9706         var icon = this.el.select('i.fa-star', true).first();
9707         
9708         if(!this.getValue().length && label && !icon){
9709             this.el.createChild({
9710                 tag : 'i',
9711                 cls : 'text-danger fa fa-lg fa-star',
9712                 tooltip : 'This field is required',
9713                 style : 'margin-right:5px;'
9714             }, label, true);
9715         }
9716
9717         this.el.addClass(this.invalidClass);
9718         
9719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9720             
9721             var feedback = this.el.select('.form-control-feedback', true).first();
9722             
9723             if(feedback){
9724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725                 
9726                 if(this.getValue().length || this.forceFeedback){
9727                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9728                 }
9729                 
9730             }
9731             
9732         }
9733         
9734         this.fireEvent('invalid', this, msg);
9735     }
9736 });
9737
9738  
9739 /*
9740  * - LGPL
9741  *
9742  * trigger field - base class for combo..
9743  * 
9744  */
9745  
9746 /**
9747  * @class Roo.bootstrap.TriggerField
9748  * @extends Roo.bootstrap.Input
9749  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9750  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9751  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9752  * for which you can provide a custom implementation.  For example:
9753  * <pre><code>
9754 var trigger = new Roo.bootstrap.TriggerField();
9755 trigger.onTriggerClick = myTriggerFn;
9756 trigger.applyTo('my-field');
9757 </code></pre>
9758  *
9759  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9760  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9761  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9762  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9763  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9764
9765  * @constructor
9766  * Create a new TriggerField.
9767  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9768  * to the base TextField)
9769  */
9770 Roo.bootstrap.TriggerField = function(config){
9771     this.mimicing = false;
9772     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9773 };
9774
9775 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9776     /**
9777      * @cfg {String} triggerClass A CSS class to apply to the trigger
9778      */
9779      /**
9780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9781      */
9782     hideTrigger:false,
9783
9784     /**
9785      * @cfg {Boolean} removable (true|false) special filter default false
9786      */
9787     removable : false,
9788     
9789     /** @cfg {Boolean} grow @hide */
9790     /** @cfg {Number} growMin @hide */
9791     /** @cfg {Number} growMax @hide */
9792
9793     /**
9794      * @hide 
9795      * @method
9796      */
9797     autoSize: Roo.emptyFn,
9798     // private
9799     monitorTab : true,
9800     // private
9801     deferHeight : true,
9802
9803     
9804     actionMode : 'wrap',
9805     
9806     caret : false,
9807     
9808     
9809     getAutoCreate : function(){
9810        
9811         var align = this.labelAlign || this.parentLabelAlign();
9812         
9813         var id = Roo.id();
9814         
9815         var cfg = {
9816             cls: 'form-group' //input-group
9817         };
9818         
9819         
9820         var input =  {
9821             tag: 'input',
9822             id : id,
9823             type : this.inputType,
9824             cls : 'form-control',
9825             autocomplete: 'new-password',
9826             placeholder : this.placeholder || '' 
9827             
9828         };
9829         if (this.name) {
9830             input.name = this.name;
9831         }
9832         if (this.size) {
9833             input.cls += ' input-' + this.size;
9834         }
9835         
9836         if (this.disabled) {
9837             input.disabled=true;
9838         }
9839         
9840         var inputblock = input;
9841         
9842         if(this.hasFeedback && !this.allowBlank){
9843             
9844             var feedback = {
9845                 tag: 'span',
9846                 cls: 'glyphicon form-control-feedback'
9847             };
9848             
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'has-feedback',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         },
9859                         feedback
9860                     ] 
9861                 };
9862             } else {
9863                 inputblock = {
9864                     cls : 'has-feedback',
9865                     cn :  [
9866                         inputblock,
9867                         feedback
9868                     ] 
9869                 };
9870             }
9871
9872         } else {
9873             if(this.removable && !this.editable && !this.tickable){
9874                 inputblock = {
9875                     cls : 'roo-removable',
9876                     cn :  [
9877                         inputblock,
9878                         {
9879                             tag: 'button',
9880                             html : 'x',
9881                             cls : 'roo-combo-removable-btn close'
9882                         }
9883                     ] 
9884                 };
9885             }
9886         }
9887         
9888         if (this.before || this.after) {
9889             
9890             inputblock = {
9891                 cls : 'input-group',
9892                 cn :  [] 
9893             };
9894             if (this.before) {
9895                 inputblock.cn.push({
9896                     tag :'span',
9897                     cls : 'input-group-addon',
9898                     html : this.before
9899                 });
9900             }
9901             
9902             inputblock.cn.push(input);
9903             
9904             if(this.hasFeedback && !this.allowBlank){
9905                 inputblock.cls += ' has-feedback';
9906                 inputblock.cn.push(feedback);
9907             }
9908             
9909             if (this.after) {
9910                 inputblock.cn.push({
9911                     tag :'span',
9912                     cls : 'input-group-addon',
9913                     html : this.after
9914                 });
9915             }
9916             
9917         };
9918         
9919         var box = {
9920             tag: 'div',
9921             cn: [
9922                 {
9923                     tag: 'input',
9924                     type : 'hidden',
9925                     cls: 'form-hidden-field'
9926                 },
9927                 inputblock
9928             ]
9929             
9930         };
9931         
9932         if(this.multiple){
9933             box = {
9934                 tag: 'div',
9935                 cn: [
9936                     {
9937                         tag: 'input',
9938                         type : 'hidden',
9939                         cls: 'form-hidden-field'
9940                     },
9941                     {
9942                         tag: 'ul',
9943                         cls: 'roo-select2-choices',
9944                         cn:[
9945                             {
9946                                 tag: 'li',
9947                                 cls: 'roo-select2-search-field',
9948                                 cn: [
9949
9950                                     inputblock
9951                                 ]
9952                             }
9953                         ]
9954                     }
9955                 ]
9956             }
9957         };
9958         
9959         var combobox = {
9960             cls: 'roo-select2-container input-group',
9961             cn: [
9962                 box
9963 //                {
9964 //                    tag: 'ul',
9965 //                    cls: 'typeahead typeahead-long dropdown-menu',
9966 //                    style: 'display:none'
9967 //                }
9968             ]
9969         };
9970         
9971         if(!this.multiple && this.showToggleBtn){
9972             
9973             var caret = {
9974                         tag: 'span',
9975                         cls: 'caret'
9976              };
9977             if (this.caret != false) {
9978                 caret = {
9979                      tag: 'i',
9980                      cls: 'fa fa-' + this.caret
9981                 };
9982                 
9983             }
9984             
9985             combobox.cn.push({
9986                 tag :'span',
9987                 cls : 'input-group-addon btn dropdown-toggle',
9988                 cn : [
9989                     caret,
9990                     {
9991                         tag: 'span',
9992                         cls: 'combobox-clear',
9993                         cn  : [
9994                             {
9995                                 tag : 'i',
9996                                 cls: 'icon-remove'
9997                             }
9998                         ]
9999                     }
10000                 ]
10001
10002             })
10003         }
10004         
10005         if(this.multiple){
10006             combobox.cls += ' roo-select2-container-multi';
10007         }
10008         
10009         if (align ==='left' && this.fieldLabel.length) {
10010             
10011             cfg.cls += ' roo-form-group-label-left';
10012
10013             cfg.cn = [
10014                 {
10015                     tag : 'i',
10016                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10017                     tooltip : 'This field is required'
10018                 },
10019                 {
10020                     tag: 'label',
10021                     'for' :  id,
10022                     cls : 'control-label',
10023                     html : this.fieldLabel
10024
10025                 },
10026                 {
10027                     cls : "", 
10028                     cn: [
10029                         combobox
10030                     ]
10031                 }
10032
10033             ];
10034             
10035             var labelCfg = cfg.cn[1];
10036             var contentCfg = cfg.cn[2];
10037             
10038             if(this.indicatorpos == 'right'){
10039                 cfg.cn = [
10040                     {
10041                         tag: 'label',
10042                         'for' :  id,
10043                         cls : 'control-label',
10044                         cn : [
10045                             {
10046                                 tag : 'span',
10047                                 html : this.fieldLabel
10048                             },
10049                             {
10050                                 tag : 'i',
10051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10052                                 tooltip : 'This field is required'
10053                             }
10054                         ]
10055                     },
10056                     {
10057                         cls : "", 
10058                         cn: [
10059                             combobox
10060                         ]
10061                     }
10062
10063                 ];
10064                 
10065                 labelCfg = cfg.cn[0];
10066                 contentCfg = cfg.cn[1];
10067             }
10068             
10069             if(this.labelWidth > 12){
10070                 labelCfg.style = "width: " + this.labelWidth + 'px';
10071             }
10072             
10073             if(this.labelWidth < 13 && this.labelmd == 0){
10074                 this.labelmd = this.labelWidth;
10075             }
10076             
10077             if(this.labellg > 0){
10078                 labelCfg.cls += ' col-lg-' + this.labellg;
10079                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10080             }
10081             
10082             if(this.labelmd > 0){
10083                 labelCfg.cls += ' col-md-' + this.labelmd;
10084                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10085             }
10086             
10087             if(this.labelsm > 0){
10088                 labelCfg.cls += ' col-sm-' + this.labelsm;
10089                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10090             }
10091             
10092             if(this.labelxs > 0){
10093                 labelCfg.cls += ' col-xs-' + this.labelxs;
10094                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10095             }
10096             
10097         } else if ( this.fieldLabel.length) {
10098 //                Roo.log(" label");
10099             cfg.cn = [
10100                 {
10101                    tag : 'i',
10102                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10103                    tooltip : 'This field is required'
10104                },
10105                {
10106                    tag: 'label',
10107                    //cls : 'input-group-addon',
10108                    html : this.fieldLabel
10109
10110                },
10111
10112                combobox
10113
10114             ];
10115             
10116             if(this.indicatorpos == 'right'){
10117                 
10118                 cfg.cn = [
10119                     {
10120                        tag: 'label',
10121                        cn : [
10122                            {
10123                                tag : 'span',
10124                                html : this.fieldLabel
10125                            },
10126                            {
10127                               tag : 'i',
10128                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10129                               tooltip : 'This field is required'
10130                            }
10131                        ]
10132
10133                     },
10134                     combobox
10135
10136                 ];
10137
10138             }
10139
10140         } else {
10141             
10142 //                Roo.log(" no label && no align");
10143                 cfg = combobox
10144                      
10145                 
10146         }
10147         
10148         var settings=this;
10149         ['xs','sm','md','lg'].map(function(size){
10150             if (settings[size]) {
10151                 cfg.cls += ' col-' + size + '-' + settings[size];
10152             }
10153         });
10154         
10155         return cfg;
10156         
10157     },
10158     
10159     
10160     
10161     // private
10162     onResize : function(w, h){
10163 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10164 //        if(typeof w == 'number'){
10165 //            var x = w - this.trigger.getWidth();
10166 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10167 //            this.trigger.setStyle('left', x+'px');
10168 //        }
10169     },
10170
10171     // private
10172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10173
10174     // private
10175     getResizeEl : function(){
10176         return this.inputEl();
10177     },
10178
10179     // private
10180     getPositionEl : function(){
10181         return this.inputEl();
10182     },
10183
10184     // private
10185     alignErrorIcon : function(){
10186         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10187     },
10188
10189     // private
10190     initEvents : function(){
10191         
10192         this.createList();
10193         
10194         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10195         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10196         if(!this.multiple && this.showToggleBtn){
10197             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10198             if(this.hideTrigger){
10199                 this.trigger.setDisplayed(false);
10200             }
10201             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10202         }
10203         
10204         if(this.multiple){
10205             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10206         }
10207         
10208         if(this.removable && !this.editable && !this.tickable){
10209             var close = this.closeTriggerEl();
10210             
10211             if(close){
10212                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10213                 close.on('click', this.removeBtnClick, this, close);
10214             }
10215         }
10216         
10217         //this.trigger.addClassOnOver('x-form-trigger-over');
10218         //this.trigger.addClassOnClick('x-form-trigger-click');
10219         
10220         //if(!this.width){
10221         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10222         //}
10223     },
10224     
10225     closeTriggerEl : function()
10226     {
10227         var close = this.el.select('.roo-combo-removable-btn', true).first();
10228         return close ? close : false;
10229     },
10230     
10231     removeBtnClick : function(e, h, el)
10232     {
10233         e.preventDefault();
10234         
10235         if(this.fireEvent("remove", this) !== false){
10236             this.reset();
10237             this.fireEvent("afterremove", this)
10238         }
10239     },
10240     
10241     createList : function()
10242     {
10243         this.list = Roo.get(document.body).createChild({
10244             tag: 'ul',
10245             cls: 'typeahead typeahead-long dropdown-menu',
10246             style: 'display:none'
10247         });
10248         
10249         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10250         
10251     },
10252
10253     // private
10254     initTrigger : function(){
10255        
10256     },
10257
10258     // private
10259     onDestroy : function(){
10260         if(this.trigger){
10261             this.trigger.removeAllListeners();
10262           //  this.trigger.remove();
10263         }
10264         //if(this.wrap){
10265         //    this.wrap.remove();
10266         //}
10267         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10268     },
10269
10270     // private
10271     onFocus : function(){
10272         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10273         /*
10274         if(!this.mimicing){
10275             this.wrap.addClass('x-trigger-wrap-focus');
10276             this.mimicing = true;
10277             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10278             if(this.monitorTab){
10279                 this.el.on("keydown", this.checkTab, this);
10280             }
10281         }
10282         */
10283     },
10284
10285     // private
10286     checkTab : function(e){
10287         if(e.getKey() == e.TAB){
10288             this.triggerBlur();
10289         }
10290     },
10291
10292     // private
10293     onBlur : function(){
10294         // do nothing
10295     },
10296
10297     // private
10298     mimicBlur : function(e, t){
10299         /*
10300         if(!this.wrap.contains(t) && this.validateBlur()){
10301             this.triggerBlur();
10302         }
10303         */
10304     },
10305
10306     // private
10307     triggerBlur : function(){
10308         this.mimicing = false;
10309         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10310         if(this.monitorTab){
10311             this.el.un("keydown", this.checkTab, this);
10312         }
10313         //this.wrap.removeClass('x-trigger-wrap-focus');
10314         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10315     },
10316
10317     // private
10318     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10319     validateBlur : function(e, t){
10320         return true;
10321     },
10322
10323     // private
10324     onDisable : function(){
10325         this.inputEl().dom.disabled = true;
10326         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10327         //if(this.wrap){
10328         //    this.wrap.addClass('x-item-disabled');
10329         //}
10330     },
10331
10332     // private
10333     onEnable : function(){
10334         this.inputEl().dom.disabled = false;
10335         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10336         //if(this.wrap){
10337         //    this.el.removeClass('x-item-disabled');
10338         //}
10339     },
10340
10341     // private
10342     onShow : function(){
10343         var ae = this.getActionEl();
10344         
10345         if(ae){
10346             ae.dom.style.display = '';
10347             ae.dom.style.visibility = 'visible';
10348         }
10349     },
10350
10351     // private
10352     
10353     onHide : function(){
10354         var ae = this.getActionEl();
10355         ae.dom.style.display = 'none';
10356     },
10357
10358     /**
10359      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10360      * by an implementing function.
10361      * @method
10362      * @param {EventObject} e
10363      */
10364     onTriggerClick : Roo.emptyFn
10365 });
10366  /*
10367  * Based on:
10368  * Ext JS Library 1.1.1
10369  * Copyright(c) 2006-2007, Ext JS, LLC.
10370  *
10371  * Originally Released Under LGPL - original licence link has changed is not relivant.
10372  *
10373  * Fork - LGPL
10374  * <script type="text/javascript">
10375  */
10376
10377
10378 /**
10379  * @class Roo.data.SortTypes
10380  * @singleton
10381  * Defines the default sorting (casting?) comparison functions used when sorting data.
10382  */
10383 Roo.data.SortTypes = {
10384     /**
10385      * Default sort that does nothing
10386      * @param {Mixed} s The value being converted
10387      * @return {Mixed} The comparison value
10388      */
10389     none : function(s){
10390         return s;
10391     },
10392     
10393     /**
10394      * The regular expression used to strip tags
10395      * @type {RegExp}
10396      * @property
10397      */
10398     stripTagsRE : /<\/?[^>]+>/gi,
10399     
10400     /**
10401      * Strips all HTML tags to sort on text only
10402      * @param {Mixed} s The value being converted
10403      * @return {String} The comparison value
10404      */
10405     asText : function(s){
10406         return String(s).replace(this.stripTagsRE, "");
10407     },
10408     
10409     /**
10410      * Strips all HTML tags to sort on text only - Case insensitive
10411      * @param {Mixed} s The value being converted
10412      * @return {String} The comparison value
10413      */
10414     asUCText : function(s){
10415         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10416     },
10417     
10418     /**
10419      * Case insensitive string
10420      * @param {Mixed} s The value being converted
10421      * @return {String} The comparison value
10422      */
10423     asUCString : function(s) {
10424         return String(s).toUpperCase();
10425     },
10426     
10427     /**
10428      * Date sorting
10429      * @param {Mixed} s The value being converted
10430      * @return {Number} The comparison value
10431      */
10432     asDate : function(s) {
10433         if(!s){
10434             return 0;
10435         }
10436         if(s instanceof Date){
10437             return s.getTime();
10438         }
10439         return Date.parse(String(s));
10440     },
10441     
10442     /**
10443      * Float sorting
10444      * @param {Mixed} s The value being converted
10445      * @return {Float} The comparison value
10446      */
10447     asFloat : function(s) {
10448         var val = parseFloat(String(s).replace(/,/g, ""));
10449         if(isNaN(val)) {
10450             val = 0;
10451         }
10452         return val;
10453     },
10454     
10455     /**
10456      * Integer sorting
10457      * @param {Mixed} s The value being converted
10458      * @return {Number} The comparison value
10459      */
10460     asInt : function(s) {
10461         var val = parseInt(String(s).replace(/,/g, ""));
10462         if(isNaN(val)) {
10463             val = 0;
10464         }
10465         return val;
10466     }
10467 };/*
10468  * Based on:
10469  * Ext JS Library 1.1.1
10470  * Copyright(c) 2006-2007, Ext JS, LLC.
10471  *
10472  * Originally Released Under LGPL - original licence link has changed is not relivant.
10473  *
10474  * Fork - LGPL
10475  * <script type="text/javascript">
10476  */
10477
10478 /**
10479 * @class Roo.data.Record
10480  * Instances of this class encapsulate both record <em>definition</em> information, and record
10481  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10482  * to access Records cached in an {@link Roo.data.Store} object.<br>
10483  * <p>
10484  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10485  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10486  * objects.<br>
10487  * <p>
10488  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10489  * @constructor
10490  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10491  * {@link #create}. The parameters are the same.
10492  * @param {Array} data An associative Array of data values keyed by the field name.
10493  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10494  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10495  * not specified an integer id is generated.
10496  */
10497 Roo.data.Record = function(data, id){
10498     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10499     this.data = data;
10500 };
10501
10502 /**
10503  * Generate a constructor for a specific record layout.
10504  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10505  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10506  * Each field definition object may contain the following properties: <ul>
10507  * <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,
10508  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10509  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10510  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10511  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10512  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10513  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10514  * this may be omitted.</p></li>
10515  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10516  * <ul><li>auto (Default, implies no conversion)</li>
10517  * <li>string</li>
10518  * <li>int</li>
10519  * <li>float</li>
10520  * <li>boolean</li>
10521  * <li>date</li></ul></p></li>
10522  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10523  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10524  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10525  * by the Reader into an object that will be stored in the Record. It is passed the
10526  * following parameters:<ul>
10527  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10528  * </ul></p></li>
10529  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10530  * </ul>
10531  * <br>usage:<br><pre><code>
10532 var TopicRecord = Roo.data.Record.create(
10533     {name: 'title', mapping: 'topic_title'},
10534     {name: 'author', mapping: 'username'},
10535     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10536     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10537     {name: 'lastPoster', mapping: 'user2'},
10538     {name: 'excerpt', mapping: 'post_text'}
10539 );
10540
10541 var myNewRecord = new TopicRecord({
10542     title: 'Do my job please',
10543     author: 'noobie',
10544     totalPosts: 1,
10545     lastPost: new Date(),
10546     lastPoster: 'Animal',
10547     excerpt: 'No way dude!'
10548 });
10549 myStore.add(myNewRecord);
10550 </code></pre>
10551  * @method create
10552  * @static
10553  */
10554 Roo.data.Record.create = function(o){
10555     var f = function(){
10556         f.superclass.constructor.apply(this, arguments);
10557     };
10558     Roo.extend(f, Roo.data.Record);
10559     var p = f.prototype;
10560     p.fields = new Roo.util.MixedCollection(false, function(field){
10561         return field.name;
10562     });
10563     for(var i = 0, len = o.length; i < len; i++){
10564         p.fields.add(new Roo.data.Field(o[i]));
10565     }
10566     f.getField = function(name){
10567         return p.fields.get(name);  
10568     };
10569     return f;
10570 };
10571
10572 Roo.data.Record.AUTO_ID = 1000;
10573 Roo.data.Record.EDIT = 'edit';
10574 Roo.data.Record.REJECT = 'reject';
10575 Roo.data.Record.COMMIT = 'commit';
10576
10577 Roo.data.Record.prototype = {
10578     /**
10579      * Readonly flag - true if this record has been modified.
10580      * @type Boolean
10581      */
10582     dirty : false,
10583     editing : false,
10584     error: null,
10585     modified: null,
10586
10587     // private
10588     join : function(store){
10589         this.store = store;
10590     },
10591
10592     /**
10593      * Set the named field to the specified value.
10594      * @param {String} name The name of the field to set.
10595      * @param {Object} value The value to set the field to.
10596      */
10597     set : function(name, value){
10598         if(this.data[name] == value){
10599             return;
10600         }
10601         this.dirty = true;
10602         if(!this.modified){
10603             this.modified = {};
10604         }
10605         if(typeof this.modified[name] == 'undefined'){
10606             this.modified[name] = this.data[name];
10607         }
10608         this.data[name] = value;
10609         if(!this.editing && this.store){
10610             this.store.afterEdit(this);
10611         }       
10612     },
10613
10614     /**
10615      * Get the value of the named field.
10616      * @param {String} name The name of the field to get the value of.
10617      * @return {Object} The value of the field.
10618      */
10619     get : function(name){
10620         return this.data[name]; 
10621     },
10622
10623     // private
10624     beginEdit : function(){
10625         this.editing = true;
10626         this.modified = {}; 
10627     },
10628
10629     // private
10630     cancelEdit : function(){
10631         this.editing = false;
10632         delete this.modified;
10633     },
10634
10635     // private
10636     endEdit : function(){
10637         this.editing = false;
10638         if(this.dirty && this.store){
10639             this.store.afterEdit(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Rejects all changes made to the Record since either creation, or the last commit operation.
10646      * Modified fields are reverted to their original values.
10647      * <p>
10648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10649      * of reject operations.
10650      */
10651     reject : function(){
10652         var m = this.modified;
10653         for(var n in m){
10654             if(typeof m[n] != "function"){
10655                 this.data[n] = m[n];
10656             }
10657         }
10658         this.dirty = false;
10659         delete this.modified;
10660         this.editing = false;
10661         if(this.store){
10662             this.store.afterReject(this);
10663         }
10664     },
10665
10666     /**
10667      * Usually called by the {@link Roo.data.Store} which owns the Record.
10668      * Commits all changes made to the Record since either creation, or the last commit operation.
10669      * <p>
10670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10671      * of commit operations.
10672      */
10673     commit : function(){
10674         this.dirty = false;
10675         delete this.modified;
10676         this.editing = false;
10677         if(this.store){
10678             this.store.afterCommit(this);
10679         }
10680     },
10681
10682     // private
10683     hasError : function(){
10684         return this.error != null;
10685     },
10686
10687     // private
10688     clearError : function(){
10689         this.error = null;
10690     },
10691
10692     /**
10693      * Creates a copy of this record.
10694      * @param {String} id (optional) A new record id if you don't want to use this record's id
10695      * @return {Record}
10696      */
10697     copy : function(newId) {
10698         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10699     }
10700 };/*
10701  * Based on:
10702  * Ext JS Library 1.1.1
10703  * Copyright(c) 2006-2007, Ext JS, LLC.
10704  *
10705  * Originally Released Under LGPL - original licence link has changed is not relivant.
10706  *
10707  * Fork - LGPL
10708  * <script type="text/javascript">
10709  */
10710
10711
10712
10713 /**
10714  * @class Roo.data.Store
10715  * @extends Roo.util.Observable
10716  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10717  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10718  * <p>
10719  * 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
10720  * has no knowledge of the format of the data returned by the Proxy.<br>
10721  * <p>
10722  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10723  * instances from the data object. These records are cached and made available through accessor functions.
10724  * @constructor
10725  * Creates a new Store.
10726  * @param {Object} config A config object containing the objects needed for the Store to access data,
10727  * and read the data into Records.
10728  */
10729 Roo.data.Store = function(config){
10730     this.data = new Roo.util.MixedCollection(false);
10731     this.data.getKey = function(o){
10732         return o.id;
10733     };
10734     this.baseParams = {};
10735     // private
10736     this.paramNames = {
10737         "start" : "start",
10738         "limit" : "limit",
10739         "sort" : "sort",
10740         "dir" : "dir",
10741         "multisort" : "_multisort"
10742     };
10743
10744     if(config && config.data){
10745         this.inlineData = config.data;
10746         delete config.data;
10747     }
10748
10749     Roo.apply(this, config);
10750     
10751     if(this.reader){ // reader passed
10752         this.reader = Roo.factory(this.reader, Roo.data);
10753         this.reader.xmodule = this.xmodule || false;
10754         if(!this.recordType){
10755             this.recordType = this.reader.recordType;
10756         }
10757         if(this.reader.onMetaChange){
10758             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10759         }
10760     }
10761
10762     if(this.recordType){
10763         this.fields = this.recordType.prototype.fields;
10764     }
10765     this.modified = [];
10766
10767     this.addEvents({
10768         /**
10769          * @event datachanged
10770          * Fires when the data cache has changed, and a widget which is using this Store
10771          * as a Record cache should refresh its view.
10772          * @param {Store} this
10773          */
10774         datachanged : true,
10775         /**
10776          * @event metachange
10777          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10778          * @param {Store} this
10779          * @param {Object} meta The JSON metadata
10780          */
10781         metachange : true,
10782         /**
10783          * @event add
10784          * Fires when Records have been added to the Store
10785          * @param {Store} this
10786          * @param {Roo.data.Record[]} records The array of Records added
10787          * @param {Number} index The index at which the record(s) were added
10788          */
10789         add : true,
10790         /**
10791          * @event remove
10792          * Fires when a Record has been removed from the Store
10793          * @param {Store} this
10794          * @param {Roo.data.Record} record The Record that was removed
10795          * @param {Number} index The index at which the record was removed
10796          */
10797         remove : true,
10798         /**
10799          * @event update
10800          * Fires when a Record has been updated
10801          * @param {Store} this
10802          * @param {Roo.data.Record} record The Record that was updated
10803          * @param {String} operation The update operation being performed.  Value may be one of:
10804          * <pre><code>
10805  Roo.data.Record.EDIT
10806  Roo.data.Record.REJECT
10807  Roo.data.Record.COMMIT
10808          * </code></pre>
10809          */
10810         update : true,
10811         /**
10812          * @event clear
10813          * Fires when the data cache has been cleared.
10814          * @param {Store} this
10815          */
10816         clear : true,
10817         /**
10818          * @event beforeload
10819          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10820          * the load action will be canceled.
10821          * @param {Store} this
10822          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10823          */
10824         beforeload : true,
10825         /**
10826          * @event beforeloadadd
10827          * Fires after a new set of Records has been loaded.
10828          * @param {Store} this
10829          * @param {Roo.data.Record[]} records The Records that were loaded
10830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10831          */
10832         beforeloadadd : true,
10833         /**
10834          * @event load
10835          * Fires after a new set of Records has been loaded, before they are added to the store.
10836          * @param {Store} this
10837          * @param {Roo.data.Record[]} records The Records that were loaded
10838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10839          * @params {Object} return from reader
10840          */
10841         load : true,
10842         /**
10843          * @event loadexception
10844          * Fires if an exception occurs in the Proxy during loading.
10845          * Called with the signature of the Proxy's "loadexception" event.
10846          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10847          * 
10848          * @param {Proxy} 
10849          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10850          * @param {Object} load options 
10851          * @param {Object} jsonData from your request (normally this contains the Exception)
10852          */
10853         loadexception : true
10854     });
10855     
10856     if(this.proxy){
10857         this.proxy = Roo.factory(this.proxy, Roo.data);
10858         this.proxy.xmodule = this.xmodule || false;
10859         this.relayEvents(this.proxy,  ["loadexception"]);
10860     }
10861     this.sortToggle = {};
10862     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10863
10864     Roo.data.Store.superclass.constructor.call(this);
10865
10866     if(this.inlineData){
10867         this.loadData(this.inlineData);
10868         delete this.inlineData;
10869     }
10870 };
10871
10872 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10873      /**
10874     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10875     * without a remote query - used by combo/forms at present.
10876     */
10877     
10878     /**
10879     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10880     */
10881     /**
10882     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10883     */
10884     /**
10885     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10886     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10887     */
10888     /**
10889     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10890     * on any HTTP request
10891     */
10892     /**
10893     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10894     */
10895     /**
10896     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10897     */
10898     multiSort: false,
10899     /**
10900     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10901     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10902     */
10903     remoteSort : false,
10904
10905     /**
10906     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10907      * loaded or when a record is removed. (defaults to false).
10908     */
10909     pruneModifiedRecords : false,
10910
10911     // private
10912     lastOptions : null,
10913
10914     /**
10915      * Add Records to the Store and fires the add event.
10916      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10917      */
10918     add : function(records){
10919         records = [].concat(records);
10920         for(var i = 0, len = records.length; i < len; i++){
10921             records[i].join(this);
10922         }
10923         var index = this.data.length;
10924         this.data.addAll(records);
10925         this.fireEvent("add", this, records, index);
10926     },
10927
10928     /**
10929      * Remove a Record from the Store and fires the remove event.
10930      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10931      */
10932     remove : function(record){
10933         var index = this.data.indexOf(record);
10934         this.data.removeAt(index);
10935         if(this.pruneModifiedRecords){
10936             this.modified.remove(record);
10937         }
10938         this.fireEvent("remove", this, record, index);
10939     },
10940
10941     /**
10942      * Remove all Records from the Store and fires the clear event.
10943      */
10944     removeAll : function(){
10945         this.data.clear();
10946         if(this.pruneModifiedRecords){
10947             this.modified = [];
10948         }
10949         this.fireEvent("clear", this);
10950     },
10951
10952     /**
10953      * Inserts Records to the Store at the given index and fires the add event.
10954      * @param {Number} index The start index at which to insert the passed Records.
10955      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10956      */
10957     insert : function(index, records){
10958         records = [].concat(records);
10959         for(var i = 0, len = records.length; i < len; i++){
10960             this.data.insert(index, records[i]);
10961             records[i].join(this);
10962         }
10963         this.fireEvent("add", this, records, index);
10964     },
10965
10966     /**
10967      * Get the index within the cache of the passed Record.
10968      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10969      * @return {Number} The index of the passed Record. Returns -1 if not found.
10970      */
10971     indexOf : function(record){
10972         return this.data.indexOf(record);
10973     },
10974
10975     /**
10976      * Get the index within the cache of the Record with the passed id.
10977      * @param {String} id The id of the Record to find.
10978      * @return {Number} The index of the Record. Returns -1 if not found.
10979      */
10980     indexOfId : function(id){
10981         return this.data.indexOfKey(id);
10982     },
10983
10984     /**
10985      * Get the Record with the specified id.
10986      * @param {String} id The id of the Record to find.
10987      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10988      */
10989     getById : function(id){
10990         return this.data.key(id);
10991     },
10992
10993     /**
10994      * Get the Record at the specified index.
10995      * @param {Number} index The index of the Record to find.
10996      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10997      */
10998     getAt : function(index){
10999         return this.data.itemAt(index);
11000     },
11001
11002     /**
11003      * Returns a range of Records between specified indices.
11004      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11005      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11006      * @return {Roo.data.Record[]} An array of Records
11007      */
11008     getRange : function(start, end){
11009         return this.data.getRange(start, end);
11010     },
11011
11012     // private
11013     storeOptions : function(o){
11014         o = Roo.apply({}, o);
11015         delete o.callback;
11016         delete o.scope;
11017         this.lastOptions = o;
11018     },
11019
11020     /**
11021      * Loads the Record cache from the configured Proxy using the configured Reader.
11022      * <p>
11023      * If using remote paging, then the first load call must specify the <em>start</em>
11024      * and <em>limit</em> properties in the options.params property to establish the initial
11025      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11026      * <p>
11027      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11028      * and this call will return before the new data has been loaded. Perform any post-processing
11029      * in a callback function, or in a "load" event handler.</strong>
11030      * <p>
11031      * @param {Object} options An object containing properties which control loading options:<ul>
11032      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11033      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11034      * passed the following arguments:<ul>
11035      * <li>r : Roo.data.Record[]</li>
11036      * <li>options: Options object from the load call</li>
11037      * <li>success: Boolean success indicator</li></ul></li>
11038      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11039      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11040      * </ul>
11041      */
11042     load : function(options){
11043         options = options || {};
11044         if(this.fireEvent("beforeload", this, options) !== false){
11045             this.storeOptions(options);
11046             var p = Roo.apply(options.params || {}, this.baseParams);
11047             // if meta was not loaded from remote source.. try requesting it.
11048             if (!this.reader.metaFromRemote) {
11049                 p._requestMeta = 1;
11050             }
11051             if(this.sortInfo && this.remoteSort){
11052                 var pn = this.paramNames;
11053                 p[pn["sort"]] = this.sortInfo.field;
11054                 p[pn["dir"]] = this.sortInfo.direction;
11055             }
11056             if (this.multiSort) {
11057                 var pn = this.paramNames;
11058                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11059             }
11060             
11061             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11062         }
11063     },
11064
11065     /**
11066      * Reloads the Record cache from the configured Proxy using the configured Reader and
11067      * the options from the last load operation performed.
11068      * @param {Object} options (optional) An object containing properties which may override the options
11069      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11070      * the most recently used options are reused).
11071      */
11072     reload : function(options){
11073         this.load(Roo.applyIf(options||{}, this.lastOptions));
11074     },
11075
11076     // private
11077     // Called as a callback by the Reader during a load operation.
11078     loadRecords : function(o, options, success){
11079         if(!o || success === false){
11080             if(success !== false){
11081                 this.fireEvent("load", this, [], options, o);
11082             }
11083             if(options.callback){
11084                 options.callback.call(options.scope || this, [], options, false);
11085             }
11086             return;
11087         }
11088         // if data returned failure - throw an exception.
11089         if (o.success === false) {
11090             // show a message if no listener is registered.
11091             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11092                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11093             }
11094             // loadmask wil be hooked into this..
11095             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11096             return;
11097         }
11098         var r = o.records, t = o.totalRecords || r.length;
11099         
11100         this.fireEvent("beforeloadadd", this, r, options, o);
11101         
11102         if(!options || options.add !== true){
11103             if(this.pruneModifiedRecords){
11104                 this.modified = [];
11105             }
11106             for(var i = 0, len = r.length; i < len; i++){
11107                 r[i].join(this);
11108             }
11109             if(this.snapshot){
11110                 this.data = this.snapshot;
11111                 delete this.snapshot;
11112             }
11113             this.data.clear();
11114             this.data.addAll(r);
11115             this.totalLength = t;
11116             this.applySort();
11117             this.fireEvent("datachanged", this);
11118         }else{
11119             this.totalLength = Math.max(t, this.data.length+r.length);
11120             this.add(r);
11121         }
11122         
11123         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11124                 
11125             var e = new Roo.data.Record({});
11126
11127             e.set(this.parent.displayField, this.parent.emptyTitle);
11128             e.set(this.parent.valueField, '');
11129
11130             this.insert(0, e);
11131         }
11132             
11133         this.fireEvent("load", this, r, options, o);
11134         if(options.callback){
11135             options.callback.call(options.scope || this, r, options, true);
11136         }
11137     },
11138
11139
11140     /**
11141      * Loads data from a passed data block. A Reader which understands the format of the data
11142      * must have been configured in the constructor.
11143      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11144      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11145      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11146      */
11147     loadData : function(o, append){
11148         var r = this.reader.readRecords(o);
11149         this.loadRecords(r, {add: append}, true);
11150     },
11151
11152     /**
11153      * Gets the number of cached records.
11154      * <p>
11155      * <em>If using paging, this may not be the total size of the dataset. If the data object
11156      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11157      * the data set size</em>
11158      */
11159     getCount : function(){
11160         return this.data.length || 0;
11161     },
11162
11163     /**
11164      * Gets the total number of records in the dataset as returned by the server.
11165      * <p>
11166      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11167      * the dataset size</em>
11168      */
11169     getTotalCount : function(){
11170         return this.totalLength || 0;
11171     },
11172
11173     /**
11174      * Returns the sort state of the Store as an object with two properties:
11175      * <pre><code>
11176  field {String} The name of the field by which the Records are sorted
11177  direction {String} The sort order, "ASC" or "DESC"
11178      * </code></pre>
11179      */
11180     getSortState : function(){
11181         return this.sortInfo;
11182     },
11183
11184     // private
11185     applySort : function(){
11186         if(this.sortInfo && !this.remoteSort){
11187             var s = this.sortInfo, f = s.field;
11188             var st = this.fields.get(f).sortType;
11189             var fn = function(r1, r2){
11190                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11191                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11192             };
11193             this.data.sort(s.direction, fn);
11194             if(this.snapshot && this.snapshot != this.data){
11195                 this.snapshot.sort(s.direction, fn);
11196             }
11197         }
11198     },
11199
11200     /**
11201      * Sets the default sort column and order to be used by the next load operation.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     setDefaultSort : function(field, dir){
11206         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11207     },
11208
11209     /**
11210      * Sort the Records.
11211      * If remote sorting is used, the sort is performed on the server, and the cache is
11212      * reloaded. If local sorting is used, the cache is sorted internally.
11213      * @param {String} fieldName The name of the field to sort by.
11214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11215      */
11216     sort : function(fieldName, dir){
11217         var f = this.fields.get(fieldName);
11218         if(!dir){
11219             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11220             
11221             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11222                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11223             }else{
11224                 dir = f.sortDir;
11225             }
11226         }
11227         this.sortToggle[f.name] = dir;
11228         this.sortInfo = {field: f.name, direction: dir};
11229         if(!this.remoteSort){
11230             this.applySort();
11231             this.fireEvent("datachanged", this);
11232         }else{
11233             this.load(this.lastOptions);
11234         }
11235     },
11236
11237     /**
11238      * Calls the specified function for each of the Records in the cache.
11239      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11240      * Returning <em>false</em> aborts and exits the iteration.
11241      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11242      */
11243     each : function(fn, scope){
11244         this.data.each(fn, scope);
11245     },
11246
11247     /**
11248      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11249      * (e.g., during paging).
11250      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11251      */
11252     getModifiedRecords : function(){
11253         return this.modified;
11254     },
11255
11256     // private
11257     createFilterFn : function(property, value, anyMatch){
11258         if(!value.exec){ // not a regex
11259             value = String(value);
11260             if(value.length == 0){
11261                 return false;
11262             }
11263             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11264         }
11265         return function(r){
11266             return value.test(r.data[property]);
11267         };
11268     },
11269
11270     /**
11271      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11272      * @param {String} property A field on your records
11273      * @param {Number} start The record index to start at (defaults to 0)
11274      * @param {Number} end The last record index to include (defaults to length - 1)
11275      * @return {Number} The sum
11276      */
11277     sum : function(property, start, end){
11278         var rs = this.data.items, v = 0;
11279         start = start || 0;
11280         end = (end || end === 0) ? end : rs.length-1;
11281
11282         for(var i = start; i <= end; i++){
11283             v += (rs[i].data[property] || 0);
11284         }
11285         return v;
11286     },
11287
11288     /**
11289      * Filter the records by a specified property.
11290      * @param {String} field A field on your records
11291      * @param {String/RegExp} value Either a string that the field
11292      * should start with or a RegExp to test against the field
11293      * @param {Boolean} anyMatch True to match any part not just the beginning
11294      */
11295     filter : function(property, value, anyMatch){
11296         var fn = this.createFilterFn(property, value, anyMatch);
11297         return fn ? this.filterBy(fn) : this.clearFilter();
11298     },
11299
11300     /**
11301      * Filter by a function. The specified function will be called with each
11302      * record in this data source. If the function returns true the record is included,
11303      * otherwise it is filtered.
11304      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11305      * @param {Object} scope (optional) The scope of the function (defaults to this)
11306      */
11307     filterBy : function(fn, scope){
11308         this.snapshot = this.snapshot || this.data;
11309         this.data = this.queryBy(fn, scope||this);
11310         this.fireEvent("datachanged", this);
11311     },
11312
11313     /**
11314      * Query the records by a specified property.
11315      * @param {String} field A field on your records
11316      * @param {String/RegExp} value Either a string that the field
11317      * should start with or a RegExp to test against the field
11318      * @param {Boolean} anyMatch True to match any part not just the beginning
11319      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11320      */
11321     query : function(property, value, anyMatch){
11322         var fn = this.createFilterFn(property, value, anyMatch);
11323         return fn ? this.queryBy(fn) : this.data.clone();
11324     },
11325
11326     /**
11327      * Query by a function. The specified function will be called with each
11328      * record in this data source. If the function returns true the record is included
11329      * in the results.
11330      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11331      * @param {Object} scope (optional) The scope of the function (defaults to this)
11332       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11333      **/
11334     queryBy : function(fn, scope){
11335         var data = this.snapshot || this.data;
11336         return data.filterBy(fn, scope||this);
11337     },
11338
11339     /**
11340      * Collects unique values for a particular dataIndex from this store.
11341      * @param {String} dataIndex The property to collect
11342      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11343      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11344      * @return {Array} An array of the unique values
11345      **/
11346     collect : function(dataIndex, allowNull, bypassFilter){
11347         var d = (bypassFilter === true && this.snapshot) ?
11348                 this.snapshot.items : this.data.items;
11349         var v, sv, r = [], l = {};
11350         for(var i = 0, len = d.length; i < len; i++){
11351             v = d[i].data[dataIndex];
11352             sv = String(v);
11353             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11354                 l[sv] = true;
11355                 r[r.length] = v;
11356             }
11357         }
11358         return r;
11359     },
11360
11361     /**
11362      * Revert to a view of the Record cache with no filtering applied.
11363      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11364      */
11365     clearFilter : function(suppressEvent){
11366         if(this.snapshot && this.snapshot != this.data){
11367             this.data = this.snapshot;
11368             delete this.snapshot;
11369             if(suppressEvent !== true){
11370                 this.fireEvent("datachanged", this);
11371             }
11372         }
11373     },
11374
11375     // private
11376     afterEdit : function(record){
11377         if(this.modified.indexOf(record) == -1){
11378             this.modified.push(record);
11379         }
11380         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11381     },
11382     
11383     // private
11384     afterReject : function(record){
11385         this.modified.remove(record);
11386         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11387     },
11388
11389     // private
11390     afterCommit : function(record){
11391         this.modified.remove(record);
11392         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11393     },
11394
11395     /**
11396      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11397      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11398      */
11399     commitChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].commit();
11404         }
11405     },
11406
11407     /**
11408      * Cancel outstanding changes on all changed records.
11409      */
11410     rejectChanges : function(){
11411         var m = this.modified.slice(0);
11412         this.modified = [];
11413         for(var i = 0, len = m.length; i < len; i++){
11414             m[i].reject();
11415         }
11416     },
11417
11418     onMetaChange : function(meta, rtype, o){
11419         this.recordType = rtype;
11420         this.fields = rtype.prototype.fields;
11421         delete this.snapshot;
11422         this.sortInfo = meta.sortInfo || this.sortInfo;
11423         this.modified = [];
11424         this.fireEvent('metachange', this, this.reader.meta);
11425     },
11426     
11427     moveIndex : function(data, type)
11428     {
11429         var index = this.indexOf(data);
11430         
11431         var newIndex = index + type;
11432         
11433         this.remove(data);
11434         
11435         this.insert(newIndex, data);
11436         
11437     }
11438 });/*
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  * @class Roo.data.SimpleStore
11451  * @extends Roo.data.Store
11452  * Small helper class to make creating Stores from Array data easier.
11453  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11454  * @cfg {Array} fields An array of field definition objects, or field name strings.
11455  * @cfg {Array} data The multi-dimensional array of data
11456  * @constructor
11457  * @param {Object} config
11458  */
11459 Roo.data.SimpleStore = function(config){
11460     Roo.data.SimpleStore.superclass.constructor.call(this, {
11461         isLocal : true,
11462         reader: new Roo.data.ArrayReader({
11463                 id: config.id
11464             },
11465             Roo.data.Record.create(config.fields)
11466         ),
11467         proxy : new Roo.data.MemoryProxy(config.data)
11468     });
11469     this.load();
11470 };
11471 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11472  * Based on:
11473  * Ext JS Library 1.1.1
11474  * Copyright(c) 2006-2007, Ext JS, LLC.
11475  *
11476  * Originally Released Under LGPL - original licence link has changed is not relivant.
11477  *
11478  * Fork - LGPL
11479  * <script type="text/javascript">
11480  */
11481
11482 /**
11483 /**
11484  * @extends Roo.data.Store
11485  * @class Roo.data.JsonStore
11486  * Small helper class to make creating Stores for JSON data easier. <br/>
11487 <pre><code>
11488 var store = new Roo.data.JsonStore({
11489     url: 'get-images.php',
11490     root: 'images',
11491     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11492 });
11493 </code></pre>
11494  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11495  * JsonReader and HttpProxy (unless inline data is provided).</b>
11496  * @cfg {Array} fields An array of field definition objects, or field name strings.
11497  * @constructor
11498  * @param {Object} config
11499  */
11500 Roo.data.JsonStore = function(c){
11501     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11502         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11503         reader: new Roo.data.JsonReader(c, c.fields)
11504     }));
11505 };
11506 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11507  * Based on:
11508  * Ext JS Library 1.1.1
11509  * Copyright(c) 2006-2007, Ext JS, LLC.
11510  *
11511  * Originally Released Under LGPL - original licence link has changed is not relivant.
11512  *
11513  * Fork - LGPL
11514  * <script type="text/javascript">
11515  */
11516
11517  
11518 Roo.data.Field = function(config){
11519     if(typeof config == "string"){
11520         config = {name: config};
11521     }
11522     Roo.apply(this, config);
11523     
11524     if(!this.type){
11525         this.type = "auto";
11526     }
11527     
11528     var st = Roo.data.SortTypes;
11529     // named sortTypes are supported, here we look them up
11530     if(typeof this.sortType == "string"){
11531         this.sortType = st[this.sortType];
11532     }
11533     
11534     // set default sortType for strings and dates
11535     if(!this.sortType){
11536         switch(this.type){
11537             case "string":
11538                 this.sortType = st.asUCString;
11539                 break;
11540             case "date":
11541                 this.sortType = st.asDate;
11542                 break;
11543             default:
11544                 this.sortType = st.none;
11545         }
11546     }
11547
11548     // define once
11549     var stripRe = /[\$,%]/g;
11550
11551     // prebuilt conversion function for this field, instead of
11552     // switching every time we're reading a value
11553     if(!this.convert){
11554         var cv, dateFormat = this.dateFormat;
11555         switch(this.type){
11556             case "":
11557             case "auto":
11558             case undefined:
11559                 cv = function(v){ return v; };
11560                 break;
11561             case "string":
11562                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11563                 break;
11564             case "int":
11565                 cv = function(v){
11566                     return v !== undefined && v !== null && v !== '' ?
11567                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11568                     };
11569                 break;
11570             case "float":
11571                 cv = function(v){
11572                     return v !== undefined && v !== null && v !== '' ?
11573                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11574                     };
11575                 break;
11576             case "bool":
11577             case "boolean":
11578                 cv = function(v){ return v === true || v === "true" || v == 1; };
11579                 break;
11580             case "date":
11581                 cv = function(v){
11582                     if(!v){
11583                         return '';
11584                     }
11585                     if(v instanceof Date){
11586                         return v;
11587                     }
11588                     if(dateFormat){
11589                         if(dateFormat == "timestamp"){
11590                             return new Date(v*1000);
11591                         }
11592                         return Date.parseDate(v, dateFormat);
11593                     }
11594                     var parsed = Date.parse(v);
11595                     return parsed ? new Date(parsed) : null;
11596                 };
11597              break;
11598             
11599         }
11600         this.convert = cv;
11601     }
11602 };
11603
11604 Roo.data.Field.prototype = {
11605     dateFormat: null,
11606     defaultValue: "",
11607     mapping: null,
11608     sortType : null,
11609     sortDir : "ASC"
11610 };/*
11611  * Based on:
11612  * Ext JS Library 1.1.1
11613  * Copyright(c) 2006-2007, Ext JS, LLC.
11614  *
11615  * Originally Released Under LGPL - original licence link has changed is not relivant.
11616  *
11617  * Fork - LGPL
11618  * <script type="text/javascript">
11619  */
11620  
11621 // Base class for reading structured data from a data source.  This class is intended to be
11622 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11623
11624 /**
11625  * @class Roo.data.DataReader
11626  * Base class for reading structured data from a data source.  This class is intended to be
11627  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11628  */
11629
11630 Roo.data.DataReader = function(meta, recordType){
11631     
11632     this.meta = meta;
11633     
11634     this.recordType = recordType instanceof Array ? 
11635         Roo.data.Record.create(recordType) : recordType;
11636 };
11637
11638 Roo.data.DataReader.prototype = {
11639      /**
11640      * Create an empty record
11641      * @param {Object} data (optional) - overlay some values
11642      * @return {Roo.data.Record} record created.
11643      */
11644     newRow :  function(d) {
11645         var da =  {};
11646         this.recordType.prototype.fields.each(function(c) {
11647             switch( c.type) {
11648                 case 'int' : da[c.name] = 0; break;
11649                 case 'date' : da[c.name] = new Date(); break;
11650                 case 'float' : da[c.name] = 0.0; break;
11651                 case 'boolean' : da[c.name] = false; break;
11652                 default : da[c.name] = ""; break;
11653             }
11654             
11655         });
11656         return new this.recordType(Roo.apply(da, d));
11657     }
11658     
11659 };/*
11660  * Based on:
11661  * Ext JS Library 1.1.1
11662  * Copyright(c) 2006-2007, Ext JS, LLC.
11663  *
11664  * Originally Released Under LGPL - original licence link has changed is not relivant.
11665  *
11666  * Fork - LGPL
11667  * <script type="text/javascript">
11668  */
11669
11670 /**
11671  * @class Roo.data.DataProxy
11672  * @extends Roo.data.Observable
11673  * This class is an abstract base class for implementations which provide retrieval of
11674  * unformatted data objects.<br>
11675  * <p>
11676  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11677  * (of the appropriate type which knows how to parse the data object) to provide a block of
11678  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11679  * <p>
11680  * Custom implementations must implement the load method as described in
11681  * {@link Roo.data.HttpProxy#load}.
11682  */
11683 Roo.data.DataProxy = function(){
11684     this.addEvents({
11685         /**
11686          * @event beforeload
11687          * Fires before a network request is made to retrieve a data object.
11688          * @param {Object} This DataProxy object.
11689          * @param {Object} params The params parameter to the load function.
11690          */
11691         beforeload : true,
11692         /**
11693          * @event load
11694          * Fires before the load method's callback is called.
11695          * @param {Object} This DataProxy object.
11696          * @param {Object} o The data object.
11697          * @param {Object} arg The callback argument object passed to the load function.
11698          */
11699         load : true,
11700         /**
11701          * @event loadexception
11702          * Fires if an Exception occurs during data retrieval.
11703          * @param {Object} This DataProxy object.
11704          * @param {Object} o The data object.
11705          * @param {Object} arg The callback argument object passed to the load function.
11706          * @param {Object} e The Exception.
11707          */
11708         loadexception : true
11709     });
11710     Roo.data.DataProxy.superclass.constructor.call(this);
11711 };
11712
11713 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11714
11715     /**
11716      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11717      */
11718 /*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728 /**
11729  * @class Roo.data.MemoryProxy
11730  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11731  * to the Reader when its load method is called.
11732  * @constructor
11733  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11734  */
11735 Roo.data.MemoryProxy = function(data){
11736     if (data.data) {
11737         data = data.data;
11738     }
11739     Roo.data.MemoryProxy.superclass.constructor.call(this);
11740     this.data = data;
11741 };
11742
11743 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11744     
11745     /**
11746      * Load data from the requested source (in this case an in-memory
11747      * data object passed to the constructor), read the data object into
11748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11749      * process that block using the passed callback.
11750      * @param {Object} params This parameter is not used by the MemoryProxy class.
11751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11752      * object into a block of Roo.data.Records.
11753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11754      * The function must be passed <ul>
11755      * <li>The Record block object</li>
11756      * <li>The "arg" argument from the load function</li>
11757      * <li>A boolean success indicator</li>
11758      * </ul>
11759      * @param {Object} scope The scope in which to call the callback
11760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11761      */
11762     load : function(params, reader, callback, scope, arg){
11763         params = params || {};
11764         var result;
11765         try {
11766             result = reader.readRecords(this.data);
11767         }catch(e){
11768             this.fireEvent("loadexception", this, arg, null, e);
11769             callback.call(scope, null, arg, false);
11770             return;
11771         }
11772         callback.call(scope, result, arg, true);
11773     },
11774     
11775     // private
11776     update : function(params, records){
11777         
11778     }
11779 });/*
11780  * Based on:
11781  * Ext JS Library 1.1.1
11782  * Copyright(c) 2006-2007, Ext JS, LLC.
11783  *
11784  * Originally Released Under LGPL - original licence link has changed is not relivant.
11785  *
11786  * Fork - LGPL
11787  * <script type="text/javascript">
11788  */
11789 /**
11790  * @class Roo.data.HttpProxy
11791  * @extends Roo.data.DataProxy
11792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11793  * configured to reference a certain URL.<br><br>
11794  * <p>
11795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11796  * from which the running page was served.<br><br>
11797  * <p>
11798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11799  * <p>
11800  * Be aware that to enable the browser to parse an XML document, the server must set
11801  * the Content-Type header in the HTTP response to "text/xml".
11802  * @constructor
11803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11805  * will be used to make the request.
11806  */
11807 Roo.data.HttpProxy = function(conn){
11808     Roo.data.HttpProxy.superclass.constructor.call(this);
11809     // is conn a conn config or a real conn?
11810     this.conn = conn;
11811     this.useAjax = !conn || !conn.events;
11812   
11813 };
11814
11815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11816     // thse are take from connection...
11817     
11818     /**
11819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11820      */
11821     /**
11822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11823      * extra parameters to each request made by this object. (defaults to undefined)
11824      */
11825     /**
11826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11827      *  to each request made by this object. (defaults to undefined)
11828      */
11829     /**
11830      * @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)
11831      */
11832     /**
11833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11834      */
11835      /**
11836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11837      * @type Boolean
11838      */
11839   
11840
11841     /**
11842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11843      * @type Boolean
11844      */
11845     /**
11846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11848      * a finer-grained basis than the DataProxy events.
11849      */
11850     getConnection : function(){
11851         return this.useAjax ? Roo.Ajax : this.conn;
11852     },
11853
11854     /**
11855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11857      * process that block using the passed callback.
11858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11859      * for the request to the remote server.
11860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11861      * object into a block of Roo.data.Records.
11862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11863      * The function must be passed <ul>
11864      * <li>The Record block object</li>
11865      * <li>The "arg" argument from the load function</li>
11866      * <li>A boolean success indicator</li>
11867      * </ul>
11868      * @param {Object} scope The scope in which to call the callback
11869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11870      */
11871     load : function(params, reader, callback, scope, arg){
11872         if(this.fireEvent("beforeload", this, params) !== false){
11873             var  o = {
11874                 params : params || {},
11875                 request: {
11876                     callback : callback,
11877                     scope : scope,
11878                     arg : arg
11879                 },
11880                 reader: reader,
11881                 callback : this.loadResponse,
11882                 scope: this
11883             };
11884             if(this.useAjax){
11885                 Roo.applyIf(o, this.conn);
11886                 if(this.activeRequest){
11887                     Roo.Ajax.abort(this.activeRequest);
11888                 }
11889                 this.activeRequest = Roo.Ajax.request(o);
11890             }else{
11891                 this.conn.request(o);
11892             }
11893         }else{
11894             callback.call(scope||this, null, arg, false);
11895         }
11896     },
11897
11898     // private
11899     loadResponse : function(o, success, response){
11900         delete this.activeRequest;
11901         if(!success){
11902             this.fireEvent("loadexception", this, o, response);
11903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11904             return;
11905         }
11906         var result;
11907         try {
11908             result = o.reader.read(response);
11909         }catch(e){
11910             this.fireEvent("loadexception", this, o, response, e);
11911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11912             return;
11913         }
11914         
11915         this.fireEvent("load", this, o, o.request.arg);
11916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11917     },
11918
11919     // private
11920     update : function(dataSet){
11921
11922     },
11923
11924     // private
11925     updateResponse : function(dataSet){
11926
11927     }
11928 });/*
11929  * Based on:
11930  * Ext JS Library 1.1.1
11931  * Copyright(c) 2006-2007, Ext JS, LLC.
11932  *
11933  * Originally Released Under LGPL - original licence link has changed is not relivant.
11934  *
11935  * Fork - LGPL
11936  * <script type="text/javascript">
11937  */
11938
11939 /**
11940  * @class Roo.data.ScriptTagProxy
11941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11942  * other than the originating domain of the running page.<br><br>
11943  * <p>
11944  * <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
11945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11946  * <p>
11947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11948  * source code that is used as the source inside a &lt;script> tag.<br><br>
11949  * <p>
11950  * In order for the browser to process the returned data, the server must wrap the data object
11951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11953  * depending on whether the callback name was passed:
11954  * <p>
11955  * <pre><code>
11956 boolean scriptTag = false;
11957 String cb = request.getParameter("callback");
11958 if (cb != null) {
11959     scriptTag = true;
11960     response.setContentType("text/javascript");
11961 } else {
11962     response.setContentType("application/x-json");
11963 }
11964 Writer out = response.getWriter();
11965 if (scriptTag) {
11966     out.write(cb + "(");
11967 }
11968 out.print(dataBlock.toJsonString());
11969 if (scriptTag) {
11970     out.write(");");
11971 }
11972 </pre></code>
11973  *
11974  * @constructor
11975  * @param {Object} config A configuration object.
11976  */
11977 Roo.data.ScriptTagProxy = function(config){
11978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11979     Roo.apply(this, config);
11980     this.head = document.getElementsByTagName("head")[0];
11981 };
11982
11983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11984
11985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11986     /**
11987      * @cfg {String} url The URL from which to request the data object.
11988      */
11989     /**
11990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11991      */
11992     timeout : 30000,
11993     /**
11994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11995      * the server the name of the callback function set up by the load call to process the returned data object.
11996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11997      * javascript output which calls this named function passing the data object as its only parameter.
11998      */
11999     callbackParam : "callback",
12000     /**
12001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12002      * name to the request.
12003      */
12004     nocache : true,
12005
12006     /**
12007      * Load data from the configured URL, read the data object into
12008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009      * process that block using the passed callback.
12010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12011      * for the request to the remote server.
12012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013      * object into a block of Roo.data.Records.
12014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12015      * The function must be passed <ul>
12016      * <li>The Record block object</li>
12017      * <li>The "arg" argument from the load function</li>
12018      * <li>A boolean success indicator</li>
12019      * </ul>
12020      * @param {Object} scope The scope in which to call the callback
12021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022      */
12023     load : function(params, reader, callback, scope, arg){
12024         if(this.fireEvent("beforeload", this, params) !== false){
12025
12026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12027
12028             var url = this.url;
12029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12030             if(this.nocache){
12031                 url += "&_dc=" + (new Date().getTime());
12032             }
12033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12034             var trans = {
12035                 id : transId,
12036                 cb : "stcCallback"+transId,
12037                 scriptId : "stcScript"+transId,
12038                 params : params,
12039                 arg : arg,
12040                 url : url,
12041                 callback : callback,
12042                 scope : scope,
12043                 reader : reader
12044             };
12045             var conn = this;
12046
12047             window[trans.cb] = function(o){
12048                 conn.handleResponse(o, trans);
12049             };
12050
12051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12052
12053             if(this.autoAbort !== false){
12054                 this.abort();
12055             }
12056
12057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12058
12059             var script = document.createElement("script");
12060             script.setAttribute("src", url);
12061             script.setAttribute("type", "text/javascript");
12062             script.setAttribute("id", trans.scriptId);
12063             this.head.appendChild(script);
12064
12065             this.trans = trans;
12066         }else{
12067             callback.call(scope||this, null, arg, false);
12068         }
12069     },
12070
12071     // private
12072     isLoading : function(){
12073         return this.trans ? true : false;
12074     },
12075
12076     /**
12077      * Abort the current server request.
12078      */
12079     abort : function(){
12080         if(this.isLoading()){
12081             this.destroyTrans(this.trans);
12082         }
12083     },
12084
12085     // private
12086     destroyTrans : function(trans, isLoaded){
12087         this.head.removeChild(document.getElementById(trans.scriptId));
12088         clearTimeout(trans.timeoutId);
12089         if(isLoaded){
12090             window[trans.cb] = undefined;
12091             try{
12092                 delete window[trans.cb];
12093             }catch(e){}
12094         }else{
12095             // if hasn't been loaded, wait for load to remove it to prevent script error
12096             window[trans.cb] = function(){
12097                 window[trans.cb] = undefined;
12098                 try{
12099                     delete window[trans.cb];
12100                 }catch(e){}
12101             };
12102         }
12103     },
12104
12105     // private
12106     handleResponse : function(o, trans){
12107         this.trans = false;
12108         this.destroyTrans(trans, true);
12109         var result;
12110         try {
12111             result = trans.reader.readRecords(o);
12112         }catch(e){
12113             this.fireEvent("loadexception", this, o, trans.arg, e);
12114             trans.callback.call(trans.scope||window, null, trans.arg, false);
12115             return;
12116         }
12117         this.fireEvent("load", this, o, trans.arg);
12118         trans.callback.call(trans.scope||window, result, trans.arg, true);
12119     },
12120
12121     // private
12122     handleFailure : function(trans){
12123         this.trans = false;
12124         this.destroyTrans(trans, false);
12125         this.fireEvent("loadexception", this, null, trans.arg);
12126         trans.callback.call(trans.scope||window, null, trans.arg, false);
12127     }
12128 });/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139 /**
12140  * @class Roo.data.JsonReader
12141  * @extends Roo.data.DataReader
12142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12143  * based on mappings in a provided Roo.data.Record constructor.
12144  * 
12145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12146  * in the reply previously. 
12147  * 
12148  * <p>
12149  * Example code:
12150  * <pre><code>
12151 var RecordDef = Roo.data.Record.create([
12152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12154 ]);
12155 var myReader = new Roo.data.JsonReader({
12156     totalProperty: "results",    // The property which contains the total dataset size (optional)
12157     root: "rows",                // The property which contains an Array of row objects
12158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12159 }, RecordDef);
12160 </code></pre>
12161  * <p>
12162  * This would consume a JSON file like this:
12163  * <pre><code>
12164 { 'results': 2, 'rows': [
12165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12167 }
12168 </code></pre>
12169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12171  * paged from the remote server.
12172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12173  * @cfg {String} root name of the property which contains the Array of row objects.
12174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12175  * @cfg {Array} fields Array of field definition objects
12176  * @constructor
12177  * Create a new JsonReader
12178  * @param {Object} meta Metadata configuration options
12179  * @param {Object} recordType Either an Array of field definition objects,
12180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12181  */
12182 Roo.data.JsonReader = function(meta, recordType){
12183     
12184     meta = meta || {};
12185     // set some defaults:
12186     Roo.applyIf(meta, {
12187         totalProperty: 'total',
12188         successProperty : 'success',
12189         root : 'data',
12190         id : 'id'
12191     });
12192     
12193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12194 };
12195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12196     
12197     /**
12198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12199      * Used by Store query builder to append _requestMeta to params.
12200      * 
12201      */
12202     metaFromRemote : false,
12203     /**
12204      * This method is only used by a DataProxy which has retrieved data from a remote server.
12205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12206      * @return {Object} data A data block which is used by an Roo.data.Store object as
12207      * a cache of Roo.data.Records.
12208      */
12209     read : function(response){
12210         var json = response.responseText;
12211        
12212         var o = /* eval:var:o */ eval("("+json+")");
12213         if(!o) {
12214             throw {message: "JsonReader.read: Json object not found"};
12215         }
12216         
12217         if(o.metaData){
12218             
12219             delete this.ef;
12220             this.metaFromRemote = true;
12221             this.meta = o.metaData;
12222             this.recordType = Roo.data.Record.create(o.metaData.fields);
12223             this.onMetaChange(this.meta, this.recordType, o);
12224         }
12225         return this.readRecords(o);
12226     },
12227
12228     // private function a store will implement
12229     onMetaChange : function(meta, recordType, o){
12230
12231     },
12232
12233     /**
12234          * @ignore
12235          */
12236     simpleAccess: function(obj, subsc) {
12237         return obj[subsc];
12238     },
12239
12240         /**
12241          * @ignore
12242          */
12243     getJsonAccessor: function(){
12244         var re = /[\[\.]/;
12245         return function(expr) {
12246             try {
12247                 return(re.test(expr))
12248                     ? new Function("obj", "return obj." + expr)
12249                     : function(obj){
12250                         return obj[expr];
12251                     };
12252             } catch(e){}
12253             return Roo.emptyFn;
12254         };
12255     }(),
12256
12257     /**
12258      * Create a data block containing Roo.data.Records from an XML document.
12259      * @param {Object} o An object which contains an Array of row objects in the property specified
12260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12261      * which contains the total size of the dataset.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     readRecords : function(o){
12266         /**
12267          * After any data loads, the raw JSON data is available for further custom processing.
12268          * @type Object
12269          */
12270         this.o = o;
12271         var s = this.meta, Record = this.recordType,
12272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12273
12274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12275         if (!this.ef) {
12276             if(s.totalProperty) {
12277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12278                 }
12279                 if(s.successProperty) {
12280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12281                 }
12282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12283                 if (s.id) {
12284                         var g = this.getJsonAccessor(s.id);
12285                         this.getId = function(rec) {
12286                                 var r = g(rec);  
12287                                 return (r === undefined || r === "") ? null : r;
12288                         };
12289                 } else {
12290                         this.getId = function(){return null;};
12291                 }
12292             this.ef = [];
12293             for(var jj = 0; jj < fl; jj++){
12294                 f = fi[jj];
12295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12296                 this.ef[jj] = this.getJsonAccessor(map);
12297             }
12298         }
12299
12300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12301         if(s.totalProperty){
12302             var vt = parseInt(this.getTotal(o), 10);
12303             if(!isNaN(vt)){
12304                 totalRecords = vt;
12305             }
12306         }
12307         if(s.successProperty){
12308             var vs = this.getSuccess(o);
12309             if(vs === false || vs === 'false'){
12310                 success = false;
12311             }
12312         }
12313         var records = [];
12314         for(var i = 0; i < c; i++){
12315                 var n = root[i];
12316             var values = {};
12317             var id = this.getId(n);
12318             for(var j = 0; j < fl; j++){
12319                 f = fi[j];
12320             var v = this.ef[j](n);
12321             if (!f.convert) {
12322                 Roo.log('missing convert for ' + f.name);
12323                 Roo.log(f);
12324                 continue;
12325             }
12326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12327             }
12328             var record = new Record(values, id);
12329             record.json = n;
12330             records[i] = record;
12331         }
12332         return {
12333             raw : o,
12334             success : success,
12335             records : records,
12336             totalRecords : totalRecords
12337         };
12338     }
12339 });/*
12340  * Based on:
12341  * Ext JS Library 1.1.1
12342  * Copyright(c) 2006-2007, Ext JS, LLC.
12343  *
12344  * Originally Released Under LGPL - original licence link has changed is not relivant.
12345  *
12346  * Fork - LGPL
12347  * <script type="text/javascript">
12348  */
12349
12350 /**
12351  * @class Roo.data.ArrayReader
12352  * @extends Roo.data.DataReader
12353  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12354  * Each element of that Array represents a row of data fields. The
12355  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12356  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12357  * <p>
12358  * Example code:.
12359  * <pre><code>
12360 var RecordDef = Roo.data.Record.create([
12361     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12362     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12363 ]);
12364 var myReader = new Roo.data.ArrayReader({
12365     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12366 }, RecordDef);
12367 </code></pre>
12368  * <p>
12369  * This would consume an Array like this:
12370  * <pre><code>
12371 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12372   </code></pre>
12373  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12374  * @constructor
12375  * Create a new JsonReader
12376  * @param {Object} meta Metadata configuration options.
12377  * @param {Object} recordType Either an Array of field definition objects
12378  * as specified to {@link Roo.data.Record#create},
12379  * or an {@link Roo.data.Record} object
12380  * created using {@link Roo.data.Record#create}.
12381  */
12382 Roo.data.ArrayReader = function(meta, recordType){
12383     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12384 };
12385
12386 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12387     /**
12388      * Create a data block containing Roo.data.Records from an XML document.
12389      * @param {Object} o An Array of row objects which represents the dataset.
12390      * @return {Object} data A data block which is used by an Roo.data.Store object as
12391      * a cache of Roo.data.Records.
12392      */
12393     readRecords : function(o){
12394         var sid = this.meta ? this.meta.id : null;
12395         var recordType = this.recordType, fields = recordType.prototype.fields;
12396         var records = [];
12397         var root = o;
12398             for(var i = 0; i < root.length; i++){
12399                     var n = root[i];
12400                 var values = {};
12401                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12402                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12403                 var f = fields.items[j];
12404                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12405                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12406                 v = f.convert(v);
12407                 values[f.name] = v;
12408             }
12409                 var record = new recordType(values, id);
12410                 record.json = n;
12411                 records[records.length] = record;
12412             }
12413             return {
12414                 records : records,
12415                 totalRecords : records.length
12416             };
12417     }
12418 });/*
12419  * - LGPL
12420  * * 
12421  */
12422
12423 /**
12424  * @class Roo.bootstrap.ComboBox
12425  * @extends Roo.bootstrap.TriggerField
12426  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12427  * @cfg {Boolean} append (true|false) default false
12428  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12429  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12430  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12431  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12432  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12433  * @cfg {Boolean} animate default true
12434  * @cfg {Boolean} emptyResultText only for touch device
12435  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12436  * @cfg {String} emptyTitle default ''
12437  * @constructor
12438  * Create a new ComboBox.
12439  * @param {Object} config Configuration options
12440  */
12441 Roo.bootstrap.ComboBox = function(config){
12442     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12443     this.addEvents({
12444         /**
12445          * @event expand
12446          * Fires when the dropdown list is expanded
12447         * @param {Roo.bootstrap.ComboBox} combo This combo box
12448         */
12449         'expand' : true,
12450         /**
12451          * @event collapse
12452          * Fires when the dropdown list is collapsed
12453         * @param {Roo.bootstrap.ComboBox} combo This combo box
12454         */
12455         'collapse' : true,
12456         /**
12457          * @event beforeselect
12458          * Fires before a list item is selected. Return false to cancel the selection.
12459         * @param {Roo.bootstrap.ComboBox} combo This combo box
12460         * @param {Roo.data.Record} record The data record returned from the underlying store
12461         * @param {Number} index The index of the selected item in the dropdown list
12462         */
12463         'beforeselect' : true,
12464         /**
12465          * @event select
12466          * Fires when a list item is selected
12467         * @param {Roo.bootstrap.ComboBox} combo This combo box
12468         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12469         * @param {Number} index The index of the selected item in the dropdown list
12470         */
12471         'select' : true,
12472         /**
12473          * @event beforequery
12474          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12475          * The event object passed has these properties:
12476         * @param {Roo.bootstrap.ComboBox} combo This combo box
12477         * @param {String} query The query
12478         * @param {Boolean} forceAll true to force "all" query
12479         * @param {Boolean} cancel true to cancel the query
12480         * @param {Object} e The query event object
12481         */
12482         'beforequery': true,
12483          /**
12484          * @event add
12485          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12486         * @param {Roo.bootstrap.ComboBox} combo This combo box
12487         */
12488         'add' : true,
12489         /**
12490          * @event edit
12491          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12492         * @param {Roo.bootstrap.ComboBox} combo This combo box
12493         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12494         */
12495         'edit' : true,
12496         /**
12497          * @event remove
12498          * Fires when the remove value from the combobox array
12499         * @param {Roo.bootstrap.ComboBox} combo This combo box
12500         */
12501         'remove' : true,
12502         /**
12503          * @event afterremove
12504          * Fires when the remove value from the combobox array
12505         * @param {Roo.bootstrap.ComboBox} combo This combo box
12506         */
12507         'afterremove' : true,
12508         /**
12509          * @event specialfilter
12510          * Fires when specialfilter
12511             * @param {Roo.bootstrap.ComboBox} combo This combo box
12512             */
12513         'specialfilter' : true,
12514         /**
12515          * @event tick
12516          * Fires when tick the element
12517             * @param {Roo.bootstrap.ComboBox} combo This combo box
12518             */
12519         'tick' : true,
12520         /**
12521          * @event touchviewdisplay
12522          * Fires when touch view require special display (default is using displayField)
12523             * @param {Roo.bootstrap.ComboBox} combo This combo box
12524             * @param {Object} cfg set html .
12525             */
12526         'touchviewdisplay' : true
12527         
12528     });
12529     
12530     this.item = [];
12531     this.tickItems = [];
12532     
12533     this.selectedIndex = -1;
12534     if(this.mode == 'local'){
12535         if(config.queryDelay === undefined){
12536             this.queryDelay = 10;
12537         }
12538         if(config.minChars === undefined){
12539             this.minChars = 0;
12540         }
12541     }
12542 };
12543
12544 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12545      
12546     /**
12547      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12548      * rendering into an Roo.Editor, defaults to false)
12549      */
12550     /**
12551      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12552      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12553      */
12554     /**
12555      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12556      */
12557     /**
12558      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12559      * the dropdown list (defaults to undefined, with no header element)
12560      */
12561
12562      /**
12563      * @cfg {String/Roo.Template} tpl The template to use to render the output
12564      */
12565      
12566      /**
12567      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12568      */
12569     listWidth: undefined,
12570     /**
12571      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12572      * mode = 'remote' or 'text' if mode = 'local')
12573      */
12574     displayField: undefined,
12575     
12576     /**
12577      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12578      * mode = 'remote' or 'value' if mode = 'local'). 
12579      * Note: use of a valueField requires the user make a selection
12580      * in order for a value to be mapped.
12581      */
12582     valueField: undefined,
12583     /**
12584      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12585      */
12586     modalTitle : '',
12587     
12588     /**
12589      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12590      * field's data value (defaults to the underlying DOM element's name)
12591      */
12592     hiddenName: undefined,
12593     /**
12594      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12595      */
12596     listClass: '',
12597     /**
12598      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12599      */
12600     selectedClass: 'active',
12601     
12602     /**
12603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12604      */
12605     shadow:'sides',
12606     /**
12607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12608      * anchor positions (defaults to 'tl-bl')
12609      */
12610     listAlign: 'tl-bl?',
12611     /**
12612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12613      */
12614     maxHeight: 300,
12615     /**
12616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12617      * query specified by the allQuery config option (defaults to 'query')
12618      */
12619     triggerAction: 'query',
12620     /**
12621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12622      * (defaults to 4, does not apply if editable = false)
12623      */
12624     minChars : 4,
12625     /**
12626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12628      */
12629     typeAhead: false,
12630     /**
12631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12633      */
12634     queryDelay: 500,
12635     /**
12636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12638      */
12639     pageSize: 0,
12640     /**
12641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12642      * when editable = true (defaults to false)
12643      */
12644     selectOnFocus:false,
12645     /**
12646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12647      */
12648     queryParam: 'query',
12649     /**
12650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12651      * when mode = 'remote' (defaults to 'Loading...')
12652      */
12653     loadingText: 'Loading...',
12654     /**
12655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12656      */
12657     resizable: false,
12658     /**
12659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12660      */
12661     handleHeight : 8,
12662     /**
12663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12664      * traditional select (defaults to true)
12665      */
12666     editable: true,
12667     /**
12668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12669      */
12670     allQuery: '',
12671     /**
12672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12673      */
12674     mode: 'remote',
12675     /**
12676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12677      * listWidth has a higher value)
12678      */
12679     minListWidth : 70,
12680     /**
12681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12682      * allow the user to set arbitrary text into the field (defaults to false)
12683      */
12684     forceSelection:false,
12685     /**
12686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12687      * if typeAhead = true (defaults to 250)
12688      */
12689     typeAheadDelay : 250,
12690     /**
12691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12693      */
12694     valueNotFoundText : undefined,
12695     /**
12696      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12697      */
12698     blockFocus : false,
12699     
12700     /**
12701      * @cfg {Boolean} disableClear Disable showing of clear button.
12702      */
12703     disableClear : false,
12704     /**
12705      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12706      */
12707     alwaysQuery : false,
12708     
12709     /**
12710      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12711      */
12712     multiple : false,
12713     
12714     /**
12715      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12716      */
12717     invalidClass : "has-warning",
12718     
12719     /**
12720      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12721      */
12722     validClass : "has-success",
12723     
12724     /**
12725      * @cfg {Boolean} specialFilter (true|false) special filter default false
12726      */
12727     specialFilter : false,
12728     
12729     /**
12730      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12731      */
12732     mobileTouchView : true,
12733     
12734     /**
12735      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12736      */
12737     useNativeIOS : false,
12738     
12739     ios_options : false,
12740     
12741     //private
12742     addicon : false,
12743     editicon: false,
12744     
12745     page: 0,
12746     hasQuery: false,
12747     append: false,
12748     loadNext: false,
12749     autoFocus : true,
12750     tickable : false,
12751     btnPosition : 'right',
12752     triggerList : true,
12753     showToggleBtn : true,
12754     animate : true,
12755     emptyResultText: 'Empty',
12756     triggerText : 'Select',
12757     emptyTitle : '',
12758     
12759     // element that contains real text value.. (when hidden is used..)
12760     
12761     getAutoCreate : function()
12762     {   
12763         var cfg = false;
12764         //render
12765         /*
12766          * Render classic select for iso
12767          */
12768         
12769         if(Roo.isIOS && this.useNativeIOS){
12770             cfg = this.getAutoCreateNativeIOS();
12771             return cfg;
12772         }
12773         
12774         /*
12775          * Touch Devices
12776          */
12777         
12778         if(Roo.isTouch && this.mobileTouchView){
12779             cfg = this.getAutoCreateTouchView();
12780             return cfg;;
12781         }
12782         
12783         /*
12784          *  Normal ComboBox
12785          */
12786         if(!this.tickable){
12787             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12788             return cfg;
12789         }
12790         
12791         /*
12792          *  ComboBox with tickable selections
12793          */
12794              
12795         var align = this.labelAlign || this.parentLabelAlign();
12796         
12797         cfg = {
12798             cls : 'form-group roo-combobox-tickable' //input-group
12799         };
12800         
12801         var btn_text_select = '';
12802         var btn_text_done = '';
12803         var btn_text_cancel = '';
12804         
12805         if (this.btn_text_show) {
12806             btn_text_select = 'Select';
12807             btn_text_done = 'Done';
12808             btn_text_cancel = 'Cancel'; 
12809         }
12810         
12811         var buttons = {
12812             tag : 'div',
12813             cls : 'tickable-buttons',
12814             cn : [
12815                 {
12816                     tag : 'button',
12817                     type : 'button',
12818                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12819                     //html : this.triggerText
12820                     html: btn_text_select
12821                 },
12822                 {
12823                     tag : 'button',
12824                     type : 'button',
12825                     name : 'ok',
12826                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12827                     //html : 'Done'
12828                     html: btn_text_done
12829                 },
12830                 {
12831                     tag : 'button',
12832                     type : 'button',
12833                     name : 'cancel',
12834                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12835                     //html : 'Cancel'
12836                     html: btn_text_cancel
12837                 }
12838             ]
12839         };
12840         
12841         if(this.editable){
12842             buttons.cn.unshift({
12843                 tag: 'input',
12844                 cls: 'roo-select2-search-field-input'
12845             });
12846         }
12847         
12848         var _this = this;
12849         
12850         Roo.each(buttons.cn, function(c){
12851             if (_this.size) {
12852                 c.cls += ' btn-' + _this.size;
12853             }
12854
12855             if (_this.disabled) {
12856                 c.disabled = true;
12857             }
12858         });
12859         
12860         var box = {
12861             tag: 'div',
12862             cn: [
12863                 {
12864                     tag: 'input',
12865                     type : 'hidden',
12866                     cls: 'form-hidden-field'
12867                 },
12868                 {
12869                     tag: 'ul',
12870                     cls: 'roo-select2-choices',
12871                     cn:[
12872                         {
12873                             tag: 'li',
12874                             cls: 'roo-select2-search-field',
12875                             cn: [
12876                                 buttons
12877                             ]
12878                         }
12879                     ]
12880                 }
12881             ]
12882         };
12883         
12884         var combobox = {
12885             cls: 'roo-select2-container input-group roo-select2-container-multi',
12886             cn: [
12887                 box
12888 //                {
12889 //                    tag: 'ul',
12890 //                    cls: 'typeahead typeahead-long dropdown-menu',
12891 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12892 //                }
12893             ]
12894         };
12895         
12896         if(this.hasFeedback && !this.allowBlank){
12897             
12898             var feedback = {
12899                 tag: 'span',
12900                 cls: 'glyphicon form-control-feedback'
12901             };
12902
12903             combobox.cn.push(feedback);
12904         }
12905         
12906         
12907         if (align ==='left' && this.fieldLabel.length) {
12908             
12909             cfg.cls += ' roo-form-group-label-left';
12910             
12911             cfg.cn = [
12912                 {
12913                     tag : 'i',
12914                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12915                     tooltip : 'This field is required'
12916                 },
12917                 {
12918                     tag: 'label',
12919                     'for' :  id,
12920                     cls : 'control-label',
12921                     html : this.fieldLabel
12922
12923                 },
12924                 {
12925                     cls : "", 
12926                     cn: [
12927                         combobox
12928                     ]
12929                 }
12930
12931             ];
12932             
12933             var labelCfg = cfg.cn[1];
12934             var contentCfg = cfg.cn[2];
12935             
12936
12937             if(this.indicatorpos == 'right'){
12938                 
12939                 cfg.cn = [
12940                     {
12941                         tag: 'label',
12942                         'for' :  id,
12943                         cls : 'control-label',
12944                         cn : [
12945                             {
12946                                 tag : 'span',
12947                                 html : this.fieldLabel
12948                             },
12949                             {
12950                                 tag : 'i',
12951                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12952                                 tooltip : 'This field is required'
12953                             }
12954                         ]
12955                     },
12956                     {
12957                         cls : "",
12958                         cn: [
12959                             combobox
12960                         ]
12961                     }
12962
12963                 ];
12964                 
12965                 
12966                 
12967                 labelCfg = cfg.cn[0];
12968                 contentCfg = cfg.cn[1];
12969             
12970             }
12971             
12972             if(this.labelWidth > 12){
12973                 labelCfg.style = "width: " + this.labelWidth + 'px';
12974             }
12975             
12976             if(this.labelWidth < 13 && this.labelmd == 0){
12977                 this.labelmd = this.labelWidth;
12978             }
12979             
12980             if(this.labellg > 0){
12981                 labelCfg.cls += ' col-lg-' + this.labellg;
12982                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12983             }
12984             
12985             if(this.labelmd > 0){
12986                 labelCfg.cls += ' col-md-' + this.labelmd;
12987                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12988             }
12989             
12990             if(this.labelsm > 0){
12991                 labelCfg.cls += ' col-sm-' + this.labelsm;
12992                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12993             }
12994             
12995             if(this.labelxs > 0){
12996                 labelCfg.cls += ' col-xs-' + this.labelxs;
12997                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12998             }
12999                 
13000                 
13001         } else if ( this.fieldLabel.length) {
13002 //                Roo.log(" label");
13003                  cfg.cn = [
13004                     {
13005                         tag : 'i',
13006                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13007                         tooltip : 'This field is required'
13008                     },
13009                     {
13010                         tag: 'label',
13011                         //cls : 'input-group-addon',
13012                         html : this.fieldLabel
13013                     },
13014                     combobox
13015                 ];
13016                 
13017                 if(this.indicatorpos == 'right'){
13018                     cfg.cn = [
13019                         {
13020                             tag: 'label',
13021                             //cls : 'input-group-addon',
13022                             html : this.fieldLabel
13023                         },
13024                         {
13025                             tag : 'i',
13026                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13027                             tooltip : 'This field is required'
13028                         },
13029                         combobox
13030                     ];
13031                     
13032                 }
13033
13034         } else {
13035             
13036 //                Roo.log(" no label && no align");
13037                 cfg = combobox
13038                      
13039                 
13040         }
13041          
13042         var settings=this;
13043         ['xs','sm','md','lg'].map(function(size){
13044             if (settings[size]) {
13045                 cfg.cls += ' col-' + size + '-' + settings[size];
13046             }
13047         });
13048         
13049         return cfg;
13050         
13051     },
13052     
13053     _initEventsCalled : false,
13054     
13055     // private
13056     initEvents: function()
13057     {   
13058         if (this._initEventsCalled) { // as we call render... prevent looping...
13059             return;
13060         }
13061         this._initEventsCalled = true;
13062         
13063         if (!this.store) {
13064             throw "can not find store for combo";
13065         }
13066         
13067         this.indicator = this.indicatorEl();
13068         
13069         this.store = Roo.factory(this.store, Roo.data);
13070         this.store.parent = this;
13071         
13072         // if we are building from html. then this element is so complex, that we can not really
13073         // use the rendered HTML.
13074         // so we have to trash and replace the previous code.
13075         if (Roo.XComponent.build_from_html) {
13076             // remove this element....
13077             var e = this.el.dom, k=0;
13078             while (e ) { e = e.previousSibling;  ++k;}
13079
13080             this.el.remove();
13081             
13082             this.el=false;
13083             this.rendered = false;
13084             
13085             this.render(this.parent().getChildContainer(true), k);
13086         }
13087         
13088         if(Roo.isIOS && this.useNativeIOS){
13089             this.initIOSView();
13090             return;
13091         }
13092         
13093         /*
13094          * Touch Devices
13095          */
13096         
13097         if(Roo.isTouch && this.mobileTouchView){
13098             this.initTouchView();
13099             return;
13100         }
13101         
13102         if(this.tickable){
13103             this.initTickableEvents();
13104             return;
13105         }
13106         
13107         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13108         
13109         if(this.hiddenName){
13110             
13111             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13112             
13113             this.hiddenField.dom.value =
13114                 this.hiddenValue !== undefined ? this.hiddenValue :
13115                 this.value !== undefined ? this.value : '';
13116
13117             // prevent input submission
13118             this.el.dom.removeAttribute('name');
13119             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13120              
13121              
13122         }
13123         //if(Roo.isGecko){
13124         //    this.el.dom.setAttribute('autocomplete', 'off');
13125         //}
13126         
13127         var cls = 'x-combo-list';
13128         
13129         //this.list = new Roo.Layer({
13130         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13131         //});
13132         
13133         var _this = this;
13134         
13135         (function(){
13136             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13137             _this.list.setWidth(lw);
13138         }).defer(100);
13139         
13140         this.list.on('mouseover', this.onViewOver, this);
13141         this.list.on('mousemove', this.onViewMove, this);
13142         this.list.on('scroll', this.onViewScroll, this);
13143         
13144         /*
13145         this.list.swallowEvent('mousewheel');
13146         this.assetHeight = 0;
13147
13148         if(this.title){
13149             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13150             this.assetHeight += this.header.getHeight();
13151         }
13152
13153         this.innerList = this.list.createChild({cls:cls+'-inner'});
13154         this.innerList.on('mouseover', this.onViewOver, this);
13155         this.innerList.on('mousemove', this.onViewMove, this);
13156         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13157         
13158         if(this.allowBlank && !this.pageSize && !this.disableClear){
13159             this.footer = this.list.createChild({cls:cls+'-ft'});
13160             this.pageTb = new Roo.Toolbar(this.footer);
13161            
13162         }
13163         if(this.pageSize){
13164             this.footer = this.list.createChild({cls:cls+'-ft'});
13165             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13166                     {pageSize: this.pageSize});
13167             
13168         }
13169         
13170         if (this.pageTb && this.allowBlank && !this.disableClear) {
13171             var _this = this;
13172             this.pageTb.add(new Roo.Toolbar.Fill(), {
13173                 cls: 'x-btn-icon x-btn-clear',
13174                 text: '&#160;',
13175                 handler: function()
13176                 {
13177                     _this.collapse();
13178                     _this.clearValue();
13179                     _this.onSelect(false, -1);
13180                 }
13181             });
13182         }
13183         if (this.footer) {
13184             this.assetHeight += this.footer.getHeight();
13185         }
13186         */
13187             
13188         if(!this.tpl){
13189             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13190         }
13191
13192         this.view = new Roo.View(this.list, this.tpl, {
13193             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13194         });
13195         //this.view.wrapEl.setDisplayed(false);
13196         this.view.on('click', this.onViewClick, this);
13197         
13198         
13199         this.store.on('beforeload', this.onBeforeLoad, this);
13200         this.store.on('load', this.onLoad, this);
13201         this.store.on('loadexception', this.onLoadException, this);
13202         /*
13203         if(this.resizable){
13204             this.resizer = new Roo.Resizable(this.list,  {
13205                pinned:true, handles:'se'
13206             });
13207             this.resizer.on('resize', function(r, w, h){
13208                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13209                 this.listWidth = w;
13210                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13211                 this.restrictHeight();
13212             }, this);
13213             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13214         }
13215         */
13216         if(!this.editable){
13217             this.editable = true;
13218             this.setEditable(false);
13219         }
13220         
13221         /*
13222         
13223         if (typeof(this.events.add.listeners) != 'undefined') {
13224             
13225             this.addicon = this.wrap.createChild(
13226                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13227        
13228             this.addicon.on('click', function(e) {
13229                 this.fireEvent('add', this);
13230             }, this);
13231         }
13232         if (typeof(this.events.edit.listeners) != 'undefined') {
13233             
13234             this.editicon = this.wrap.createChild(
13235                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13236             if (this.addicon) {
13237                 this.editicon.setStyle('margin-left', '40px');
13238             }
13239             this.editicon.on('click', function(e) {
13240                 
13241                 // we fire even  if inothing is selected..
13242                 this.fireEvent('edit', this, this.lastData );
13243                 
13244             }, this);
13245         }
13246         */
13247         
13248         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13249             "up" : function(e){
13250                 this.inKeyMode = true;
13251                 this.selectPrev();
13252             },
13253
13254             "down" : function(e){
13255                 if(!this.isExpanded()){
13256                     this.onTriggerClick();
13257                 }else{
13258                     this.inKeyMode = true;
13259                     this.selectNext();
13260                 }
13261             },
13262
13263             "enter" : function(e){
13264 //                this.onViewClick();
13265                 //return true;
13266                 this.collapse();
13267                 
13268                 if(this.fireEvent("specialkey", this, e)){
13269                     this.onViewClick(false);
13270                 }
13271                 
13272                 return true;
13273             },
13274
13275             "esc" : function(e){
13276                 this.collapse();
13277             },
13278
13279             "tab" : function(e){
13280                 this.collapse();
13281                 
13282                 if(this.fireEvent("specialkey", this, e)){
13283                     this.onViewClick(false);
13284                 }
13285                 
13286                 return true;
13287             },
13288
13289             scope : this,
13290
13291             doRelay : function(foo, bar, hname){
13292                 if(hname == 'down' || this.scope.isExpanded()){
13293                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13294                 }
13295                 return true;
13296             },
13297
13298             forceKeyDown: true
13299         });
13300         
13301         
13302         this.queryDelay = Math.max(this.queryDelay || 10,
13303                 this.mode == 'local' ? 10 : 250);
13304         
13305         
13306         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13307         
13308         if(this.typeAhead){
13309             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13310         }
13311         if(this.editable !== false){
13312             this.inputEl().on("keyup", this.onKeyUp, this);
13313         }
13314         if(this.forceSelection){
13315             this.inputEl().on('blur', this.doForce, this);
13316         }
13317         
13318         if(this.multiple){
13319             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13320             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13321         }
13322     },
13323     
13324     initTickableEvents: function()
13325     {   
13326         this.createList();
13327         
13328         if(this.hiddenName){
13329             
13330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13331             
13332             this.hiddenField.dom.value =
13333                 this.hiddenValue !== undefined ? this.hiddenValue :
13334                 this.value !== undefined ? this.value : '';
13335
13336             // prevent input submission
13337             this.el.dom.removeAttribute('name');
13338             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13339              
13340              
13341         }
13342         
13343 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13344         
13345         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13346         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13347         if(this.triggerList){
13348             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13349         }
13350          
13351         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13352         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13353         
13354         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13355         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13356         
13357         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13358         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13359         
13360         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13361         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13362         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13363         
13364         this.okBtn.hide();
13365         this.cancelBtn.hide();
13366         
13367         var _this = this;
13368         
13369         (function(){
13370             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13371             _this.list.setWidth(lw);
13372         }).defer(100);
13373         
13374         this.list.on('mouseover', this.onViewOver, this);
13375         this.list.on('mousemove', this.onViewMove, this);
13376         
13377         this.list.on('scroll', this.onViewScroll, this);
13378         
13379         if(!this.tpl){
13380             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>';
13381         }
13382
13383         this.view = new Roo.View(this.list, this.tpl, {
13384             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13385         });
13386         
13387         //this.view.wrapEl.setDisplayed(false);
13388         this.view.on('click', this.onViewClick, this);
13389         
13390         
13391         
13392         this.store.on('beforeload', this.onBeforeLoad, this);
13393         this.store.on('load', this.onLoad, this);
13394         this.store.on('loadexception', this.onLoadException, this);
13395         
13396         if(this.editable){
13397             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13398                 "up" : function(e){
13399                     this.inKeyMode = true;
13400                     this.selectPrev();
13401                 },
13402
13403                 "down" : function(e){
13404                     this.inKeyMode = true;
13405                     this.selectNext();
13406                 },
13407
13408                 "enter" : function(e){
13409                     if(this.fireEvent("specialkey", this, e)){
13410                         this.onViewClick(false);
13411                     }
13412                     
13413                     return true;
13414                 },
13415
13416                 "esc" : function(e){
13417                     this.onTickableFooterButtonClick(e, false, false);
13418                 },
13419
13420                 "tab" : function(e){
13421                     this.fireEvent("specialkey", this, e);
13422                     
13423                     this.onTickableFooterButtonClick(e, false, false);
13424                     
13425                     return true;
13426                 },
13427
13428                 scope : this,
13429
13430                 doRelay : function(e, fn, key){
13431                     if(this.scope.isExpanded()){
13432                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13433                     }
13434                     return true;
13435                 },
13436
13437                 forceKeyDown: true
13438             });
13439         }
13440         
13441         this.queryDelay = Math.max(this.queryDelay || 10,
13442                 this.mode == 'local' ? 10 : 250);
13443         
13444         
13445         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13446         
13447         if(this.typeAhead){
13448             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13449         }
13450         
13451         if(this.editable !== false){
13452             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13453         }
13454         
13455         this.indicator = this.indicatorEl();
13456         
13457         if(this.indicator){
13458             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13459             this.indicator.hide();
13460         }
13461         
13462     },
13463
13464     onDestroy : function(){
13465         if(this.view){
13466             this.view.setStore(null);
13467             this.view.el.removeAllListeners();
13468             this.view.el.remove();
13469             this.view.purgeListeners();
13470         }
13471         if(this.list){
13472             this.list.dom.innerHTML  = '';
13473         }
13474         
13475         if(this.store){
13476             this.store.un('beforeload', this.onBeforeLoad, this);
13477             this.store.un('load', this.onLoad, this);
13478             this.store.un('loadexception', this.onLoadException, this);
13479         }
13480         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13481     },
13482
13483     // private
13484     fireKey : function(e){
13485         if(e.isNavKeyPress() && !this.list.isVisible()){
13486             this.fireEvent("specialkey", this, e);
13487         }
13488     },
13489
13490     // private
13491     onResize: function(w, h){
13492 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13493 //        
13494 //        if(typeof w != 'number'){
13495 //            // we do not handle it!?!?
13496 //            return;
13497 //        }
13498 //        var tw = this.trigger.getWidth();
13499 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13500 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13501 //        var x = w - tw;
13502 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13503 //            
13504 //        //this.trigger.setStyle('left', x+'px');
13505 //        
13506 //        if(this.list && this.listWidth === undefined){
13507 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13508 //            this.list.setWidth(lw);
13509 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13510 //        }
13511         
13512     
13513         
13514     },
13515
13516     /**
13517      * Allow or prevent the user from directly editing the field text.  If false is passed,
13518      * the user will only be able to select from the items defined in the dropdown list.  This method
13519      * is the runtime equivalent of setting the 'editable' config option at config time.
13520      * @param {Boolean} value True to allow the user to directly edit the field text
13521      */
13522     setEditable : function(value){
13523         if(value == this.editable){
13524             return;
13525         }
13526         this.editable = value;
13527         if(!value){
13528             this.inputEl().dom.setAttribute('readOnly', true);
13529             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13530             this.inputEl().addClass('x-combo-noedit');
13531         }else{
13532             this.inputEl().dom.setAttribute('readOnly', false);
13533             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13534             this.inputEl().removeClass('x-combo-noedit');
13535         }
13536     },
13537
13538     // private
13539     
13540     onBeforeLoad : function(combo,opts){
13541         if(!this.hasFocus){
13542             return;
13543         }
13544          if (!opts.add) {
13545             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13546          }
13547         this.restrictHeight();
13548         this.selectedIndex = -1;
13549     },
13550
13551     // private
13552     onLoad : function(){
13553         
13554         this.hasQuery = false;
13555         
13556         if(!this.hasFocus){
13557             return;
13558         }
13559         
13560         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13561             this.loading.hide();
13562         }
13563         
13564         if(this.store.getCount() > 0){
13565             
13566             this.expand();
13567             this.restrictHeight();
13568             if(this.lastQuery == this.allQuery){
13569                 if(this.editable && !this.tickable){
13570                     this.inputEl().dom.select();
13571                 }
13572                 
13573                 if(
13574                     !this.selectByValue(this.value, true) &&
13575                     this.autoFocus && 
13576                     (
13577                         !this.store.lastOptions ||
13578                         typeof(this.store.lastOptions.add) == 'undefined' || 
13579                         this.store.lastOptions.add != true
13580                     )
13581                 ){
13582                     this.select(0, true);
13583                 }
13584             }else{
13585                 if(this.autoFocus){
13586                     this.selectNext();
13587                 }
13588                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13589                     this.taTask.delay(this.typeAheadDelay);
13590                 }
13591             }
13592         }else{
13593             this.onEmptyResults();
13594         }
13595         
13596         //this.el.focus();
13597     },
13598     // private
13599     onLoadException : function()
13600     {
13601         this.hasQuery = false;
13602         
13603         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13604             this.loading.hide();
13605         }
13606         
13607         if(this.tickable && this.editable){
13608             return;
13609         }
13610         
13611         this.collapse();
13612         // only causes errors at present
13613         //Roo.log(this.store.reader.jsonData);
13614         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13615             // fixme
13616             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13617         //}
13618         
13619         
13620     },
13621     // private
13622     onTypeAhead : function(){
13623         if(this.store.getCount() > 0){
13624             var r = this.store.getAt(0);
13625             var newValue = r.data[this.displayField];
13626             var len = newValue.length;
13627             var selStart = this.getRawValue().length;
13628             
13629             if(selStart != len){
13630                 this.setRawValue(newValue);
13631                 this.selectText(selStart, newValue.length);
13632             }
13633         }
13634     },
13635
13636     // private
13637     onSelect : function(record, index){
13638         
13639         if(this.fireEvent('beforeselect', this, record, index) !== false){
13640         
13641             this.setFromData(index > -1 ? record.data : false);
13642             
13643             this.collapse();
13644             this.fireEvent('select', this, record, index);
13645         }
13646     },
13647
13648     /**
13649      * Returns the currently selected field value or empty string if no value is set.
13650      * @return {String} value The selected value
13651      */
13652     getValue : function()
13653     {
13654         if(Roo.isIOS && this.useNativeIOS){
13655             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13656         }
13657         
13658         if(this.multiple){
13659             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13660         }
13661         
13662         if(this.valueField){
13663             return typeof this.value != 'undefined' ? this.value : '';
13664         }else{
13665             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13666         }
13667     },
13668     
13669     getRawValue : function()
13670     {
13671         if(Roo.isIOS && this.useNativeIOS){
13672             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13673         }
13674         
13675         var v = this.inputEl().getValue();
13676         
13677         return v;
13678     },
13679
13680     /**
13681      * Clears any text/value currently set in the field
13682      */
13683     clearValue : function(){
13684         
13685         if(this.hiddenField){
13686             this.hiddenField.dom.value = '';
13687         }
13688         this.value = '';
13689         this.setRawValue('');
13690         this.lastSelectionText = '';
13691         this.lastData = false;
13692         
13693         var close = this.closeTriggerEl();
13694         
13695         if(close){
13696             close.hide();
13697         }
13698         
13699         this.validate();
13700         
13701     },
13702
13703     /**
13704      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13705      * will be displayed in the field.  If the value does not match the data value of an existing item,
13706      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13707      * Otherwise the field will be blank (although the value will still be set).
13708      * @param {String} value The value to match
13709      */
13710     setValue : function(v)
13711     {
13712         if(Roo.isIOS && this.useNativeIOS){
13713             this.setIOSValue(v);
13714             return;
13715         }
13716         
13717         if(this.multiple){
13718             this.syncValue();
13719             return;
13720         }
13721         
13722         var text = v;
13723         if(this.valueField){
13724             var r = this.findRecord(this.valueField, v);
13725             if(r){
13726                 text = r.data[this.displayField];
13727             }else if(this.valueNotFoundText !== undefined){
13728                 text = this.valueNotFoundText;
13729             }
13730         }
13731         this.lastSelectionText = text;
13732         if(this.hiddenField){
13733             this.hiddenField.dom.value = v;
13734         }
13735         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13736         this.value = v;
13737         
13738         var close = this.closeTriggerEl();
13739         
13740         if(close){
13741             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13742         }
13743         
13744         this.validate();
13745     },
13746     /**
13747      * @property {Object} the last set data for the element
13748      */
13749     
13750     lastData : false,
13751     /**
13752      * Sets the value of the field based on a object which is related to the record format for the store.
13753      * @param {Object} value the value to set as. or false on reset?
13754      */
13755     setFromData : function(o){
13756         
13757         if(this.multiple){
13758             this.addItem(o);
13759             return;
13760         }
13761             
13762         var dv = ''; // display value
13763         var vv = ''; // value value..
13764         this.lastData = o;
13765         if (this.displayField) {
13766             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13767         } else {
13768             // this is an error condition!!!
13769             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13770         }
13771         
13772         if(this.valueField){
13773             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13774         }
13775         
13776         var close = this.closeTriggerEl();
13777         
13778         if(close){
13779             if(dv.length || vv * 1 > 0){
13780                 close.show() ;
13781                 this.blockFocus=true;
13782             } else {
13783                 close.hide();
13784             }             
13785         }
13786         
13787         if(this.hiddenField){
13788             this.hiddenField.dom.value = vv;
13789             
13790             this.lastSelectionText = dv;
13791             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13792             this.value = vv;
13793             return;
13794         }
13795         // no hidden field.. - we store the value in 'value', but still display
13796         // display field!!!!
13797         this.lastSelectionText = dv;
13798         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13799         this.value = vv;
13800         
13801         
13802         
13803     },
13804     // private
13805     reset : function(){
13806         // overridden so that last data is reset..
13807         
13808         if(this.multiple){
13809             this.clearItem();
13810             return;
13811         }
13812         
13813         this.setValue(this.originalValue);
13814         //this.clearInvalid();
13815         this.lastData = false;
13816         if (this.view) {
13817             this.view.clearSelections();
13818         }
13819         
13820         this.validate();
13821     },
13822     // private
13823     findRecord : function(prop, value){
13824         var record;
13825         if(this.store.getCount() > 0){
13826             this.store.each(function(r){
13827                 if(r.data[prop] == value){
13828                     record = r;
13829                     return false;
13830                 }
13831                 return true;
13832             });
13833         }
13834         return record;
13835     },
13836     
13837     getName: function()
13838     {
13839         // returns hidden if it's set..
13840         if (!this.rendered) {return ''};
13841         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13842         
13843     },
13844     // private
13845     onViewMove : function(e, t){
13846         this.inKeyMode = false;
13847     },
13848
13849     // private
13850     onViewOver : function(e, t){
13851         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13852             return;
13853         }
13854         var item = this.view.findItemFromChild(t);
13855         
13856         if(item){
13857             var index = this.view.indexOf(item);
13858             this.select(index, false);
13859         }
13860     },
13861
13862     // private
13863     onViewClick : function(view, doFocus, el, e)
13864     {
13865         var index = this.view.getSelectedIndexes()[0];
13866         
13867         var r = this.store.getAt(index);
13868         
13869         if(this.tickable){
13870             
13871             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13872                 return;
13873             }
13874             
13875             var rm = false;
13876             var _this = this;
13877             
13878             Roo.each(this.tickItems, function(v,k){
13879                 
13880                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13881                     Roo.log(v);
13882                     _this.tickItems.splice(k, 1);
13883                     
13884                     if(typeof(e) == 'undefined' && view == false){
13885                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13886                     }
13887                     
13888                     rm = true;
13889                     return;
13890                 }
13891             });
13892             
13893             if(rm){
13894                 return;
13895             }
13896             
13897             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13898                 this.tickItems.push(r.data);
13899             }
13900             
13901             if(typeof(e) == 'undefined' && view == false){
13902                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13903             }
13904                     
13905             return;
13906         }
13907         
13908         if(r){
13909             this.onSelect(r, index);
13910         }
13911         if(doFocus !== false && !this.blockFocus){
13912             this.inputEl().focus();
13913         }
13914     },
13915
13916     // private
13917     restrictHeight : function(){
13918         //this.innerList.dom.style.height = '';
13919         //var inner = this.innerList.dom;
13920         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13921         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13922         //this.list.beginUpdate();
13923         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13924         this.list.alignTo(this.inputEl(), this.listAlign);
13925         this.list.alignTo(this.inputEl(), this.listAlign);
13926         //this.list.endUpdate();
13927     },
13928
13929     // private
13930     onEmptyResults : function(){
13931         
13932         if(this.tickable && this.editable){
13933             this.restrictHeight();
13934             return;
13935         }
13936         
13937         this.collapse();
13938     },
13939
13940     /**
13941      * Returns true if the dropdown list is expanded, else false.
13942      */
13943     isExpanded : function(){
13944         return this.list.isVisible();
13945     },
13946
13947     /**
13948      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13949      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13950      * @param {String} value The data value of the item to select
13951      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13952      * selected item if it is not currently in view (defaults to true)
13953      * @return {Boolean} True if the value matched an item in the list, else false
13954      */
13955     selectByValue : function(v, scrollIntoView){
13956         if(v !== undefined && v !== null){
13957             var r = this.findRecord(this.valueField || this.displayField, v);
13958             if(r){
13959                 this.select(this.store.indexOf(r), scrollIntoView);
13960                 return true;
13961             }
13962         }
13963         return false;
13964     },
13965
13966     /**
13967      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13968      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13969      * @param {Number} index The zero-based index of the list item to select
13970      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13971      * selected item if it is not currently in view (defaults to true)
13972      */
13973     select : function(index, scrollIntoView){
13974         this.selectedIndex = index;
13975         this.view.select(index);
13976         if(scrollIntoView !== false){
13977             var el = this.view.getNode(index);
13978             /*
13979              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13980              */
13981             if(el){
13982                 this.list.scrollChildIntoView(el, false);
13983             }
13984         }
13985     },
13986
13987     // private
13988     selectNext : function(){
13989         var ct = this.store.getCount();
13990         if(ct > 0){
13991             if(this.selectedIndex == -1){
13992                 this.select(0);
13993             }else if(this.selectedIndex < ct-1){
13994                 this.select(this.selectedIndex+1);
13995             }
13996         }
13997     },
13998
13999     // private
14000     selectPrev : function(){
14001         var ct = this.store.getCount();
14002         if(ct > 0){
14003             if(this.selectedIndex == -1){
14004                 this.select(0);
14005             }else if(this.selectedIndex != 0){
14006                 this.select(this.selectedIndex-1);
14007             }
14008         }
14009     },
14010
14011     // private
14012     onKeyUp : function(e){
14013         if(this.editable !== false && !e.isSpecialKey()){
14014             this.lastKey = e.getKey();
14015             this.dqTask.delay(this.queryDelay);
14016         }
14017     },
14018
14019     // private
14020     validateBlur : function(){
14021         return !this.list || !this.list.isVisible();   
14022     },
14023
14024     // private
14025     initQuery : function(){
14026         
14027         var v = this.getRawValue();
14028         
14029         if(this.tickable && this.editable){
14030             v = this.tickableInputEl().getValue();
14031         }
14032         
14033         this.doQuery(v);
14034     },
14035
14036     // private
14037     doForce : function(){
14038         if(this.inputEl().dom.value.length > 0){
14039             this.inputEl().dom.value =
14040                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14041              
14042         }
14043     },
14044
14045     /**
14046      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14047      * query allowing the query action to be canceled if needed.
14048      * @param {String} query The SQL query to execute
14049      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14050      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14051      * saved in the current store (defaults to false)
14052      */
14053     doQuery : function(q, forceAll){
14054         
14055         if(q === undefined || q === null){
14056             q = '';
14057         }
14058         var qe = {
14059             query: q,
14060             forceAll: forceAll,
14061             combo: this,
14062             cancel:false
14063         };
14064         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14065             return false;
14066         }
14067         q = qe.query;
14068         
14069         forceAll = qe.forceAll;
14070         if(forceAll === true || (q.length >= this.minChars)){
14071             
14072             this.hasQuery = true;
14073             
14074             if(this.lastQuery != q || this.alwaysQuery){
14075                 this.lastQuery = q;
14076                 if(this.mode == 'local'){
14077                     this.selectedIndex = -1;
14078                     if(forceAll){
14079                         this.store.clearFilter();
14080                     }else{
14081                         
14082                         if(this.specialFilter){
14083                             this.fireEvent('specialfilter', this);
14084                             this.onLoad();
14085                             return;
14086                         }
14087                         
14088                         this.store.filter(this.displayField, q);
14089                     }
14090                     
14091                     this.store.fireEvent("datachanged", this.store);
14092                     
14093                     this.onLoad();
14094                     
14095                     
14096                 }else{
14097                     
14098                     this.store.baseParams[this.queryParam] = q;
14099                     
14100                     var options = {params : this.getParams(q)};
14101                     
14102                     if(this.loadNext){
14103                         options.add = true;
14104                         options.params.start = this.page * this.pageSize;
14105                     }
14106                     
14107                     this.store.load(options);
14108                     
14109                     /*
14110                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14111                      *  we should expand the list on onLoad
14112                      *  so command out it
14113                      */
14114 //                    this.expand();
14115                 }
14116             }else{
14117                 this.selectedIndex = -1;
14118                 this.onLoad();   
14119             }
14120         }
14121         
14122         this.loadNext = false;
14123     },
14124     
14125     // private
14126     getParams : function(q){
14127         var p = {};
14128         //p[this.queryParam] = q;
14129         
14130         if(this.pageSize){
14131             p.start = 0;
14132             p.limit = this.pageSize;
14133         }
14134         return p;
14135     },
14136
14137     /**
14138      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14139      */
14140     collapse : function(){
14141         if(!this.isExpanded()){
14142             return;
14143         }
14144         
14145         this.list.hide();
14146         
14147         this.hasFocus = false;
14148         
14149         if(this.tickable){
14150             this.okBtn.hide();
14151             this.cancelBtn.hide();
14152             this.trigger.show();
14153             
14154             if(this.editable){
14155                 this.tickableInputEl().dom.value = '';
14156                 this.tickableInputEl().blur();
14157             }
14158             
14159         }
14160         
14161         Roo.get(document).un('mousedown', this.collapseIf, this);
14162         Roo.get(document).un('mousewheel', this.collapseIf, this);
14163         if (!this.editable) {
14164             Roo.get(document).un('keydown', this.listKeyPress, this);
14165         }
14166         this.fireEvent('collapse', this);
14167         
14168         this.validate();
14169     },
14170
14171     // private
14172     collapseIf : function(e){
14173         var in_combo  = e.within(this.el);
14174         var in_list =  e.within(this.list);
14175         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14176         
14177         if (in_combo || in_list || is_list) {
14178             //e.stopPropagation();
14179             return;
14180         }
14181         
14182         if(this.tickable){
14183             this.onTickableFooterButtonClick(e, false, false);
14184         }
14185
14186         this.collapse();
14187         
14188     },
14189
14190     /**
14191      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14192      */
14193     expand : function(){
14194        
14195         if(this.isExpanded() || !this.hasFocus){
14196             return;
14197         }
14198         
14199         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14200         this.list.setWidth(lw);
14201         
14202         Roo.log('expand');
14203         
14204         this.list.show();
14205         
14206         this.restrictHeight();
14207         
14208         if(this.tickable){
14209             
14210             this.tickItems = Roo.apply([], this.item);
14211             
14212             this.okBtn.show();
14213             this.cancelBtn.show();
14214             this.trigger.hide();
14215             
14216             if(this.editable){
14217                 this.tickableInputEl().focus();
14218             }
14219             
14220         }
14221         
14222         Roo.get(document).on('mousedown', this.collapseIf, this);
14223         Roo.get(document).on('mousewheel', this.collapseIf, this);
14224         if (!this.editable) {
14225             Roo.get(document).on('keydown', this.listKeyPress, this);
14226         }
14227         
14228         this.fireEvent('expand', this);
14229     },
14230
14231     // private
14232     // Implements the default empty TriggerField.onTriggerClick function
14233     onTriggerClick : function(e)
14234     {
14235         Roo.log('trigger click');
14236         
14237         if(this.disabled || !this.triggerList){
14238             return;
14239         }
14240         
14241         this.page = 0;
14242         this.loadNext = false;
14243         
14244         if(this.isExpanded()){
14245             this.collapse();
14246             if (!this.blockFocus) {
14247                 this.inputEl().focus();
14248             }
14249             
14250         }else {
14251             this.hasFocus = true;
14252             if(this.triggerAction == 'all') {
14253                 this.doQuery(this.allQuery, true);
14254             } else {
14255                 this.doQuery(this.getRawValue());
14256             }
14257             if (!this.blockFocus) {
14258                 this.inputEl().focus();
14259             }
14260         }
14261     },
14262     
14263     onTickableTriggerClick : function(e)
14264     {
14265         if(this.disabled){
14266             return;
14267         }
14268         
14269         this.page = 0;
14270         this.loadNext = false;
14271         this.hasFocus = true;
14272         
14273         if(this.triggerAction == 'all') {
14274             this.doQuery(this.allQuery, true);
14275         } else {
14276             this.doQuery(this.getRawValue());
14277         }
14278     },
14279     
14280     onSearchFieldClick : function(e)
14281     {
14282         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14283             this.onTickableFooterButtonClick(e, false, false);
14284             return;
14285         }
14286         
14287         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14288             return;
14289         }
14290         
14291         this.page = 0;
14292         this.loadNext = false;
14293         this.hasFocus = true;
14294         
14295         if(this.triggerAction == 'all') {
14296             this.doQuery(this.allQuery, true);
14297         } else {
14298             this.doQuery(this.getRawValue());
14299         }
14300     },
14301     
14302     listKeyPress : function(e)
14303     {
14304         //Roo.log('listkeypress');
14305         // scroll to first matching element based on key pres..
14306         if (e.isSpecialKey()) {
14307             return false;
14308         }
14309         var k = String.fromCharCode(e.getKey()).toUpperCase();
14310         //Roo.log(k);
14311         var match  = false;
14312         var csel = this.view.getSelectedNodes();
14313         var cselitem = false;
14314         if (csel.length) {
14315             var ix = this.view.indexOf(csel[0]);
14316             cselitem  = this.store.getAt(ix);
14317             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14318                 cselitem = false;
14319             }
14320             
14321         }
14322         
14323         this.store.each(function(v) { 
14324             if (cselitem) {
14325                 // start at existing selection.
14326                 if (cselitem.id == v.id) {
14327                     cselitem = false;
14328                 }
14329                 return true;
14330             }
14331                 
14332             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14333                 match = this.store.indexOf(v);
14334                 return false;
14335             }
14336             return true;
14337         }, this);
14338         
14339         if (match === false) {
14340             return true; // no more action?
14341         }
14342         // scroll to?
14343         this.view.select(match);
14344         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14345         sn.scrollIntoView(sn.dom.parentNode, false);
14346     },
14347     
14348     onViewScroll : function(e, t){
14349         
14350         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){
14351             return;
14352         }
14353         
14354         this.hasQuery = true;
14355         
14356         this.loading = this.list.select('.loading', true).first();
14357         
14358         if(this.loading === null){
14359             this.list.createChild({
14360                 tag: 'div',
14361                 cls: 'loading roo-select2-more-results roo-select2-active',
14362                 html: 'Loading more results...'
14363             });
14364             
14365             this.loading = this.list.select('.loading', true).first();
14366             
14367             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14368             
14369             this.loading.hide();
14370         }
14371         
14372         this.loading.show();
14373         
14374         var _combo = this;
14375         
14376         this.page++;
14377         this.loadNext = true;
14378         
14379         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14380         
14381         return;
14382     },
14383     
14384     addItem : function(o)
14385     {   
14386         var dv = ''; // display value
14387         
14388         if (this.displayField) {
14389             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14390         } else {
14391             // this is an error condition!!!
14392             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14393         }
14394         
14395         if(!dv.length){
14396             return;
14397         }
14398         
14399         var choice = this.choices.createChild({
14400             tag: 'li',
14401             cls: 'roo-select2-search-choice',
14402             cn: [
14403                 {
14404                     tag: 'div',
14405                     html: dv
14406                 },
14407                 {
14408                     tag: 'a',
14409                     href: '#',
14410                     cls: 'roo-select2-search-choice-close fa fa-times',
14411                     tabindex: '-1'
14412                 }
14413             ]
14414             
14415         }, this.searchField);
14416         
14417         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14418         
14419         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14420         
14421         this.item.push(o);
14422         
14423         this.lastData = o;
14424         
14425         this.syncValue();
14426         
14427         this.inputEl().dom.value = '';
14428         
14429         this.validate();
14430     },
14431     
14432     onRemoveItem : function(e, _self, o)
14433     {
14434         e.preventDefault();
14435         
14436         this.lastItem = Roo.apply([], this.item);
14437         
14438         var index = this.item.indexOf(o.data) * 1;
14439         
14440         if( index < 0){
14441             Roo.log('not this item?!');
14442             return;
14443         }
14444         
14445         this.item.splice(index, 1);
14446         o.item.remove();
14447         
14448         this.syncValue();
14449         
14450         this.fireEvent('remove', this, e);
14451         
14452         this.validate();
14453         
14454     },
14455     
14456     syncValue : function()
14457     {
14458         if(!this.item.length){
14459             this.clearValue();
14460             return;
14461         }
14462             
14463         var value = [];
14464         var _this = this;
14465         Roo.each(this.item, function(i){
14466             if(_this.valueField){
14467                 value.push(i[_this.valueField]);
14468                 return;
14469             }
14470
14471             value.push(i);
14472         });
14473
14474         this.value = value.join(',');
14475
14476         if(this.hiddenField){
14477             this.hiddenField.dom.value = this.value;
14478         }
14479         
14480         this.store.fireEvent("datachanged", this.store);
14481         
14482         this.validate();
14483     },
14484     
14485     clearItem : function()
14486     {
14487         if(!this.multiple){
14488             return;
14489         }
14490         
14491         this.item = [];
14492         
14493         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14494            c.remove();
14495         });
14496         
14497         this.syncValue();
14498         
14499         this.validate();
14500         
14501         if(this.tickable && !Roo.isTouch){
14502             this.view.refresh();
14503         }
14504     },
14505     
14506     inputEl: function ()
14507     {
14508         if(Roo.isIOS && this.useNativeIOS){
14509             return this.el.select('select.roo-ios-select', true).first();
14510         }
14511         
14512         if(Roo.isTouch && this.mobileTouchView){
14513             return this.el.select('input.form-control',true).first();
14514         }
14515         
14516         if(this.tickable){
14517             return this.searchField;
14518         }
14519         
14520         return this.el.select('input.form-control',true).first();
14521     },
14522     
14523     onTickableFooterButtonClick : function(e, btn, el)
14524     {
14525         e.preventDefault();
14526         
14527         this.lastItem = Roo.apply([], this.item);
14528         
14529         if(btn && btn.name == 'cancel'){
14530             this.tickItems = Roo.apply([], this.item);
14531             this.collapse();
14532             return;
14533         }
14534         
14535         this.clearItem();
14536         
14537         var _this = this;
14538         
14539         Roo.each(this.tickItems, function(o){
14540             _this.addItem(o);
14541         });
14542         
14543         this.collapse();
14544         
14545     },
14546     
14547     validate : function()
14548     {
14549         var v = this.getRawValue();
14550         
14551         if(this.multiple){
14552             v = this.getValue();
14553         }
14554         
14555         if(this.disabled || this.allowBlank || v.length){
14556             this.markValid();
14557             return true;
14558         }
14559         
14560         this.markInvalid();
14561         return false;
14562     },
14563     
14564     tickableInputEl : function()
14565     {
14566         if(!this.tickable || !this.editable){
14567             return this.inputEl();
14568         }
14569         
14570         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14571     },
14572     
14573     
14574     getAutoCreateTouchView : function()
14575     {
14576         var id = Roo.id();
14577         
14578         var cfg = {
14579             cls: 'form-group' //input-group
14580         };
14581         
14582         var input =  {
14583             tag: 'input',
14584             id : id,
14585             type : this.inputType,
14586             cls : 'form-control x-combo-noedit',
14587             autocomplete: 'new-password',
14588             placeholder : this.placeholder || '',
14589             readonly : true
14590         };
14591         
14592         if (this.name) {
14593             input.name = this.name;
14594         }
14595         
14596         if (this.size) {
14597             input.cls += ' input-' + this.size;
14598         }
14599         
14600         if (this.disabled) {
14601             input.disabled = true;
14602         }
14603         
14604         var inputblock = {
14605             cls : '',
14606             cn : [
14607                 input
14608             ]
14609         };
14610         
14611         if(this.before){
14612             inputblock.cls += ' input-group';
14613             
14614             inputblock.cn.unshift({
14615                 tag :'span',
14616                 cls : 'input-group-addon',
14617                 html : this.before
14618             });
14619         }
14620         
14621         if(this.removable && !this.multiple){
14622             inputblock.cls += ' roo-removable';
14623             
14624             inputblock.cn.push({
14625                 tag: 'button',
14626                 html : 'x',
14627                 cls : 'roo-combo-removable-btn close'
14628             });
14629         }
14630
14631         if(this.hasFeedback && !this.allowBlank){
14632             
14633             inputblock.cls += ' has-feedback';
14634             
14635             inputblock.cn.push({
14636                 tag: 'span',
14637                 cls: 'glyphicon form-control-feedback'
14638             });
14639             
14640         }
14641         
14642         if (this.after) {
14643             
14644             inputblock.cls += (this.before) ? '' : ' input-group';
14645             
14646             inputblock.cn.push({
14647                 tag :'span',
14648                 cls : 'input-group-addon',
14649                 html : this.after
14650             });
14651         }
14652
14653         var box = {
14654             tag: 'div',
14655             cn: [
14656                 {
14657                     tag: 'input',
14658                     type : 'hidden',
14659                     cls: 'form-hidden-field'
14660                 },
14661                 inputblock
14662             ]
14663             
14664         };
14665         
14666         if(this.multiple){
14667             box = {
14668                 tag: 'div',
14669                 cn: [
14670                     {
14671                         tag: 'input',
14672                         type : 'hidden',
14673                         cls: 'form-hidden-field'
14674                     },
14675                     {
14676                         tag: 'ul',
14677                         cls: 'roo-select2-choices',
14678                         cn:[
14679                             {
14680                                 tag: 'li',
14681                                 cls: 'roo-select2-search-field',
14682                                 cn: [
14683
14684                                     inputblock
14685                                 ]
14686                             }
14687                         ]
14688                     }
14689                 ]
14690             }
14691         };
14692         
14693         var combobox = {
14694             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14695             cn: [
14696                 box
14697             ]
14698         };
14699         
14700         if(!this.multiple && this.showToggleBtn){
14701             
14702             var caret = {
14703                         tag: 'span',
14704                         cls: 'caret'
14705             };
14706             
14707             if (this.caret != false) {
14708                 caret = {
14709                      tag: 'i',
14710                      cls: 'fa fa-' + this.caret
14711                 };
14712                 
14713             }
14714             
14715             combobox.cn.push({
14716                 tag :'span',
14717                 cls : 'input-group-addon btn dropdown-toggle',
14718                 cn : [
14719                     caret,
14720                     {
14721                         tag: 'span',
14722                         cls: 'combobox-clear',
14723                         cn  : [
14724                             {
14725                                 tag : 'i',
14726                                 cls: 'icon-remove'
14727                             }
14728                         ]
14729                     }
14730                 ]
14731
14732             })
14733         }
14734         
14735         if(this.multiple){
14736             combobox.cls += ' roo-select2-container-multi';
14737         }
14738         
14739         var align = this.labelAlign || this.parentLabelAlign();
14740         
14741         if (align ==='left' && this.fieldLabel.length) {
14742
14743             cfg.cn = [
14744                 {
14745                    tag : 'i',
14746                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14747                    tooltip : 'This field is required'
14748                 },
14749                 {
14750                     tag: 'label',
14751                     cls : 'control-label',
14752                     html : this.fieldLabel
14753
14754                 },
14755                 {
14756                     cls : '', 
14757                     cn: [
14758                         combobox
14759                     ]
14760                 }
14761             ];
14762             
14763             var labelCfg = cfg.cn[1];
14764             var contentCfg = cfg.cn[2];
14765             
14766
14767             if(this.indicatorpos == 'right'){
14768                 cfg.cn = [
14769                     {
14770                         tag: 'label',
14771                         'for' :  id,
14772                         cls : 'control-label',
14773                         cn : [
14774                             {
14775                                 tag : 'span',
14776                                 html : this.fieldLabel
14777                             },
14778                             {
14779                                 tag : 'i',
14780                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14781                                 tooltip : 'This field is required'
14782                             }
14783                         ]
14784                     },
14785                     {
14786                         cls : "",
14787                         cn: [
14788                             combobox
14789                         ]
14790                     }
14791
14792                 ];
14793                 
14794                 labelCfg = cfg.cn[0];
14795                 contentCfg = cfg.cn[1];
14796             }
14797             
14798            
14799             
14800             if(this.labelWidth > 12){
14801                 labelCfg.style = "width: " + this.labelWidth + 'px';
14802             }
14803             
14804             if(this.labelWidth < 13 && this.labelmd == 0){
14805                 this.labelmd = this.labelWidth;
14806             }
14807             
14808             if(this.labellg > 0){
14809                 labelCfg.cls += ' col-lg-' + this.labellg;
14810                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14811             }
14812             
14813             if(this.labelmd > 0){
14814                 labelCfg.cls += ' col-md-' + this.labelmd;
14815                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14816             }
14817             
14818             if(this.labelsm > 0){
14819                 labelCfg.cls += ' col-sm-' + this.labelsm;
14820                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14821             }
14822             
14823             if(this.labelxs > 0){
14824                 labelCfg.cls += ' col-xs-' + this.labelxs;
14825                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14826             }
14827                 
14828                 
14829         } else if ( this.fieldLabel.length) {
14830             cfg.cn = [
14831                 {
14832                    tag : 'i',
14833                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14834                    tooltip : 'This field is required'
14835                 },
14836                 {
14837                     tag: 'label',
14838                     cls : 'control-label',
14839                     html : this.fieldLabel
14840
14841                 },
14842                 {
14843                     cls : '', 
14844                     cn: [
14845                         combobox
14846                     ]
14847                 }
14848             ];
14849             
14850             if(this.indicatorpos == 'right'){
14851                 cfg.cn = [
14852                     {
14853                         tag: 'label',
14854                         cls : 'control-label',
14855                         html : this.fieldLabel,
14856                         cn : [
14857                             {
14858                                tag : 'i',
14859                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14860                                tooltip : 'This field is required'
14861                             }
14862                         ]
14863                     },
14864                     {
14865                         cls : '', 
14866                         cn: [
14867                             combobox
14868                         ]
14869                     }
14870                 ];
14871             }
14872         } else {
14873             cfg.cn = combobox;    
14874         }
14875         
14876         
14877         var settings = this;
14878         
14879         ['xs','sm','md','lg'].map(function(size){
14880             if (settings[size]) {
14881                 cfg.cls += ' col-' + size + '-' + settings[size];
14882             }
14883         });
14884         
14885         return cfg;
14886     },
14887     
14888     initTouchView : function()
14889     {
14890         this.renderTouchView();
14891         
14892         this.touchViewEl.on('scroll', function(){
14893             this.el.dom.scrollTop = 0;
14894         }, this);
14895         
14896         this.originalValue = this.getValue();
14897         
14898         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14899         
14900         this.inputEl().on("click", this.showTouchView, this);
14901         if (this.triggerEl) {
14902             this.triggerEl.on("click", this.showTouchView, this);
14903         }
14904         
14905         
14906         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14907         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14908         
14909         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14910         
14911         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14912         this.store.on('load', this.onTouchViewLoad, this);
14913         this.store.on('loadexception', this.onTouchViewLoadException, this);
14914         
14915         if(this.hiddenName){
14916             
14917             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14918             
14919             this.hiddenField.dom.value =
14920                 this.hiddenValue !== undefined ? this.hiddenValue :
14921                 this.value !== undefined ? this.value : '';
14922         
14923             this.el.dom.removeAttribute('name');
14924             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14925         }
14926         
14927         if(this.multiple){
14928             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14929             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14930         }
14931         
14932         if(this.removable && !this.multiple){
14933             var close = this.closeTriggerEl();
14934             if(close){
14935                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14936                 close.on('click', this.removeBtnClick, this, close);
14937             }
14938         }
14939         /*
14940          * fix the bug in Safari iOS8
14941          */
14942         this.inputEl().on("focus", function(e){
14943             document.activeElement.blur();
14944         }, this);
14945         
14946         return;
14947         
14948         
14949     },
14950     
14951     renderTouchView : function()
14952     {
14953         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14954         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14955         
14956         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14957         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14958         
14959         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14960         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14961         this.touchViewBodyEl.setStyle('overflow', 'auto');
14962         
14963         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14964         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14965         
14966         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14967         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14968         
14969     },
14970     
14971     showTouchView : function()
14972     {
14973         if(this.disabled){
14974             return;
14975         }
14976         
14977         this.touchViewHeaderEl.hide();
14978
14979         if(this.modalTitle.length){
14980             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14981             this.touchViewHeaderEl.show();
14982         }
14983
14984         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14985         this.touchViewEl.show();
14986
14987         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14988         
14989         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14990         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14991
14992         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14993
14994         if(this.modalTitle.length){
14995             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14996         }
14997         
14998         this.touchViewBodyEl.setHeight(bodyHeight);
14999
15000         if(this.animate){
15001             var _this = this;
15002             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15003         }else{
15004             this.touchViewEl.addClass('in');
15005         }
15006
15007         this.doTouchViewQuery();
15008         
15009     },
15010     
15011     hideTouchView : function()
15012     {
15013         this.touchViewEl.removeClass('in');
15014
15015         if(this.animate){
15016             var _this = this;
15017             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15018         }else{
15019             this.touchViewEl.setStyle('display', 'none');
15020         }
15021         
15022     },
15023     
15024     setTouchViewValue : function()
15025     {
15026         if(this.multiple){
15027             this.clearItem();
15028         
15029             var _this = this;
15030
15031             Roo.each(this.tickItems, function(o){
15032                 this.addItem(o);
15033             }, this);
15034         }
15035         
15036         this.hideTouchView();
15037     },
15038     
15039     doTouchViewQuery : function()
15040     {
15041         var qe = {
15042             query: '',
15043             forceAll: true,
15044             combo: this,
15045             cancel:false
15046         };
15047         
15048         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15049             return false;
15050         }
15051         
15052         if(!this.alwaysQuery || this.mode == 'local'){
15053             this.onTouchViewLoad();
15054             return;
15055         }
15056         
15057         this.store.load();
15058     },
15059     
15060     onTouchViewBeforeLoad : function(combo,opts)
15061     {
15062         return;
15063     },
15064
15065     // private
15066     onTouchViewLoad : function()
15067     {
15068         if(this.store.getCount() < 1){
15069             this.onTouchViewEmptyResults();
15070             return;
15071         }
15072         
15073         this.clearTouchView();
15074         
15075         var rawValue = this.getRawValue();
15076         
15077         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15078         
15079         this.tickItems = [];
15080         
15081         this.store.data.each(function(d, rowIndex){
15082             var row = this.touchViewListGroup.createChild(template);
15083             
15084             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15085                 row.addClass(d.data.cls);
15086             }
15087             
15088             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15089                 var cfg = {
15090                     data : d.data,
15091                     html : d.data[this.displayField]
15092                 };
15093                 
15094                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15095                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15096                 }
15097             }
15098             row.removeClass('selected');
15099             if(!this.multiple && this.valueField &&
15100                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15101             {
15102                 // radio buttons..
15103                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15104                 row.addClass('selected');
15105             }
15106             
15107             if(this.multiple && this.valueField &&
15108                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15109             {
15110                 
15111                 // checkboxes...
15112                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15113                 this.tickItems.push(d.data);
15114             }
15115             
15116             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15117             
15118         }, this);
15119         
15120         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15121         
15122         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15123
15124         if(this.modalTitle.length){
15125             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15126         }
15127
15128         var listHeight = this.touchViewListGroup.getHeight();
15129         
15130         var _this = this;
15131         
15132         if(firstChecked && listHeight > bodyHeight){
15133             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15134         }
15135         
15136     },
15137     
15138     onTouchViewLoadException : function()
15139     {
15140         this.hideTouchView();
15141     },
15142     
15143     onTouchViewEmptyResults : function()
15144     {
15145         this.clearTouchView();
15146         
15147         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15148         
15149         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15150         
15151     },
15152     
15153     clearTouchView : function()
15154     {
15155         this.touchViewListGroup.dom.innerHTML = '';
15156     },
15157     
15158     onTouchViewClick : function(e, el, o)
15159     {
15160         e.preventDefault();
15161         
15162         var row = o.row;
15163         var rowIndex = o.rowIndex;
15164         
15165         var r = this.store.getAt(rowIndex);
15166         
15167         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15168             
15169             if(!this.multiple){
15170                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15171                     c.dom.removeAttribute('checked');
15172                 }, this);
15173
15174                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15175
15176                 this.setFromData(r.data);
15177
15178                 var close = this.closeTriggerEl();
15179
15180                 if(close){
15181                     close.show();
15182                 }
15183
15184                 this.hideTouchView();
15185
15186                 this.fireEvent('select', this, r, rowIndex);
15187
15188                 return;
15189             }
15190
15191             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15192                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15193                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15194                 return;
15195             }
15196
15197             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15198             this.addItem(r.data);
15199             this.tickItems.push(r.data);
15200         }
15201     },
15202     
15203     getAutoCreateNativeIOS : function()
15204     {
15205         var cfg = {
15206             cls: 'form-group' //input-group,
15207         };
15208         
15209         var combobox =  {
15210             tag: 'select',
15211             cls : 'roo-ios-select'
15212         };
15213         
15214         if (this.name) {
15215             combobox.name = this.name;
15216         }
15217         
15218         if (this.disabled) {
15219             combobox.disabled = true;
15220         }
15221         
15222         var settings = this;
15223         
15224         ['xs','sm','md','lg'].map(function(size){
15225             if (settings[size]) {
15226                 cfg.cls += ' col-' + size + '-' + settings[size];
15227             }
15228         });
15229         
15230         cfg.cn = combobox;
15231         
15232         return cfg;
15233         
15234     },
15235     
15236     initIOSView : function()
15237     {
15238         this.store.on('load', this.onIOSViewLoad, this);
15239         
15240         return;
15241     },
15242     
15243     onIOSViewLoad : function()
15244     {
15245         if(this.store.getCount() < 1){
15246             return;
15247         }
15248         
15249         this.clearIOSView();
15250         
15251         if(this.allowBlank) {
15252             
15253             var default_text = '-- SELECT --';
15254             
15255             if(this.placeholder.length){
15256                 default_text = this.placeholder;
15257             }
15258             
15259             if(this.emptyTitle.length){
15260                 default_text += ' - ' + this.emptyTitle + ' -';
15261             }
15262             
15263             var opt = this.inputEl().createChild({
15264                 tag: 'option',
15265                 value : 0,
15266                 html : default_text
15267             });
15268             
15269             var o = {};
15270             o[this.valueField] = 0;
15271             o[this.displayField] = default_text;
15272             
15273             this.ios_options.push({
15274                 data : o,
15275                 el : opt
15276             });
15277             
15278         }
15279         
15280         this.store.data.each(function(d, rowIndex){
15281             
15282             var html = '';
15283             
15284             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15285                 html = d.data[this.displayField];
15286             }
15287             
15288             var value = '';
15289             
15290             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15291                 value = d.data[this.valueField];
15292             }
15293             
15294             var option = {
15295                 tag: 'option',
15296                 value : value,
15297                 html : html
15298             };
15299             
15300             if(this.value == d.data[this.valueField]){
15301                 option['selected'] = true;
15302             }
15303             
15304             var opt = this.inputEl().createChild(option);
15305             
15306             this.ios_options.push({
15307                 data : d.data,
15308                 el : opt
15309             });
15310             
15311         }, this);
15312         
15313         this.inputEl().on('change', function(){
15314            this.fireEvent('select', this);
15315         }, this);
15316         
15317     },
15318     
15319     clearIOSView: function()
15320     {
15321         this.inputEl().dom.innerHTML = '';
15322         
15323         this.ios_options = [];
15324     },
15325     
15326     setIOSValue: function(v)
15327     {
15328         this.value = v;
15329         
15330         if(!this.ios_options){
15331             return;
15332         }
15333         
15334         Roo.each(this.ios_options, function(opts){
15335            
15336            opts.el.dom.removeAttribute('selected');
15337            
15338            if(opts.data[this.valueField] != v){
15339                return;
15340            }
15341            
15342            opts.el.dom.setAttribute('selected', true);
15343            
15344         }, this);
15345     }
15346
15347     /** 
15348     * @cfg {Boolean} grow 
15349     * @hide 
15350     */
15351     /** 
15352     * @cfg {Number} growMin 
15353     * @hide 
15354     */
15355     /** 
15356     * @cfg {Number} growMax 
15357     * @hide 
15358     */
15359     /**
15360      * @hide
15361      * @method autoSize
15362      */
15363 });
15364
15365 Roo.apply(Roo.bootstrap.ComboBox,  {
15366     
15367     header : {
15368         tag: 'div',
15369         cls: 'modal-header',
15370         cn: [
15371             {
15372                 tag: 'h4',
15373                 cls: 'modal-title'
15374             }
15375         ]
15376     },
15377     
15378     body : {
15379         tag: 'div',
15380         cls: 'modal-body',
15381         cn: [
15382             {
15383                 tag: 'ul',
15384                 cls: 'list-group'
15385             }
15386         ]
15387     },
15388     
15389     listItemRadio : {
15390         tag: 'li',
15391         cls: 'list-group-item',
15392         cn: [
15393             {
15394                 tag: 'span',
15395                 cls: 'roo-combobox-list-group-item-value'
15396             },
15397             {
15398                 tag: 'div',
15399                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15400                 cn: [
15401                     {
15402                         tag: 'input',
15403                         type: 'radio'
15404                     },
15405                     {
15406                         tag: 'label'
15407                     }
15408                 ]
15409             }
15410         ]
15411     },
15412     
15413     listItemCheckbox : {
15414         tag: 'li',
15415         cls: 'list-group-item',
15416         cn: [
15417             {
15418                 tag: 'span',
15419                 cls: 'roo-combobox-list-group-item-value'
15420             },
15421             {
15422                 tag: 'div',
15423                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15424                 cn: [
15425                     {
15426                         tag: 'input',
15427                         type: 'checkbox'
15428                     },
15429                     {
15430                         tag: 'label'
15431                     }
15432                 ]
15433             }
15434         ]
15435     },
15436     
15437     emptyResult : {
15438         tag: 'div',
15439         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15440     },
15441     
15442     footer : {
15443         tag: 'div',
15444         cls: 'modal-footer',
15445         cn: [
15446             {
15447                 tag: 'div',
15448                 cls: 'row',
15449                 cn: [
15450                     {
15451                         tag: 'div',
15452                         cls: 'col-xs-6 text-left',
15453                         cn: {
15454                             tag: 'button',
15455                             cls: 'btn btn-danger roo-touch-view-cancel',
15456                             html: 'Cancel'
15457                         }
15458                     },
15459                     {
15460                         tag: 'div',
15461                         cls: 'col-xs-6 text-right',
15462                         cn: {
15463                             tag: 'button',
15464                             cls: 'btn btn-success roo-touch-view-ok',
15465                             html: 'OK'
15466                         }
15467                     }
15468                 ]
15469             }
15470         ]
15471         
15472     }
15473 });
15474
15475 Roo.apply(Roo.bootstrap.ComboBox,  {
15476     
15477     touchViewTemplate : {
15478         tag: 'div',
15479         cls: 'modal fade roo-combobox-touch-view',
15480         cn: [
15481             {
15482                 tag: 'div',
15483                 cls: 'modal-dialog',
15484                 style : 'position:fixed', // we have to fix position....
15485                 cn: [
15486                     {
15487                         tag: 'div',
15488                         cls: 'modal-content',
15489                         cn: [
15490                             Roo.bootstrap.ComboBox.header,
15491                             Roo.bootstrap.ComboBox.body,
15492                             Roo.bootstrap.ComboBox.footer
15493                         ]
15494                     }
15495                 ]
15496             }
15497         ]
15498     }
15499 });/*
15500  * Based on:
15501  * Ext JS Library 1.1.1
15502  * Copyright(c) 2006-2007, Ext JS, LLC.
15503  *
15504  * Originally Released Under LGPL - original licence link has changed is not relivant.
15505  *
15506  * Fork - LGPL
15507  * <script type="text/javascript">
15508  */
15509
15510 /**
15511  * @class Roo.View
15512  * @extends Roo.util.Observable
15513  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15514  * This class also supports single and multi selection modes. <br>
15515  * Create a data model bound view:
15516  <pre><code>
15517  var store = new Roo.data.Store(...);
15518
15519  var view = new Roo.View({
15520     el : "my-element",
15521     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15522  
15523     singleSelect: true,
15524     selectedClass: "ydataview-selected",
15525     store: store
15526  });
15527
15528  // listen for node click?
15529  view.on("click", function(vw, index, node, e){
15530  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15531  });
15532
15533  // load XML data
15534  dataModel.load("foobar.xml");
15535  </code></pre>
15536  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15537  * <br><br>
15538  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15539  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15540  * 
15541  * Note: old style constructor is still suported (container, template, config)
15542  * 
15543  * @constructor
15544  * Create a new View
15545  * @param {Object} config The config object
15546  * 
15547  */
15548 Roo.View = function(config, depreciated_tpl, depreciated_config){
15549     
15550     this.parent = false;
15551     
15552     if (typeof(depreciated_tpl) == 'undefined') {
15553         // new way.. - universal constructor.
15554         Roo.apply(this, config);
15555         this.el  = Roo.get(this.el);
15556     } else {
15557         // old format..
15558         this.el  = Roo.get(config);
15559         this.tpl = depreciated_tpl;
15560         Roo.apply(this, depreciated_config);
15561     }
15562     this.wrapEl  = this.el.wrap().wrap();
15563     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15564     
15565     
15566     if(typeof(this.tpl) == "string"){
15567         this.tpl = new Roo.Template(this.tpl);
15568     } else {
15569         // support xtype ctors..
15570         this.tpl = new Roo.factory(this.tpl, Roo);
15571     }
15572     
15573     
15574     this.tpl.compile();
15575     
15576     /** @private */
15577     this.addEvents({
15578         /**
15579          * @event beforeclick
15580          * Fires before a click is processed. Returns false to cancel the default action.
15581          * @param {Roo.View} this
15582          * @param {Number} index The index of the target node
15583          * @param {HTMLElement} node The target node
15584          * @param {Roo.EventObject} e The raw event object
15585          */
15586             "beforeclick" : true,
15587         /**
15588          * @event click
15589          * Fires when a template node is clicked.
15590          * @param {Roo.View} this
15591          * @param {Number} index The index of the target node
15592          * @param {HTMLElement} node The target node
15593          * @param {Roo.EventObject} e The raw event object
15594          */
15595             "click" : true,
15596         /**
15597          * @event dblclick
15598          * Fires when a template node is double clicked.
15599          * @param {Roo.View} this
15600          * @param {Number} index The index of the target node
15601          * @param {HTMLElement} node The target node
15602          * @param {Roo.EventObject} e The raw event object
15603          */
15604             "dblclick" : true,
15605         /**
15606          * @event contextmenu
15607          * Fires when a template node is right clicked.
15608          * @param {Roo.View} this
15609          * @param {Number} index The index of the target node
15610          * @param {HTMLElement} node The target node
15611          * @param {Roo.EventObject} e The raw event object
15612          */
15613             "contextmenu" : true,
15614         /**
15615          * @event selectionchange
15616          * Fires when the selected nodes change.
15617          * @param {Roo.View} this
15618          * @param {Array} selections Array of the selected nodes
15619          */
15620             "selectionchange" : true,
15621     
15622         /**
15623          * @event beforeselect
15624          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15625          * @param {Roo.View} this
15626          * @param {HTMLElement} node The node to be selected
15627          * @param {Array} selections Array of currently selected nodes
15628          */
15629             "beforeselect" : true,
15630         /**
15631          * @event preparedata
15632          * Fires on every row to render, to allow you to change the data.
15633          * @param {Roo.View} this
15634          * @param {Object} data to be rendered (change this)
15635          */
15636           "preparedata" : true
15637           
15638           
15639         });
15640
15641
15642
15643     this.el.on({
15644         "click": this.onClick,
15645         "dblclick": this.onDblClick,
15646         "contextmenu": this.onContextMenu,
15647         scope:this
15648     });
15649
15650     this.selections = [];
15651     this.nodes = [];
15652     this.cmp = new Roo.CompositeElementLite([]);
15653     if(this.store){
15654         this.store = Roo.factory(this.store, Roo.data);
15655         this.setStore(this.store, true);
15656     }
15657     
15658     if ( this.footer && this.footer.xtype) {
15659            
15660          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15661         
15662         this.footer.dataSource = this.store;
15663         this.footer.container = fctr;
15664         this.footer = Roo.factory(this.footer, Roo);
15665         fctr.insertFirst(this.el);
15666         
15667         // this is a bit insane - as the paging toolbar seems to detach the el..
15668 //        dom.parentNode.parentNode.parentNode
15669          // they get detached?
15670     }
15671     
15672     
15673     Roo.View.superclass.constructor.call(this);
15674     
15675     
15676 };
15677
15678 Roo.extend(Roo.View, Roo.util.Observable, {
15679     
15680      /**
15681      * @cfg {Roo.data.Store} store Data store to load data from.
15682      */
15683     store : false,
15684     
15685     /**
15686      * @cfg {String|Roo.Element} el The container element.
15687      */
15688     el : '',
15689     
15690     /**
15691      * @cfg {String|Roo.Template} tpl The template used by this View 
15692      */
15693     tpl : false,
15694     /**
15695      * @cfg {String} dataName the named area of the template to use as the data area
15696      *                          Works with domtemplates roo-name="name"
15697      */
15698     dataName: false,
15699     /**
15700      * @cfg {String} selectedClass The css class to add to selected nodes
15701      */
15702     selectedClass : "x-view-selected",
15703      /**
15704      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15705      */
15706     emptyText : "",
15707     
15708     /**
15709      * @cfg {String} text to display on mask (default Loading)
15710      */
15711     mask : false,
15712     /**
15713      * @cfg {Boolean} multiSelect Allow multiple selection
15714      */
15715     multiSelect : false,
15716     /**
15717      * @cfg {Boolean} singleSelect Allow single selection
15718      */
15719     singleSelect:  false,
15720     
15721     /**
15722      * @cfg {Boolean} toggleSelect - selecting 
15723      */
15724     toggleSelect : false,
15725     
15726     /**
15727      * @cfg {Boolean} tickable - selecting 
15728      */
15729     tickable : false,
15730     
15731     /**
15732      * Returns the element this view is bound to.
15733      * @return {Roo.Element}
15734      */
15735     getEl : function(){
15736         return this.wrapEl;
15737     },
15738     
15739     
15740
15741     /**
15742      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15743      */
15744     refresh : function(){
15745         //Roo.log('refresh');
15746         var t = this.tpl;
15747         
15748         // if we are using something like 'domtemplate', then
15749         // the what gets used is:
15750         // t.applySubtemplate(NAME, data, wrapping data..)
15751         // the outer template then get' applied with
15752         //     the store 'extra data'
15753         // and the body get's added to the
15754         //      roo-name="data" node?
15755         //      <span class='roo-tpl-{name}'></span> ?????
15756         
15757         
15758         
15759         this.clearSelections();
15760         this.el.update("");
15761         var html = [];
15762         var records = this.store.getRange();
15763         if(records.length < 1) {
15764             
15765             // is this valid??  = should it render a template??
15766             
15767             this.el.update(this.emptyText);
15768             return;
15769         }
15770         var el = this.el;
15771         if (this.dataName) {
15772             this.el.update(t.apply(this.store.meta)); //????
15773             el = this.el.child('.roo-tpl-' + this.dataName);
15774         }
15775         
15776         for(var i = 0, len = records.length; i < len; i++){
15777             var data = this.prepareData(records[i].data, i, records[i]);
15778             this.fireEvent("preparedata", this, data, i, records[i]);
15779             
15780             var d = Roo.apply({}, data);
15781             
15782             if(this.tickable){
15783                 Roo.apply(d, {'roo-id' : Roo.id()});
15784                 
15785                 var _this = this;
15786             
15787                 Roo.each(this.parent.item, function(item){
15788                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15789                         return;
15790                     }
15791                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15792                 });
15793             }
15794             
15795             html[html.length] = Roo.util.Format.trim(
15796                 this.dataName ?
15797                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15798                     t.apply(d)
15799             );
15800         }
15801         
15802         
15803         
15804         el.update(html.join(""));
15805         this.nodes = el.dom.childNodes;
15806         this.updateIndexes(0);
15807     },
15808     
15809
15810     /**
15811      * Function to override to reformat the data that is sent to
15812      * the template for each node.
15813      * DEPRICATED - use the preparedata event handler.
15814      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15815      * a JSON object for an UpdateManager bound view).
15816      */
15817     prepareData : function(data, index, record)
15818     {
15819         this.fireEvent("preparedata", this, data, index, record);
15820         return data;
15821     },
15822
15823     onUpdate : function(ds, record){
15824         // Roo.log('on update');   
15825         this.clearSelections();
15826         var index = this.store.indexOf(record);
15827         var n = this.nodes[index];
15828         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15829         n.parentNode.removeChild(n);
15830         this.updateIndexes(index, index);
15831     },
15832
15833     
15834     
15835 // --------- FIXME     
15836     onAdd : function(ds, records, index)
15837     {
15838         //Roo.log(['on Add', ds, records, index] );        
15839         this.clearSelections();
15840         if(this.nodes.length == 0){
15841             this.refresh();
15842             return;
15843         }
15844         var n = this.nodes[index];
15845         for(var i = 0, len = records.length; i < len; i++){
15846             var d = this.prepareData(records[i].data, i, records[i]);
15847             if(n){
15848                 this.tpl.insertBefore(n, d);
15849             }else{
15850                 
15851                 this.tpl.append(this.el, d);
15852             }
15853         }
15854         this.updateIndexes(index);
15855     },
15856
15857     onRemove : function(ds, record, index){
15858        // Roo.log('onRemove');
15859         this.clearSelections();
15860         var el = this.dataName  ?
15861             this.el.child('.roo-tpl-' + this.dataName) :
15862             this.el; 
15863         
15864         el.dom.removeChild(this.nodes[index]);
15865         this.updateIndexes(index);
15866     },
15867
15868     /**
15869      * Refresh an individual node.
15870      * @param {Number} index
15871      */
15872     refreshNode : function(index){
15873         this.onUpdate(this.store, this.store.getAt(index));
15874     },
15875
15876     updateIndexes : function(startIndex, endIndex){
15877         var ns = this.nodes;
15878         startIndex = startIndex || 0;
15879         endIndex = endIndex || ns.length - 1;
15880         for(var i = startIndex; i <= endIndex; i++){
15881             ns[i].nodeIndex = i;
15882         }
15883     },
15884
15885     /**
15886      * Changes the data store this view uses and refresh the view.
15887      * @param {Store} store
15888      */
15889     setStore : function(store, initial){
15890         if(!initial && this.store){
15891             this.store.un("datachanged", this.refresh);
15892             this.store.un("add", this.onAdd);
15893             this.store.un("remove", this.onRemove);
15894             this.store.un("update", this.onUpdate);
15895             this.store.un("clear", this.refresh);
15896             this.store.un("beforeload", this.onBeforeLoad);
15897             this.store.un("load", this.onLoad);
15898             this.store.un("loadexception", this.onLoad);
15899         }
15900         if(store){
15901           
15902             store.on("datachanged", this.refresh, this);
15903             store.on("add", this.onAdd, this);
15904             store.on("remove", this.onRemove, this);
15905             store.on("update", this.onUpdate, this);
15906             store.on("clear", this.refresh, this);
15907             store.on("beforeload", this.onBeforeLoad, this);
15908             store.on("load", this.onLoad, this);
15909             store.on("loadexception", this.onLoad, this);
15910         }
15911         
15912         if(store){
15913             this.refresh();
15914         }
15915     },
15916     /**
15917      * onbeforeLoad - masks the loading area.
15918      *
15919      */
15920     onBeforeLoad : function(store,opts)
15921     {
15922          //Roo.log('onBeforeLoad');   
15923         if (!opts.add) {
15924             this.el.update("");
15925         }
15926         this.el.mask(this.mask ? this.mask : "Loading" ); 
15927     },
15928     onLoad : function ()
15929     {
15930         this.el.unmask();
15931     },
15932     
15933
15934     /**
15935      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15936      * @param {HTMLElement} node
15937      * @return {HTMLElement} The template node
15938      */
15939     findItemFromChild : function(node){
15940         var el = this.dataName  ?
15941             this.el.child('.roo-tpl-' + this.dataName,true) :
15942             this.el.dom; 
15943         
15944         if(!node || node.parentNode == el){
15945                     return node;
15946             }
15947             var p = node.parentNode;
15948             while(p && p != el){
15949             if(p.parentNode == el){
15950                 return p;
15951             }
15952             p = p.parentNode;
15953         }
15954             return null;
15955     },
15956
15957     /** @ignore */
15958     onClick : function(e){
15959         var item = this.findItemFromChild(e.getTarget());
15960         if(item){
15961             var index = this.indexOf(item);
15962             if(this.onItemClick(item, index, e) !== false){
15963                 this.fireEvent("click", this, index, item, e);
15964             }
15965         }else{
15966             this.clearSelections();
15967         }
15968     },
15969
15970     /** @ignore */
15971     onContextMenu : function(e){
15972         var item = this.findItemFromChild(e.getTarget());
15973         if(item){
15974             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15975         }
15976     },
15977
15978     /** @ignore */
15979     onDblClick : function(e){
15980         var item = this.findItemFromChild(e.getTarget());
15981         if(item){
15982             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15983         }
15984     },
15985
15986     onItemClick : function(item, index, e)
15987     {
15988         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15989             return false;
15990         }
15991         if (this.toggleSelect) {
15992             var m = this.isSelected(item) ? 'unselect' : 'select';
15993             //Roo.log(m);
15994             var _t = this;
15995             _t[m](item, true, false);
15996             return true;
15997         }
15998         if(this.multiSelect || this.singleSelect){
15999             if(this.multiSelect && e.shiftKey && this.lastSelection){
16000                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16001             }else{
16002                 this.select(item, this.multiSelect && e.ctrlKey);
16003                 this.lastSelection = item;
16004             }
16005             
16006             if(!this.tickable){
16007                 e.preventDefault();
16008             }
16009             
16010         }
16011         return true;
16012     },
16013
16014     /**
16015      * Get the number of selected nodes.
16016      * @return {Number}
16017      */
16018     getSelectionCount : function(){
16019         return this.selections.length;
16020     },
16021
16022     /**
16023      * Get the currently selected nodes.
16024      * @return {Array} An array of HTMLElements
16025      */
16026     getSelectedNodes : function(){
16027         return this.selections;
16028     },
16029
16030     /**
16031      * Get the indexes of the selected nodes.
16032      * @return {Array}
16033      */
16034     getSelectedIndexes : function(){
16035         var indexes = [], s = this.selections;
16036         for(var i = 0, len = s.length; i < len; i++){
16037             indexes.push(s[i].nodeIndex);
16038         }
16039         return indexes;
16040     },
16041
16042     /**
16043      * Clear all selections
16044      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16045      */
16046     clearSelections : function(suppressEvent){
16047         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16048             this.cmp.elements = this.selections;
16049             this.cmp.removeClass(this.selectedClass);
16050             this.selections = [];
16051             if(!suppressEvent){
16052                 this.fireEvent("selectionchange", this, this.selections);
16053             }
16054         }
16055     },
16056
16057     /**
16058      * Returns true if the passed node is selected
16059      * @param {HTMLElement/Number} node The node or node index
16060      * @return {Boolean}
16061      */
16062     isSelected : function(node){
16063         var s = this.selections;
16064         if(s.length < 1){
16065             return false;
16066         }
16067         node = this.getNode(node);
16068         return s.indexOf(node) !== -1;
16069     },
16070
16071     /**
16072      * Selects nodes.
16073      * @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
16074      * @param {Boolean} keepExisting (optional) true to keep existing selections
16075      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16076      */
16077     select : function(nodeInfo, keepExisting, suppressEvent){
16078         if(nodeInfo instanceof Array){
16079             if(!keepExisting){
16080                 this.clearSelections(true);
16081             }
16082             for(var i = 0, len = nodeInfo.length; i < len; i++){
16083                 this.select(nodeInfo[i], true, true);
16084             }
16085             return;
16086         } 
16087         var node = this.getNode(nodeInfo);
16088         if(!node || this.isSelected(node)){
16089             return; // already selected.
16090         }
16091         if(!keepExisting){
16092             this.clearSelections(true);
16093         }
16094         
16095         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16096             Roo.fly(node).addClass(this.selectedClass);
16097             this.selections.push(node);
16098             if(!suppressEvent){
16099                 this.fireEvent("selectionchange", this, this.selections);
16100             }
16101         }
16102         
16103         
16104     },
16105       /**
16106      * Unselects nodes.
16107      * @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
16108      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16109      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16110      */
16111     unselect : function(nodeInfo, keepExisting, suppressEvent)
16112     {
16113         if(nodeInfo instanceof Array){
16114             Roo.each(this.selections, function(s) {
16115                 this.unselect(s, nodeInfo);
16116             }, this);
16117             return;
16118         }
16119         var node = this.getNode(nodeInfo);
16120         if(!node || !this.isSelected(node)){
16121             //Roo.log("not selected");
16122             return; // not selected.
16123         }
16124         // fireevent???
16125         var ns = [];
16126         Roo.each(this.selections, function(s) {
16127             if (s == node ) {
16128                 Roo.fly(node).removeClass(this.selectedClass);
16129
16130                 return;
16131             }
16132             ns.push(s);
16133         },this);
16134         
16135         this.selections= ns;
16136         this.fireEvent("selectionchange", this, this.selections);
16137     },
16138
16139     /**
16140      * Gets a template node.
16141      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16142      * @return {HTMLElement} The node or null if it wasn't found
16143      */
16144     getNode : function(nodeInfo){
16145         if(typeof nodeInfo == "string"){
16146             return document.getElementById(nodeInfo);
16147         }else if(typeof nodeInfo == "number"){
16148             return this.nodes[nodeInfo];
16149         }
16150         return nodeInfo;
16151     },
16152
16153     /**
16154      * Gets a range template nodes.
16155      * @param {Number} startIndex
16156      * @param {Number} endIndex
16157      * @return {Array} An array of nodes
16158      */
16159     getNodes : function(start, end){
16160         var ns = this.nodes;
16161         start = start || 0;
16162         end = typeof end == "undefined" ? ns.length - 1 : end;
16163         var nodes = [];
16164         if(start <= end){
16165             for(var i = start; i <= end; i++){
16166                 nodes.push(ns[i]);
16167             }
16168         } else{
16169             for(var i = start; i >= end; i--){
16170                 nodes.push(ns[i]);
16171             }
16172         }
16173         return nodes;
16174     },
16175
16176     /**
16177      * Finds the index of the passed node
16178      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16179      * @return {Number} The index of the node or -1
16180      */
16181     indexOf : function(node){
16182         node = this.getNode(node);
16183         if(typeof node.nodeIndex == "number"){
16184             return node.nodeIndex;
16185         }
16186         var ns = this.nodes;
16187         for(var i = 0, len = ns.length; i < len; i++){
16188             if(ns[i] == node){
16189                 return i;
16190             }
16191         }
16192         return -1;
16193     }
16194 });
16195 /*
16196  * - LGPL
16197  *
16198  * based on jquery fullcalendar
16199  * 
16200  */
16201
16202 Roo.bootstrap = Roo.bootstrap || {};
16203 /**
16204  * @class Roo.bootstrap.Calendar
16205  * @extends Roo.bootstrap.Component
16206  * Bootstrap Calendar class
16207  * @cfg {Boolean} loadMask (true|false) default false
16208  * @cfg {Object} header generate the user specific header of the calendar, default false
16209
16210  * @constructor
16211  * Create a new Container
16212  * @param {Object} config The config object
16213  */
16214
16215
16216
16217 Roo.bootstrap.Calendar = function(config){
16218     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16219      this.addEvents({
16220         /**
16221              * @event select
16222              * Fires when a date is selected
16223              * @param {DatePicker} this
16224              * @param {Date} date The selected date
16225              */
16226         'select': true,
16227         /**
16228              * @event monthchange
16229              * Fires when the displayed month changes 
16230              * @param {DatePicker} this
16231              * @param {Date} date The selected month
16232              */
16233         'monthchange': true,
16234         /**
16235              * @event evententer
16236              * Fires when mouse over an event
16237              * @param {Calendar} this
16238              * @param {event} Event
16239              */
16240         'evententer': true,
16241         /**
16242              * @event eventleave
16243              * Fires when the mouse leaves an
16244              * @param {Calendar} this
16245              * @param {event}
16246              */
16247         'eventleave': true,
16248         /**
16249              * @event eventclick
16250              * Fires when the mouse click an
16251              * @param {Calendar} this
16252              * @param {event}
16253              */
16254         'eventclick': true
16255         
16256     });
16257
16258 };
16259
16260 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16261     
16262      /**
16263      * @cfg {Number} startDay
16264      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16265      */
16266     startDay : 0,
16267     
16268     loadMask : false,
16269     
16270     header : false,
16271       
16272     getAutoCreate : function(){
16273         
16274         
16275         var fc_button = function(name, corner, style, content ) {
16276             return Roo.apply({},{
16277                 tag : 'span',
16278                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16279                          (corner.length ?
16280                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16281                             ''
16282                         ),
16283                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16284                 unselectable: 'on'
16285             });
16286         };
16287         
16288         var header = {};
16289         
16290         if(!this.header){
16291             header = {
16292                 tag : 'table',
16293                 cls : 'fc-header',
16294                 style : 'width:100%',
16295                 cn : [
16296                     {
16297                         tag: 'tr',
16298                         cn : [
16299                             {
16300                                 tag : 'td',
16301                                 cls : 'fc-header-left',
16302                                 cn : [
16303                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16304                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16305                                     { tag: 'span', cls: 'fc-header-space' },
16306                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16307
16308
16309                                 ]
16310                             },
16311
16312                             {
16313                                 tag : 'td',
16314                                 cls : 'fc-header-center',
16315                                 cn : [
16316                                     {
16317                                         tag: 'span',
16318                                         cls: 'fc-header-title',
16319                                         cn : {
16320                                             tag: 'H2',
16321                                             html : 'month / year'
16322                                         }
16323                                     }
16324
16325                                 ]
16326                             },
16327                             {
16328                                 tag : 'td',
16329                                 cls : 'fc-header-right',
16330                                 cn : [
16331                               /*      fc_button('month', 'left', '', 'month' ),
16332                                     fc_button('week', '', '', 'week' ),
16333                                     fc_button('day', 'right', '', 'day' )
16334                                 */    
16335
16336                                 ]
16337                             }
16338
16339                         ]
16340                     }
16341                 ]
16342             };
16343         }
16344         
16345         header = this.header;
16346         
16347        
16348         var cal_heads = function() {
16349             var ret = [];
16350             // fixme - handle this.
16351             
16352             for (var i =0; i < Date.dayNames.length; i++) {
16353                 var d = Date.dayNames[i];
16354                 ret.push({
16355                     tag: 'th',
16356                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16357                     html : d.substring(0,3)
16358                 });
16359                 
16360             }
16361             ret[0].cls += ' fc-first';
16362             ret[6].cls += ' fc-last';
16363             return ret;
16364         };
16365         var cal_cell = function(n) {
16366             return  {
16367                 tag: 'td',
16368                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16369                 cn : [
16370                     {
16371                         cn : [
16372                             {
16373                                 cls: 'fc-day-number',
16374                                 html: 'D'
16375                             },
16376                             {
16377                                 cls: 'fc-day-content',
16378                              
16379                                 cn : [
16380                                      {
16381                                         style: 'position: relative;' // height: 17px;
16382                                     }
16383                                 ]
16384                             }
16385                             
16386                             
16387                         ]
16388                     }
16389                 ]
16390                 
16391             }
16392         };
16393         var cal_rows = function() {
16394             
16395             var ret = [];
16396             for (var r = 0; r < 6; r++) {
16397                 var row= {
16398                     tag : 'tr',
16399                     cls : 'fc-week',
16400                     cn : []
16401                 };
16402                 
16403                 for (var i =0; i < Date.dayNames.length; i++) {
16404                     var d = Date.dayNames[i];
16405                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16406
16407                 }
16408                 row.cn[0].cls+=' fc-first';
16409                 row.cn[0].cn[0].style = 'min-height:90px';
16410                 row.cn[6].cls+=' fc-last';
16411                 ret.push(row);
16412                 
16413             }
16414             ret[0].cls += ' fc-first';
16415             ret[4].cls += ' fc-prev-last';
16416             ret[5].cls += ' fc-last';
16417             return ret;
16418             
16419         };
16420         
16421         var cal_table = {
16422             tag: 'table',
16423             cls: 'fc-border-separate',
16424             style : 'width:100%',
16425             cellspacing  : 0,
16426             cn : [
16427                 { 
16428                     tag: 'thead',
16429                     cn : [
16430                         { 
16431                             tag: 'tr',
16432                             cls : 'fc-first fc-last',
16433                             cn : cal_heads()
16434                         }
16435                     ]
16436                 },
16437                 { 
16438                     tag: 'tbody',
16439                     cn : cal_rows()
16440                 }
16441                   
16442             ]
16443         };
16444          
16445          var cfg = {
16446             cls : 'fc fc-ltr',
16447             cn : [
16448                 header,
16449                 {
16450                     cls : 'fc-content',
16451                     style : "position: relative;",
16452                     cn : [
16453                         {
16454                             cls : 'fc-view fc-view-month fc-grid',
16455                             style : 'position: relative',
16456                             unselectable : 'on',
16457                             cn : [
16458                                 {
16459                                     cls : 'fc-event-container',
16460                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16461                                 },
16462                                 cal_table
16463                             ]
16464                         }
16465                     ]
16466     
16467                 }
16468            ] 
16469             
16470         };
16471         
16472          
16473         
16474         return cfg;
16475     },
16476     
16477     
16478     initEvents : function()
16479     {
16480         if(!this.store){
16481             throw "can not find store for calendar";
16482         }
16483         
16484         var mark = {
16485             tag: "div",
16486             cls:"x-dlg-mask",
16487             style: "text-align:center",
16488             cn: [
16489                 {
16490                     tag: "div",
16491                     style: "background-color:white;width:50%;margin:250 auto",
16492                     cn: [
16493                         {
16494                             tag: "img",
16495                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16496                         },
16497                         {
16498                             tag: "span",
16499                             html: "Loading"
16500                         }
16501                         
16502                     ]
16503                 }
16504             ]
16505         };
16506         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16507         
16508         var size = this.el.select('.fc-content', true).first().getSize();
16509         this.maskEl.setSize(size.width, size.height);
16510         this.maskEl.enableDisplayMode("block");
16511         if(!this.loadMask){
16512             this.maskEl.hide();
16513         }
16514         
16515         this.store = Roo.factory(this.store, Roo.data);
16516         this.store.on('load', this.onLoad, this);
16517         this.store.on('beforeload', this.onBeforeLoad, this);
16518         
16519         this.resize();
16520         
16521         this.cells = this.el.select('.fc-day',true);
16522         //Roo.log(this.cells);
16523         this.textNodes = this.el.query('.fc-day-number');
16524         this.cells.addClassOnOver('fc-state-hover');
16525         
16526         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16527         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16528         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16529         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16530         
16531         this.on('monthchange', this.onMonthChange, this);
16532         
16533         this.update(new Date().clearTime());
16534     },
16535     
16536     resize : function() {
16537         var sz  = this.el.getSize();
16538         
16539         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16540         this.el.select('.fc-day-content div',true).setHeight(34);
16541     },
16542     
16543     
16544     // private
16545     showPrevMonth : function(e){
16546         this.update(this.activeDate.add("mo", -1));
16547     },
16548     showToday : function(e){
16549         this.update(new Date().clearTime());
16550     },
16551     // private
16552     showNextMonth : function(e){
16553         this.update(this.activeDate.add("mo", 1));
16554     },
16555
16556     // private
16557     showPrevYear : function(){
16558         this.update(this.activeDate.add("y", -1));
16559     },
16560
16561     // private
16562     showNextYear : function(){
16563         this.update(this.activeDate.add("y", 1));
16564     },
16565
16566     
16567    // private
16568     update : function(date)
16569     {
16570         var vd = this.activeDate;
16571         this.activeDate = date;
16572 //        if(vd && this.el){
16573 //            var t = date.getTime();
16574 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16575 //                Roo.log('using add remove');
16576 //                
16577 //                this.fireEvent('monthchange', this, date);
16578 //                
16579 //                this.cells.removeClass("fc-state-highlight");
16580 //                this.cells.each(function(c){
16581 //                   if(c.dateValue == t){
16582 //                       c.addClass("fc-state-highlight");
16583 //                       setTimeout(function(){
16584 //                            try{c.dom.firstChild.focus();}catch(e){}
16585 //                       }, 50);
16586 //                       return false;
16587 //                   }
16588 //                   return true;
16589 //                });
16590 //                return;
16591 //            }
16592 //        }
16593         
16594         var days = date.getDaysInMonth();
16595         
16596         var firstOfMonth = date.getFirstDateOfMonth();
16597         var startingPos = firstOfMonth.getDay()-this.startDay;
16598         
16599         if(startingPos < this.startDay){
16600             startingPos += 7;
16601         }
16602         
16603         var pm = date.add(Date.MONTH, -1);
16604         var prevStart = pm.getDaysInMonth()-startingPos;
16605 //        
16606         this.cells = this.el.select('.fc-day',true);
16607         this.textNodes = this.el.query('.fc-day-number');
16608         this.cells.addClassOnOver('fc-state-hover');
16609         
16610         var cells = this.cells.elements;
16611         var textEls = this.textNodes;
16612         
16613         Roo.each(cells, function(cell){
16614             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16615         });
16616         
16617         days += startingPos;
16618
16619         // convert everything to numbers so it's fast
16620         var day = 86400000;
16621         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16622         //Roo.log(d);
16623         //Roo.log(pm);
16624         //Roo.log(prevStart);
16625         
16626         var today = new Date().clearTime().getTime();
16627         var sel = date.clearTime().getTime();
16628         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16629         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16630         var ddMatch = this.disabledDatesRE;
16631         var ddText = this.disabledDatesText;
16632         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16633         var ddaysText = this.disabledDaysText;
16634         var format = this.format;
16635         
16636         var setCellClass = function(cal, cell){
16637             cell.row = 0;
16638             cell.events = [];
16639             cell.more = [];
16640             //Roo.log('set Cell Class');
16641             cell.title = "";
16642             var t = d.getTime();
16643             
16644             //Roo.log(d);
16645             
16646             cell.dateValue = t;
16647             if(t == today){
16648                 cell.className += " fc-today";
16649                 cell.className += " fc-state-highlight";
16650                 cell.title = cal.todayText;
16651             }
16652             if(t == sel){
16653                 // disable highlight in other month..
16654                 //cell.className += " fc-state-highlight";
16655                 
16656             }
16657             // disabling
16658             if(t < min) {
16659                 cell.className = " fc-state-disabled";
16660                 cell.title = cal.minText;
16661                 return;
16662             }
16663             if(t > max) {
16664                 cell.className = " fc-state-disabled";
16665                 cell.title = cal.maxText;
16666                 return;
16667             }
16668             if(ddays){
16669                 if(ddays.indexOf(d.getDay()) != -1){
16670                     cell.title = ddaysText;
16671                     cell.className = " fc-state-disabled";
16672                 }
16673             }
16674             if(ddMatch && format){
16675                 var fvalue = d.dateFormat(format);
16676                 if(ddMatch.test(fvalue)){
16677                     cell.title = ddText.replace("%0", fvalue);
16678                     cell.className = " fc-state-disabled";
16679                 }
16680             }
16681             
16682             if (!cell.initialClassName) {
16683                 cell.initialClassName = cell.dom.className;
16684             }
16685             
16686             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16687         };
16688
16689         var i = 0;
16690         
16691         for(; i < startingPos; i++) {
16692             textEls[i].innerHTML = (++prevStart);
16693             d.setDate(d.getDate()+1);
16694             
16695             cells[i].className = "fc-past fc-other-month";
16696             setCellClass(this, cells[i]);
16697         }
16698         
16699         var intDay = 0;
16700         
16701         for(; i < days; i++){
16702             intDay = i - startingPos + 1;
16703             textEls[i].innerHTML = (intDay);
16704             d.setDate(d.getDate()+1);
16705             
16706             cells[i].className = ''; // "x-date-active";
16707             setCellClass(this, cells[i]);
16708         }
16709         var extraDays = 0;
16710         
16711         for(; i < 42; i++) {
16712             textEls[i].innerHTML = (++extraDays);
16713             d.setDate(d.getDate()+1);
16714             
16715             cells[i].className = "fc-future fc-other-month";
16716             setCellClass(this, cells[i]);
16717         }
16718         
16719         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16720         
16721         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16722         
16723         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16724         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16725         
16726         if(totalRows != 6){
16727             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16728             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16729         }
16730         
16731         this.fireEvent('monthchange', this, date);
16732         
16733         
16734         /*
16735         if(!this.internalRender){
16736             var main = this.el.dom.firstChild;
16737             var w = main.offsetWidth;
16738             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16739             Roo.fly(main).setWidth(w);
16740             this.internalRender = true;
16741             // opera does not respect the auto grow header center column
16742             // then, after it gets a width opera refuses to recalculate
16743             // without a second pass
16744             if(Roo.isOpera && !this.secondPass){
16745                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16746                 this.secondPass = true;
16747                 this.update.defer(10, this, [date]);
16748             }
16749         }
16750         */
16751         
16752     },
16753     
16754     findCell : function(dt) {
16755         dt = dt.clearTime().getTime();
16756         var ret = false;
16757         this.cells.each(function(c){
16758             //Roo.log("check " +c.dateValue + '?=' + dt);
16759             if(c.dateValue == dt){
16760                 ret = c;
16761                 return false;
16762             }
16763             return true;
16764         });
16765         
16766         return ret;
16767     },
16768     
16769     findCells : function(ev) {
16770         var s = ev.start.clone().clearTime().getTime();
16771        // Roo.log(s);
16772         var e= ev.end.clone().clearTime().getTime();
16773        // Roo.log(e);
16774         var ret = [];
16775         this.cells.each(function(c){
16776              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16777             
16778             if(c.dateValue > e){
16779                 return ;
16780             }
16781             if(c.dateValue < s){
16782                 return ;
16783             }
16784             ret.push(c);
16785         });
16786         
16787         return ret;    
16788     },
16789     
16790 //    findBestRow: function(cells)
16791 //    {
16792 //        var ret = 0;
16793 //        
16794 //        for (var i =0 ; i < cells.length;i++) {
16795 //            ret  = Math.max(cells[i].rows || 0,ret);
16796 //        }
16797 //        return ret;
16798 //        
16799 //    },
16800     
16801     
16802     addItem : function(ev)
16803     {
16804         // look for vertical location slot in
16805         var cells = this.findCells(ev);
16806         
16807 //        ev.row = this.findBestRow(cells);
16808         
16809         // work out the location.
16810         
16811         var crow = false;
16812         var rows = [];
16813         for(var i =0; i < cells.length; i++) {
16814             
16815             cells[i].row = cells[0].row;
16816             
16817             if(i == 0){
16818                 cells[i].row = cells[i].row + 1;
16819             }
16820             
16821             if (!crow) {
16822                 crow = {
16823                     start : cells[i],
16824                     end :  cells[i]
16825                 };
16826                 continue;
16827             }
16828             if (crow.start.getY() == cells[i].getY()) {
16829                 // on same row.
16830                 crow.end = cells[i];
16831                 continue;
16832             }
16833             // different row.
16834             rows.push(crow);
16835             crow = {
16836                 start: cells[i],
16837                 end : cells[i]
16838             };
16839             
16840         }
16841         
16842         rows.push(crow);
16843         ev.els = [];
16844         ev.rows = rows;
16845         ev.cells = cells;
16846         
16847         cells[0].events.push(ev);
16848         
16849         this.calevents.push(ev);
16850     },
16851     
16852     clearEvents: function() {
16853         
16854         if(!this.calevents){
16855             return;
16856         }
16857         
16858         Roo.each(this.cells.elements, function(c){
16859             c.row = 0;
16860             c.events = [];
16861             c.more = [];
16862         });
16863         
16864         Roo.each(this.calevents, function(e) {
16865             Roo.each(e.els, function(el) {
16866                 el.un('mouseenter' ,this.onEventEnter, this);
16867                 el.un('mouseleave' ,this.onEventLeave, this);
16868                 el.remove();
16869             },this);
16870         },this);
16871         
16872         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16873             e.remove();
16874         });
16875         
16876     },
16877     
16878     renderEvents: function()
16879     {   
16880         var _this = this;
16881         
16882         this.cells.each(function(c) {
16883             
16884             if(c.row < 5){
16885                 return;
16886             }
16887             
16888             var ev = c.events;
16889             
16890             var r = 4;
16891             if(c.row != c.events.length){
16892                 r = 4 - (4 - (c.row - c.events.length));
16893             }
16894             
16895             c.events = ev.slice(0, r);
16896             c.more = ev.slice(r);
16897             
16898             if(c.more.length && c.more.length == 1){
16899                 c.events.push(c.more.pop());
16900             }
16901             
16902             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16903             
16904         });
16905             
16906         this.cells.each(function(c) {
16907             
16908             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16909             
16910             
16911             for (var e = 0; e < c.events.length; e++){
16912                 var ev = c.events[e];
16913                 var rows = ev.rows;
16914                 
16915                 for(var i = 0; i < rows.length; i++) {
16916                 
16917                     // how many rows should it span..
16918
16919                     var  cfg = {
16920                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16921                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16922
16923                         unselectable : "on",
16924                         cn : [
16925                             {
16926                                 cls: 'fc-event-inner',
16927                                 cn : [
16928     //                                {
16929     //                                  tag:'span',
16930     //                                  cls: 'fc-event-time',
16931     //                                  html : cells.length > 1 ? '' : ev.time
16932     //                                },
16933                                     {
16934                                       tag:'span',
16935                                       cls: 'fc-event-title',
16936                                       html : String.format('{0}', ev.title)
16937                                     }
16938
16939
16940                                 ]
16941                             },
16942                             {
16943                                 cls: 'ui-resizable-handle ui-resizable-e',
16944                                 html : '&nbsp;&nbsp;&nbsp'
16945                             }
16946
16947                         ]
16948                     };
16949
16950                     if (i == 0) {
16951                         cfg.cls += ' fc-event-start';
16952                     }
16953                     if ((i+1) == rows.length) {
16954                         cfg.cls += ' fc-event-end';
16955                     }
16956
16957                     var ctr = _this.el.select('.fc-event-container',true).first();
16958                     var cg = ctr.createChild(cfg);
16959
16960                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16961                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16962
16963                     var r = (c.more.length) ? 1 : 0;
16964                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16965                     cg.setWidth(ebox.right - sbox.x -2);
16966
16967                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16968                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16969                     cg.on('click', _this.onEventClick, _this, ev);
16970
16971                     ev.els.push(cg);
16972                     
16973                 }
16974                 
16975             }
16976             
16977             
16978             if(c.more.length){
16979                 var  cfg = {
16980                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16981                     style : 'position: absolute',
16982                     unselectable : "on",
16983                     cn : [
16984                         {
16985                             cls: 'fc-event-inner',
16986                             cn : [
16987                                 {
16988                                   tag:'span',
16989                                   cls: 'fc-event-title',
16990                                   html : 'More'
16991                                 }
16992
16993
16994                             ]
16995                         },
16996                         {
16997                             cls: 'ui-resizable-handle ui-resizable-e',
16998                             html : '&nbsp;&nbsp;&nbsp'
16999                         }
17000
17001                     ]
17002                 };
17003
17004                 var ctr = _this.el.select('.fc-event-container',true).first();
17005                 var cg = ctr.createChild(cfg);
17006
17007                 var sbox = c.select('.fc-day-content',true).first().getBox();
17008                 var ebox = c.select('.fc-day-content',true).first().getBox();
17009                 //Roo.log(cg);
17010                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17011                 cg.setWidth(ebox.right - sbox.x -2);
17012
17013                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17014                 
17015             }
17016             
17017         });
17018         
17019         
17020         
17021     },
17022     
17023     onEventEnter: function (e, el,event,d) {
17024         this.fireEvent('evententer', this, el, event);
17025     },
17026     
17027     onEventLeave: function (e, el,event,d) {
17028         this.fireEvent('eventleave', this, el, event);
17029     },
17030     
17031     onEventClick: function (e, el,event,d) {
17032         this.fireEvent('eventclick', this, el, event);
17033     },
17034     
17035     onMonthChange: function () {
17036         this.store.load();
17037     },
17038     
17039     onMoreEventClick: function(e, el, more)
17040     {
17041         var _this = this;
17042         
17043         this.calpopover.placement = 'right';
17044         this.calpopover.setTitle('More');
17045         
17046         this.calpopover.setContent('');
17047         
17048         var ctr = this.calpopover.el.select('.popover-content', true).first();
17049         
17050         Roo.each(more, function(m){
17051             var cfg = {
17052                 cls : 'fc-event-hori fc-event-draggable',
17053                 html : m.title
17054             };
17055             var cg = ctr.createChild(cfg);
17056             
17057             cg.on('click', _this.onEventClick, _this, m);
17058         });
17059         
17060         this.calpopover.show(el);
17061         
17062         
17063     },
17064     
17065     onLoad: function () 
17066     {   
17067         this.calevents = [];
17068         var cal = this;
17069         
17070         if(this.store.getCount() > 0){
17071             this.store.data.each(function(d){
17072                cal.addItem({
17073                     id : d.data.id,
17074                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17075                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17076                     time : d.data.start_time,
17077                     title : d.data.title,
17078                     description : d.data.description,
17079                     venue : d.data.venue
17080                 });
17081             });
17082         }
17083         
17084         this.renderEvents();
17085         
17086         if(this.calevents.length && this.loadMask){
17087             this.maskEl.hide();
17088         }
17089     },
17090     
17091     onBeforeLoad: function()
17092     {
17093         this.clearEvents();
17094         if(this.loadMask){
17095             this.maskEl.show();
17096         }
17097     }
17098 });
17099
17100  
17101  /*
17102  * - LGPL
17103  *
17104  * element
17105  * 
17106  */
17107
17108 /**
17109  * @class Roo.bootstrap.Popover
17110  * @extends Roo.bootstrap.Component
17111  * Bootstrap Popover class
17112  * @cfg {String} html contents of the popover   (or false to use children..)
17113  * @cfg {String} title of popover (or false to hide)
17114  * @cfg {String} placement how it is placed
17115  * @cfg {String} trigger click || hover (or false to trigger manually)
17116  * @cfg {String} over what (parent or false to trigger manually.)
17117  * @cfg {Number} delay - delay before showing
17118  
17119  * @constructor
17120  * Create a new Popover
17121  * @param {Object} config The config object
17122  */
17123
17124 Roo.bootstrap.Popover = function(config){
17125     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17126     
17127     this.addEvents({
17128         // raw events
17129          /**
17130          * @event show
17131          * After the popover show
17132          * 
17133          * @param {Roo.bootstrap.Popover} this
17134          */
17135         "show" : true,
17136         /**
17137          * @event hide
17138          * After the popover hide
17139          * 
17140          * @param {Roo.bootstrap.Popover} this
17141          */
17142         "hide" : true
17143     });
17144 };
17145
17146 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17147     
17148     title: 'Fill in a title',
17149     html: false,
17150     
17151     placement : 'right',
17152     trigger : 'hover', // hover
17153     
17154     delay : 0,
17155     
17156     over: 'parent',
17157     
17158     can_build_overlaid : false,
17159     
17160     getChildContainer : function()
17161     {
17162         return this.el.select('.popover-content',true).first();
17163     },
17164     
17165     getAutoCreate : function(){
17166          
17167         var cfg = {
17168            cls : 'popover roo-dynamic',
17169            style: 'display:block',
17170            cn : [
17171                 {
17172                     cls : 'arrow'
17173                 },
17174                 {
17175                     cls : 'popover-inner',
17176                     cn : [
17177                         {
17178                             tag: 'h3',
17179                             cls: 'popover-title',
17180                             html : this.title
17181                         },
17182                         {
17183                             cls : 'popover-content',
17184                             html : this.html
17185                         }
17186                     ]
17187                     
17188                 }
17189            ]
17190         };
17191         
17192         return cfg;
17193     },
17194     setTitle: function(str)
17195     {
17196         this.title = str;
17197         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17198     },
17199     setContent: function(str)
17200     {
17201         this.html = str;
17202         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17203     },
17204     // as it get's added to the bottom of the page.
17205     onRender : function(ct, position)
17206     {
17207         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17208         if(!this.el){
17209             var cfg = Roo.apply({},  this.getAutoCreate());
17210             cfg.id = Roo.id();
17211             
17212             if (this.cls) {
17213                 cfg.cls += ' ' + this.cls;
17214             }
17215             if (this.style) {
17216                 cfg.style = this.style;
17217             }
17218             //Roo.log("adding to ");
17219             this.el = Roo.get(document.body).createChild(cfg, position);
17220 //            Roo.log(this.el);
17221         }
17222         this.initEvents();
17223     },
17224     
17225     initEvents : function()
17226     {
17227         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17228         this.el.enableDisplayMode('block');
17229         this.el.hide();
17230         if (this.over === false) {
17231             return; 
17232         }
17233         if (this.triggers === false) {
17234             return;
17235         }
17236         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17237         var triggers = this.trigger ? this.trigger.split(' ') : [];
17238         Roo.each(triggers, function(trigger) {
17239         
17240             if (trigger == 'click') {
17241                 on_el.on('click', this.toggle, this);
17242             } else if (trigger != 'manual') {
17243                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17244                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17245       
17246                 on_el.on(eventIn  ,this.enter, this);
17247                 on_el.on(eventOut, this.leave, this);
17248             }
17249         }, this);
17250         
17251     },
17252     
17253     
17254     // private
17255     timeout : null,
17256     hoverState : null,
17257     
17258     toggle : function () {
17259         this.hoverState == 'in' ? this.leave() : this.enter();
17260     },
17261     
17262     enter : function () {
17263         
17264         clearTimeout(this.timeout);
17265     
17266         this.hoverState = 'in';
17267     
17268         if (!this.delay || !this.delay.show) {
17269             this.show();
17270             return;
17271         }
17272         var _t = this;
17273         this.timeout = setTimeout(function () {
17274             if (_t.hoverState == 'in') {
17275                 _t.show();
17276             }
17277         }, this.delay.show)
17278     },
17279     
17280     leave : function() {
17281         clearTimeout(this.timeout);
17282     
17283         this.hoverState = 'out';
17284     
17285         if (!this.delay || !this.delay.hide) {
17286             this.hide();
17287             return;
17288         }
17289         var _t = this;
17290         this.timeout = setTimeout(function () {
17291             if (_t.hoverState == 'out') {
17292                 _t.hide();
17293             }
17294         }, this.delay.hide)
17295     },
17296     
17297     show : function (on_el)
17298     {
17299         if (!on_el) {
17300             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17301         }
17302         
17303         // set content.
17304         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17305         if (this.html !== false) {
17306             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17307         }
17308         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17309         if (!this.title.length) {
17310             this.el.select('.popover-title',true).hide();
17311         }
17312         
17313         var placement = typeof this.placement == 'function' ?
17314             this.placement.call(this, this.el, on_el) :
17315             this.placement;
17316             
17317         var autoToken = /\s?auto?\s?/i;
17318         var autoPlace = autoToken.test(placement);
17319         if (autoPlace) {
17320             placement = placement.replace(autoToken, '') || 'top';
17321         }
17322         
17323         //this.el.detach()
17324         //this.el.setXY([0,0]);
17325         this.el.show();
17326         this.el.dom.style.display='block';
17327         this.el.addClass(placement);
17328         
17329         //this.el.appendTo(on_el);
17330         
17331         var p = this.getPosition();
17332         var box = this.el.getBox();
17333         
17334         if (autoPlace) {
17335             // fixme..
17336         }
17337         var align = Roo.bootstrap.Popover.alignment[placement];
17338         
17339 //        Roo.log(align);
17340         this.el.alignTo(on_el, align[0],align[1]);
17341         //var arrow = this.el.select('.arrow',true).first();
17342         //arrow.set(align[2], 
17343         
17344         this.el.addClass('in');
17345         
17346         
17347         if (this.el.hasClass('fade')) {
17348             // fade it?
17349         }
17350         
17351         this.hoverState = 'in';
17352         
17353         this.fireEvent('show', this);
17354         
17355     },
17356     hide : function()
17357     {
17358         this.el.setXY([0,0]);
17359         this.el.removeClass('in');
17360         this.el.hide();
17361         this.hoverState = null;
17362         
17363         this.fireEvent('hide', this);
17364     }
17365     
17366 });
17367
17368 Roo.bootstrap.Popover.alignment = {
17369     'left' : ['r-l', [-10,0], 'right'],
17370     'right' : ['l-r', [10,0], 'left'],
17371     'bottom' : ['t-b', [0,10], 'top'],
17372     'top' : [ 'b-t', [0,-10], 'bottom']
17373 };
17374
17375  /*
17376  * - LGPL
17377  *
17378  * Progress
17379  * 
17380  */
17381
17382 /**
17383  * @class Roo.bootstrap.Progress
17384  * @extends Roo.bootstrap.Component
17385  * Bootstrap Progress class
17386  * @cfg {Boolean} striped striped of the progress bar
17387  * @cfg {Boolean} active animated of the progress bar
17388  * 
17389  * 
17390  * @constructor
17391  * Create a new Progress
17392  * @param {Object} config The config object
17393  */
17394
17395 Roo.bootstrap.Progress = function(config){
17396     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17397 };
17398
17399 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17400     
17401     striped : false,
17402     active: false,
17403     
17404     getAutoCreate : function(){
17405         var cfg = {
17406             tag: 'div',
17407             cls: 'progress'
17408         };
17409         
17410         
17411         if(this.striped){
17412             cfg.cls += ' progress-striped';
17413         }
17414       
17415         if(this.active){
17416             cfg.cls += ' active';
17417         }
17418         
17419         
17420         return cfg;
17421     }
17422    
17423 });
17424
17425  
17426
17427  /*
17428  * - LGPL
17429  *
17430  * ProgressBar
17431  * 
17432  */
17433
17434 /**
17435  * @class Roo.bootstrap.ProgressBar
17436  * @extends Roo.bootstrap.Component
17437  * Bootstrap ProgressBar class
17438  * @cfg {Number} aria_valuenow aria-value now
17439  * @cfg {Number} aria_valuemin aria-value min
17440  * @cfg {Number} aria_valuemax aria-value max
17441  * @cfg {String} label label for the progress bar
17442  * @cfg {String} panel (success | info | warning | danger )
17443  * @cfg {String} role role of the progress bar
17444  * @cfg {String} sr_only text
17445  * 
17446  * 
17447  * @constructor
17448  * Create a new ProgressBar
17449  * @param {Object} config The config object
17450  */
17451
17452 Roo.bootstrap.ProgressBar = function(config){
17453     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17454 };
17455
17456 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17457     
17458     aria_valuenow : 0,
17459     aria_valuemin : 0,
17460     aria_valuemax : 100,
17461     label : false,
17462     panel : false,
17463     role : false,
17464     sr_only: false,
17465     
17466     getAutoCreate : function()
17467     {
17468         
17469         var cfg = {
17470             tag: 'div',
17471             cls: 'progress-bar',
17472             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17473         };
17474         
17475         if(this.sr_only){
17476             cfg.cn = {
17477                 tag: 'span',
17478                 cls: 'sr-only',
17479                 html: this.sr_only
17480             }
17481         }
17482         
17483         if(this.role){
17484             cfg.role = this.role;
17485         }
17486         
17487         if(this.aria_valuenow){
17488             cfg['aria-valuenow'] = this.aria_valuenow;
17489         }
17490         
17491         if(this.aria_valuemin){
17492             cfg['aria-valuemin'] = this.aria_valuemin;
17493         }
17494         
17495         if(this.aria_valuemax){
17496             cfg['aria-valuemax'] = this.aria_valuemax;
17497         }
17498         
17499         if(this.label && !this.sr_only){
17500             cfg.html = this.label;
17501         }
17502         
17503         if(this.panel){
17504             cfg.cls += ' progress-bar-' + this.panel;
17505         }
17506         
17507         return cfg;
17508     },
17509     
17510     update : function(aria_valuenow)
17511     {
17512         this.aria_valuenow = aria_valuenow;
17513         
17514         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17515     }
17516    
17517 });
17518
17519  
17520
17521  /*
17522  * - LGPL
17523  *
17524  * column
17525  * 
17526  */
17527
17528 /**
17529  * @class Roo.bootstrap.TabGroup
17530  * @extends Roo.bootstrap.Column
17531  * Bootstrap Column class
17532  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17533  * @cfg {Boolean} carousel true to make the group behave like a carousel
17534  * @cfg {Boolean} bullets show bullets for the panels
17535  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17536  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17537  * @cfg {Boolean} showarrow (true|false) show arrow default true
17538  * 
17539  * @constructor
17540  * Create a new TabGroup
17541  * @param {Object} config The config object
17542  */
17543
17544 Roo.bootstrap.TabGroup = function(config){
17545     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17546     if (!this.navId) {
17547         this.navId = Roo.id();
17548     }
17549     this.tabs = [];
17550     Roo.bootstrap.TabGroup.register(this);
17551     
17552 };
17553
17554 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17555     
17556     carousel : false,
17557     transition : false,
17558     bullets : 0,
17559     timer : 0,
17560     autoslide : false,
17561     slideFn : false,
17562     slideOnTouch : false,
17563     showarrow : true,
17564     
17565     getAutoCreate : function()
17566     {
17567         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17568         
17569         cfg.cls += ' tab-content';
17570         
17571         if (this.carousel) {
17572             cfg.cls += ' carousel slide';
17573             
17574             cfg.cn = [{
17575                cls : 'carousel-inner',
17576                cn : []
17577             }];
17578         
17579             if(this.bullets  && !Roo.isTouch){
17580                 
17581                 var bullets = {
17582                     cls : 'carousel-bullets',
17583                     cn : []
17584                 };
17585                
17586                 if(this.bullets_cls){
17587                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17588                 }
17589                 
17590                 bullets.cn.push({
17591                     cls : 'clear'
17592                 });
17593                 
17594                 cfg.cn[0].cn.push(bullets);
17595             }
17596             
17597             if(this.showarrow){
17598                 cfg.cn[0].cn.push({
17599                     tag : 'div',
17600                     class : 'carousel-arrow',
17601                     cn : [
17602                         {
17603                             tag : 'div',
17604                             class : 'carousel-prev',
17605                             cn : [
17606                                 {
17607                                     tag : 'i',
17608                                     class : 'fa fa-chevron-left'
17609                                 }
17610                             ]
17611                         },
17612                         {
17613                             tag : 'div',
17614                             class : 'carousel-next',
17615                             cn : [
17616                                 {
17617                                     tag : 'i',
17618                                     class : 'fa fa-chevron-right'
17619                                 }
17620                             ]
17621                         }
17622                     ]
17623                 });
17624             }
17625             
17626         }
17627         
17628         return cfg;
17629     },
17630     
17631     initEvents:  function()
17632     {
17633 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17634 //            this.el.on("touchstart", this.onTouchStart, this);
17635 //        }
17636         
17637         if(this.autoslide){
17638             var _this = this;
17639             
17640             this.slideFn = window.setInterval(function() {
17641                 _this.showPanelNext();
17642             }, this.timer);
17643         }
17644         
17645         if(this.showarrow){
17646             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17647             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17648         }
17649         
17650         
17651     },
17652     
17653 //    onTouchStart : function(e, el, o)
17654 //    {
17655 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17656 //            return;
17657 //        }
17658 //        
17659 //        this.showPanelNext();
17660 //    },
17661     
17662     
17663     getChildContainer : function()
17664     {
17665         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17666     },
17667     
17668     /**
17669     * register a Navigation item
17670     * @param {Roo.bootstrap.NavItem} the navitem to add
17671     */
17672     register : function(item)
17673     {
17674         this.tabs.push( item);
17675         item.navId = this.navId; // not really needed..
17676         this.addBullet();
17677     
17678     },
17679     
17680     getActivePanel : function()
17681     {
17682         var r = false;
17683         Roo.each(this.tabs, function(t) {
17684             if (t.active) {
17685                 r = t;
17686                 return false;
17687             }
17688             return null;
17689         });
17690         return r;
17691         
17692     },
17693     getPanelByName : function(n)
17694     {
17695         var r = false;
17696         Roo.each(this.tabs, function(t) {
17697             if (t.tabId == n) {
17698                 r = t;
17699                 return false;
17700             }
17701             return null;
17702         });
17703         return r;
17704     },
17705     indexOfPanel : function(p)
17706     {
17707         var r = false;
17708         Roo.each(this.tabs, function(t,i) {
17709             if (t.tabId == p.tabId) {
17710                 r = i;
17711                 return false;
17712             }
17713             return null;
17714         });
17715         return r;
17716     },
17717     /**
17718      * show a specific panel
17719      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17720      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17721      */
17722     showPanel : function (pan)
17723     {
17724         if(this.transition || typeof(pan) == 'undefined'){
17725             Roo.log("waiting for the transitionend");
17726             return;
17727         }
17728         
17729         if (typeof(pan) == 'number') {
17730             pan = this.tabs[pan];
17731         }
17732         
17733         if (typeof(pan) == 'string') {
17734             pan = this.getPanelByName(pan);
17735         }
17736         
17737         var cur = this.getActivePanel();
17738         
17739         if(!pan || !cur){
17740             Roo.log('pan or acitve pan is undefined');
17741             return false;
17742         }
17743         
17744         if (pan.tabId == this.getActivePanel().tabId) {
17745             return true;
17746         }
17747         
17748         if (false === cur.fireEvent('beforedeactivate')) {
17749             return false;
17750         }
17751         
17752         if(this.bullets > 0 && !Roo.isTouch){
17753             this.setActiveBullet(this.indexOfPanel(pan));
17754         }
17755         
17756         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17757             
17758             this.transition = true;
17759             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17760             var lr = dir == 'next' ? 'left' : 'right';
17761             pan.el.addClass(dir); // or prev
17762             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17763             cur.el.addClass(lr); // or right
17764             pan.el.addClass(lr);
17765             
17766             var _this = this;
17767             cur.el.on('transitionend', function() {
17768                 Roo.log("trans end?");
17769                 
17770                 pan.el.removeClass([lr,dir]);
17771                 pan.setActive(true);
17772                 
17773                 cur.el.removeClass([lr]);
17774                 cur.setActive(false);
17775                 
17776                 _this.transition = false;
17777                 
17778             }, this, { single:  true } );
17779             
17780             return true;
17781         }
17782         
17783         cur.setActive(false);
17784         pan.setActive(true);
17785         
17786         return true;
17787         
17788     },
17789     showPanelNext : function()
17790     {
17791         var i = this.indexOfPanel(this.getActivePanel());
17792         
17793         if (i >= this.tabs.length - 1 && !this.autoslide) {
17794             return;
17795         }
17796         
17797         if (i >= this.tabs.length - 1 && this.autoslide) {
17798             i = -1;
17799         }
17800         
17801         this.showPanel(this.tabs[i+1]);
17802     },
17803     
17804     showPanelPrev : function()
17805     {
17806         var i = this.indexOfPanel(this.getActivePanel());
17807         
17808         if (i  < 1 && !this.autoslide) {
17809             return;
17810         }
17811         
17812         if (i < 1 && this.autoslide) {
17813             i = this.tabs.length;
17814         }
17815         
17816         this.showPanel(this.tabs[i-1]);
17817     },
17818     
17819     
17820     addBullet: function()
17821     {
17822         if(!this.bullets || Roo.isTouch){
17823             return;
17824         }
17825         var ctr = this.el.select('.carousel-bullets',true).first();
17826         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17827         var bullet = ctr.createChild({
17828             cls : 'bullet bullet-' + i
17829         },ctr.dom.lastChild);
17830         
17831         
17832         var _this = this;
17833         
17834         bullet.on('click', (function(e, el, o, ii, t){
17835
17836             e.preventDefault();
17837
17838             this.showPanel(ii);
17839
17840             if(this.autoslide && this.slideFn){
17841                 clearInterval(this.slideFn);
17842                 this.slideFn = window.setInterval(function() {
17843                     _this.showPanelNext();
17844                 }, this.timer);
17845             }
17846
17847         }).createDelegate(this, [i, bullet], true));
17848                 
17849         
17850     },
17851      
17852     setActiveBullet : function(i)
17853     {
17854         if(Roo.isTouch){
17855             return;
17856         }
17857         
17858         Roo.each(this.el.select('.bullet', true).elements, function(el){
17859             el.removeClass('selected');
17860         });
17861
17862         var bullet = this.el.select('.bullet-' + i, true).first();
17863         
17864         if(!bullet){
17865             return;
17866         }
17867         
17868         bullet.addClass('selected');
17869     }
17870     
17871     
17872   
17873 });
17874
17875  
17876
17877  
17878  
17879 Roo.apply(Roo.bootstrap.TabGroup, {
17880     
17881     groups: {},
17882      /**
17883     * register a Navigation Group
17884     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17885     */
17886     register : function(navgrp)
17887     {
17888         this.groups[navgrp.navId] = navgrp;
17889         
17890     },
17891     /**
17892     * fetch a Navigation Group based on the navigation ID
17893     * if one does not exist , it will get created.
17894     * @param {string} the navgroup to add
17895     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17896     */
17897     get: function(navId) {
17898         if (typeof(this.groups[navId]) == 'undefined') {
17899             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17900         }
17901         return this.groups[navId] ;
17902     }
17903     
17904     
17905     
17906 });
17907
17908  /*
17909  * - LGPL
17910  *
17911  * TabPanel
17912  * 
17913  */
17914
17915 /**
17916  * @class Roo.bootstrap.TabPanel
17917  * @extends Roo.bootstrap.Component
17918  * Bootstrap TabPanel class
17919  * @cfg {Boolean} active panel active
17920  * @cfg {String} html panel content
17921  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17922  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17923  * @cfg {String} href click to link..
17924  * 
17925  * 
17926  * @constructor
17927  * Create a new TabPanel
17928  * @param {Object} config The config object
17929  */
17930
17931 Roo.bootstrap.TabPanel = function(config){
17932     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17933     this.addEvents({
17934         /**
17935              * @event changed
17936              * Fires when the active status changes
17937              * @param {Roo.bootstrap.TabPanel} this
17938              * @param {Boolean} state the new state
17939             
17940          */
17941         'changed': true,
17942         /**
17943              * @event beforedeactivate
17944              * Fires before a tab is de-activated - can be used to do validation on a form.
17945              * @param {Roo.bootstrap.TabPanel} this
17946              * @return {Boolean} false if there is an error
17947             
17948          */
17949         'beforedeactivate': true
17950      });
17951     
17952     this.tabId = this.tabId || Roo.id();
17953   
17954 };
17955
17956 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17957     
17958     active: false,
17959     html: false,
17960     tabId: false,
17961     navId : false,
17962     href : '',
17963     
17964     getAutoCreate : function(){
17965         var cfg = {
17966             tag: 'div',
17967             // item is needed for carousel - not sure if it has any effect otherwise
17968             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17969             html: this.html || ''
17970         };
17971         
17972         if(this.active){
17973             cfg.cls += ' active';
17974         }
17975         
17976         if(this.tabId){
17977             cfg.tabId = this.tabId;
17978         }
17979         
17980         
17981         return cfg;
17982     },
17983     
17984     initEvents:  function()
17985     {
17986         var p = this.parent();
17987         
17988         this.navId = this.navId || p.navId;
17989         
17990         if (typeof(this.navId) != 'undefined') {
17991             // not really needed.. but just in case.. parent should be a NavGroup.
17992             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17993             
17994             tg.register(this);
17995             
17996             var i = tg.tabs.length - 1;
17997             
17998             if(this.active && tg.bullets > 0 && i < tg.bullets){
17999                 tg.setActiveBullet(i);
18000             }
18001         }
18002         
18003         this.el.on('click', this.onClick, this);
18004         
18005         if(Roo.isTouch){
18006             this.el.on("touchstart", this.onTouchStart, this);
18007             this.el.on("touchmove", this.onTouchMove, this);
18008             this.el.on("touchend", this.onTouchEnd, this);
18009         }
18010         
18011     },
18012     
18013     onRender : function(ct, position)
18014     {
18015         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18016     },
18017     
18018     setActive : function(state)
18019     {
18020         Roo.log("panel - set active " + this.tabId + "=" + state);
18021         
18022         this.active = state;
18023         if (!state) {
18024             this.el.removeClass('active');
18025             
18026         } else  if (!this.el.hasClass('active')) {
18027             this.el.addClass('active');
18028         }
18029         
18030         this.fireEvent('changed', this, state);
18031     },
18032     
18033     onClick : function(e)
18034     {
18035         e.preventDefault();
18036         
18037         if(!this.href.length){
18038             return;
18039         }
18040         
18041         window.location.href = this.href;
18042     },
18043     
18044     startX : 0,
18045     startY : 0,
18046     endX : 0,
18047     endY : 0,
18048     swiping : false,
18049     
18050     onTouchStart : function(e)
18051     {
18052         this.swiping = false;
18053         
18054         this.startX = e.browserEvent.touches[0].clientX;
18055         this.startY = e.browserEvent.touches[0].clientY;
18056     },
18057     
18058     onTouchMove : function(e)
18059     {
18060         this.swiping = true;
18061         
18062         this.endX = e.browserEvent.touches[0].clientX;
18063         this.endY = e.browserEvent.touches[0].clientY;
18064     },
18065     
18066     onTouchEnd : function(e)
18067     {
18068         if(!this.swiping){
18069             this.onClick(e);
18070             return;
18071         }
18072         
18073         var tabGroup = this.parent();
18074         
18075         if(this.endX > this.startX){ // swiping right
18076             tabGroup.showPanelPrev();
18077             return;
18078         }
18079         
18080         if(this.startX > this.endX){ // swiping left
18081             tabGroup.showPanelNext();
18082             return;
18083         }
18084     }
18085     
18086     
18087 });
18088  
18089
18090  
18091
18092  /*
18093  * - LGPL
18094  *
18095  * DateField
18096  * 
18097  */
18098
18099 /**
18100  * @class Roo.bootstrap.DateField
18101  * @extends Roo.bootstrap.Input
18102  * Bootstrap DateField class
18103  * @cfg {Number} weekStart default 0
18104  * @cfg {String} viewMode default empty, (months|years)
18105  * @cfg {String} minViewMode default empty, (months|years)
18106  * @cfg {Number} startDate default -Infinity
18107  * @cfg {Number} endDate default Infinity
18108  * @cfg {Boolean} todayHighlight default false
18109  * @cfg {Boolean} todayBtn default false
18110  * @cfg {Boolean} calendarWeeks default false
18111  * @cfg {Object} daysOfWeekDisabled default empty
18112  * @cfg {Boolean} singleMode default false (true | false)
18113  * 
18114  * @cfg {Boolean} keyboardNavigation default true
18115  * @cfg {String} language default en
18116  * 
18117  * @constructor
18118  * Create a new DateField
18119  * @param {Object} config The config object
18120  */
18121
18122 Roo.bootstrap.DateField = function(config){
18123     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18124      this.addEvents({
18125             /**
18126              * @event show
18127              * Fires when this field show.
18128              * @param {Roo.bootstrap.DateField} this
18129              * @param {Mixed} date The date value
18130              */
18131             show : true,
18132             /**
18133              * @event show
18134              * Fires when this field hide.
18135              * @param {Roo.bootstrap.DateField} this
18136              * @param {Mixed} date The date value
18137              */
18138             hide : true,
18139             /**
18140              * @event select
18141              * Fires when select a date.
18142              * @param {Roo.bootstrap.DateField} this
18143              * @param {Mixed} date The date value
18144              */
18145             select : true,
18146             /**
18147              * @event beforeselect
18148              * Fires when before select a date.
18149              * @param {Roo.bootstrap.DateField} this
18150              * @param {Mixed} date The date value
18151              */
18152             beforeselect : true
18153         });
18154 };
18155
18156 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18157     
18158     /**
18159      * @cfg {String} format
18160      * The default date format string which can be overriden for localization support.  The format must be
18161      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18162      */
18163     format : "m/d/y",
18164     /**
18165      * @cfg {String} altFormats
18166      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18167      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18168      */
18169     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18170     
18171     weekStart : 0,
18172     
18173     viewMode : '',
18174     
18175     minViewMode : '',
18176     
18177     todayHighlight : false,
18178     
18179     todayBtn: false,
18180     
18181     language: 'en',
18182     
18183     keyboardNavigation: true,
18184     
18185     calendarWeeks: false,
18186     
18187     startDate: -Infinity,
18188     
18189     endDate: Infinity,
18190     
18191     daysOfWeekDisabled: [],
18192     
18193     _events: [],
18194     
18195     singleMode : false,
18196     
18197     UTCDate: function()
18198     {
18199         return new Date(Date.UTC.apply(Date, arguments));
18200     },
18201     
18202     UTCToday: function()
18203     {
18204         var today = new Date();
18205         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18206     },
18207     
18208     getDate: function() {
18209             var d = this.getUTCDate();
18210             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18211     },
18212     
18213     getUTCDate: function() {
18214             return this.date;
18215     },
18216     
18217     setDate: function(d) {
18218             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18219     },
18220     
18221     setUTCDate: function(d) {
18222             this.date = d;
18223             this.setValue(this.formatDate(this.date));
18224     },
18225         
18226     onRender: function(ct, position)
18227     {
18228         
18229         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18230         
18231         this.language = this.language || 'en';
18232         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18233         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18234         
18235         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18236         this.format = this.format || 'm/d/y';
18237         this.isInline = false;
18238         this.isInput = true;
18239         this.component = this.el.select('.add-on', true).first() || false;
18240         this.component = (this.component && this.component.length === 0) ? false : this.component;
18241         this.hasInput = this.component && this.inputEl().length;
18242         
18243         if (typeof(this.minViewMode === 'string')) {
18244             switch (this.minViewMode) {
18245                 case 'months':
18246                     this.minViewMode = 1;
18247                     break;
18248                 case 'years':
18249                     this.minViewMode = 2;
18250                     break;
18251                 default:
18252                     this.minViewMode = 0;
18253                     break;
18254             }
18255         }
18256         
18257         if (typeof(this.viewMode === 'string')) {
18258             switch (this.viewMode) {
18259                 case 'months':
18260                     this.viewMode = 1;
18261                     break;
18262                 case 'years':
18263                     this.viewMode = 2;
18264                     break;
18265                 default:
18266                     this.viewMode = 0;
18267                     break;
18268             }
18269         }
18270                 
18271         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18272         
18273 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18274         
18275         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18276         
18277         this.picker().on('mousedown', this.onMousedown, this);
18278         this.picker().on('click', this.onClick, this);
18279         
18280         this.picker().addClass('datepicker-dropdown');
18281         
18282         this.startViewMode = this.viewMode;
18283         
18284         if(this.singleMode){
18285             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18286                 v.setVisibilityMode(Roo.Element.DISPLAY);
18287                 v.hide();
18288             });
18289             
18290             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18291                 v.setStyle('width', '189px');
18292             });
18293         }
18294         
18295         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18296             if(!this.calendarWeeks){
18297                 v.remove();
18298                 return;
18299             }
18300             
18301             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18302             v.attr('colspan', function(i, val){
18303                 return parseInt(val) + 1;
18304             });
18305         });
18306                         
18307         
18308         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18309         
18310         this.setStartDate(this.startDate);
18311         this.setEndDate(this.endDate);
18312         
18313         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18314         
18315         this.fillDow();
18316         this.fillMonths();
18317         this.update();
18318         this.showMode();
18319         
18320         if(this.isInline) {
18321             this.show();
18322         }
18323     },
18324     
18325     picker : function()
18326     {
18327         return this.pickerEl;
18328 //        return this.el.select('.datepicker', true).first();
18329     },
18330     
18331     fillDow: function()
18332     {
18333         var dowCnt = this.weekStart;
18334         
18335         var dow = {
18336             tag: 'tr',
18337             cn: [
18338                 
18339             ]
18340         };
18341         
18342         if(this.calendarWeeks){
18343             dow.cn.push({
18344                 tag: 'th',
18345                 cls: 'cw',
18346                 html: '&nbsp;'
18347             })
18348         }
18349         
18350         while (dowCnt < this.weekStart + 7) {
18351             dow.cn.push({
18352                 tag: 'th',
18353                 cls: 'dow',
18354                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18355             });
18356         }
18357         
18358         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18359     },
18360     
18361     fillMonths: function()
18362     {    
18363         var i = 0;
18364         var months = this.picker().select('>.datepicker-months td', true).first();
18365         
18366         months.dom.innerHTML = '';
18367         
18368         while (i < 12) {
18369             var month = {
18370                 tag: 'span',
18371                 cls: 'month',
18372                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18373             };
18374             
18375             months.createChild(month);
18376         }
18377         
18378     },
18379     
18380     update: function()
18381     {
18382         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;
18383         
18384         if (this.date < this.startDate) {
18385             this.viewDate = new Date(this.startDate);
18386         } else if (this.date > this.endDate) {
18387             this.viewDate = new Date(this.endDate);
18388         } else {
18389             this.viewDate = new Date(this.date);
18390         }
18391         
18392         this.fill();
18393     },
18394     
18395     fill: function() 
18396     {
18397         var d = new Date(this.viewDate),
18398                 year = d.getUTCFullYear(),
18399                 month = d.getUTCMonth(),
18400                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18401                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18402                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18403                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18404                 currentDate = this.date && this.date.valueOf(),
18405                 today = this.UTCToday();
18406         
18407         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18408         
18409 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18410         
18411 //        this.picker.select('>tfoot th.today').
18412 //                                              .text(dates[this.language].today)
18413 //                                              .toggle(this.todayBtn !== false);
18414     
18415         this.updateNavArrows();
18416         this.fillMonths();
18417                                                 
18418         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18419         
18420         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18421          
18422         prevMonth.setUTCDate(day);
18423         
18424         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18425         
18426         var nextMonth = new Date(prevMonth);
18427         
18428         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18429         
18430         nextMonth = nextMonth.valueOf();
18431         
18432         var fillMonths = false;
18433         
18434         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18435         
18436         while(prevMonth.valueOf() < nextMonth) {
18437             var clsName = '';
18438             
18439             if (prevMonth.getUTCDay() === this.weekStart) {
18440                 if(fillMonths){
18441                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18442                 }
18443                     
18444                 fillMonths = {
18445                     tag: 'tr',
18446                     cn: []
18447                 };
18448                 
18449                 if(this.calendarWeeks){
18450                     // ISO 8601: First week contains first thursday.
18451                     // ISO also states week starts on Monday, but we can be more abstract here.
18452                     var
18453                     // Start of current week: based on weekstart/current date
18454                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18455                     // Thursday of this week
18456                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18457                     // First Thursday of year, year from thursday
18458                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18459                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18460                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18461                     
18462                     fillMonths.cn.push({
18463                         tag: 'td',
18464                         cls: 'cw',
18465                         html: calWeek
18466                     });
18467                 }
18468             }
18469             
18470             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18471                 clsName += ' old';
18472             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18473                 clsName += ' new';
18474             }
18475             if (this.todayHighlight &&
18476                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18477                 prevMonth.getUTCMonth() == today.getMonth() &&
18478                 prevMonth.getUTCDate() == today.getDate()) {
18479                 clsName += ' today';
18480             }
18481             
18482             if (currentDate && prevMonth.valueOf() === currentDate) {
18483                 clsName += ' active';
18484             }
18485             
18486             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18487                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18488                     clsName += ' disabled';
18489             }
18490             
18491             fillMonths.cn.push({
18492                 tag: 'td',
18493                 cls: 'day ' + clsName,
18494                 html: prevMonth.getDate()
18495             });
18496             
18497             prevMonth.setDate(prevMonth.getDate()+1);
18498         }
18499           
18500         var currentYear = this.date && this.date.getUTCFullYear();
18501         var currentMonth = this.date && this.date.getUTCMonth();
18502         
18503         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18504         
18505         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18506             v.removeClass('active');
18507             
18508             if(currentYear === year && k === currentMonth){
18509                 v.addClass('active');
18510             }
18511             
18512             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18513                 v.addClass('disabled');
18514             }
18515             
18516         });
18517         
18518         
18519         year = parseInt(year/10, 10) * 10;
18520         
18521         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18522         
18523         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18524         
18525         year -= 1;
18526         for (var i = -1; i < 11; i++) {
18527             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18528                 tag: 'span',
18529                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18530                 html: year
18531             });
18532             
18533             year += 1;
18534         }
18535     },
18536     
18537     showMode: function(dir) 
18538     {
18539         if (dir) {
18540             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18541         }
18542         
18543         Roo.each(this.picker().select('>div',true).elements, function(v){
18544             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18545             v.hide();
18546         });
18547         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18548     },
18549     
18550     place: function()
18551     {
18552         if(this.isInline) {
18553             return;
18554         }
18555         
18556         this.picker().removeClass(['bottom', 'top']);
18557         
18558         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18559             /*
18560              * place to the top of element!
18561              *
18562              */
18563             
18564             this.picker().addClass('top');
18565             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18566             
18567             return;
18568         }
18569         
18570         this.picker().addClass('bottom');
18571         
18572         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18573     },
18574     
18575     parseDate : function(value)
18576     {
18577         if(!value || value instanceof Date){
18578             return value;
18579         }
18580         var v = Date.parseDate(value, this.format);
18581         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18582             v = Date.parseDate(value, 'Y-m-d');
18583         }
18584         if(!v && this.altFormats){
18585             if(!this.altFormatsArray){
18586                 this.altFormatsArray = this.altFormats.split("|");
18587             }
18588             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18589                 v = Date.parseDate(value, this.altFormatsArray[i]);
18590             }
18591         }
18592         return v;
18593     },
18594     
18595     formatDate : function(date, fmt)
18596     {   
18597         return (!date || !(date instanceof Date)) ?
18598         date : date.dateFormat(fmt || this.format);
18599     },
18600     
18601     onFocus : function()
18602     {
18603         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18604         this.show();
18605     },
18606     
18607     onBlur : function()
18608     {
18609         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18610         
18611         var d = this.inputEl().getValue();
18612         
18613         this.setValue(d);
18614                 
18615         this.hide();
18616     },
18617     
18618     show : function()
18619     {
18620         this.picker().show();
18621         this.update();
18622         this.place();
18623         
18624         this.fireEvent('show', this, this.date);
18625     },
18626     
18627     hide : function()
18628     {
18629         if(this.isInline) {
18630             return;
18631         }
18632         this.picker().hide();
18633         this.viewMode = this.startViewMode;
18634         this.showMode();
18635         
18636         this.fireEvent('hide', this, this.date);
18637         
18638     },
18639     
18640     onMousedown: function(e)
18641     {
18642         e.stopPropagation();
18643         e.preventDefault();
18644     },
18645     
18646     keyup: function(e)
18647     {
18648         Roo.bootstrap.DateField.superclass.keyup.call(this);
18649         this.update();
18650     },
18651
18652     setValue: function(v)
18653     {
18654         if(this.fireEvent('beforeselect', this, v) !== false){
18655             var d = new Date(this.parseDate(v) ).clearTime();
18656         
18657             if(isNaN(d.getTime())){
18658                 this.date = this.viewDate = '';
18659                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18660                 return;
18661             }
18662
18663             v = this.formatDate(d);
18664
18665             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18666
18667             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18668
18669             this.update();
18670
18671             this.fireEvent('select', this, this.date);
18672         }
18673     },
18674     
18675     getValue: function()
18676     {
18677         return this.formatDate(this.date);
18678     },
18679     
18680     fireKey: function(e)
18681     {
18682         if (!this.picker().isVisible()){
18683             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18684                 this.show();
18685             }
18686             return;
18687         }
18688         
18689         var dateChanged = false,
18690         dir, day, month,
18691         newDate, newViewDate;
18692         
18693         switch(e.keyCode){
18694             case 27: // escape
18695                 this.hide();
18696                 e.preventDefault();
18697                 break;
18698             case 37: // left
18699             case 39: // right
18700                 if (!this.keyboardNavigation) {
18701                     break;
18702                 }
18703                 dir = e.keyCode == 37 ? -1 : 1;
18704                 
18705                 if (e.ctrlKey){
18706                     newDate = this.moveYear(this.date, dir);
18707                     newViewDate = this.moveYear(this.viewDate, dir);
18708                 } else if (e.shiftKey){
18709                     newDate = this.moveMonth(this.date, dir);
18710                     newViewDate = this.moveMonth(this.viewDate, dir);
18711                 } else {
18712                     newDate = new Date(this.date);
18713                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18714                     newViewDate = new Date(this.viewDate);
18715                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18716                 }
18717                 if (this.dateWithinRange(newDate)){
18718                     this.date = newDate;
18719                     this.viewDate = newViewDate;
18720                     this.setValue(this.formatDate(this.date));
18721 //                    this.update();
18722                     e.preventDefault();
18723                     dateChanged = true;
18724                 }
18725                 break;
18726             case 38: // up
18727             case 40: // down
18728                 if (!this.keyboardNavigation) {
18729                     break;
18730                 }
18731                 dir = e.keyCode == 38 ? -1 : 1;
18732                 if (e.ctrlKey){
18733                     newDate = this.moveYear(this.date, dir);
18734                     newViewDate = this.moveYear(this.viewDate, dir);
18735                 } else if (e.shiftKey){
18736                     newDate = this.moveMonth(this.date, dir);
18737                     newViewDate = this.moveMonth(this.viewDate, dir);
18738                 } else {
18739                     newDate = new Date(this.date);
18740                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18741                     newViewDate = new Date(this.viewDate);
18742                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18743                 }
18744                 if (this.dateWithinRange(newDate)){
18745                     this.date = newDate;
18746                     this.viewDate = newViewDate;
18747                     this.setValue(this.formatDate(this.date));
18748 //                    this.update();
18749                     e.preventDefault();
18750                     dateChanged = true;
18751                 }
18752                 break;
18753             case 13: // enter
18754                 this.setValue(this.formatDate(this.date));
18755                 this.hide();
18756                 e.preventDefault();
18757                 break;
18758             case 9: // tab
18759                 this.setValue(this.formatDate(this.date));
18760                 this.hide();
18761                 break;
18762             case 16: // shift
18763             case 17: // ctrl
18764             case 18: // alt
18765                 break;
18766             default :
18767                 this.hide();
18768                 
18769         }
18770     },
18771     
18772     
18773     onClick: function(e) 
18774     {
18775         e.stopPropagation();
18776         e.preventDefault();
18777         
18778         var target = e.getTarget();
18779         
18780         if(target.nodeName.toLowerCase() === 'i'){
18781             target = Roo.get(target).dom.parentNode;
18782         }
18783         
18784         var nodeName = target.nodeName;
18785         var className = target.className;
18786         var html = target.innerHTML;
18787         //Roo.log(nodeName);
18788         
18789         switch(nodeName.toLowerCase()) {
18790             case 'th':
18791                 switch(className) {
18792                     case 'switch':
18793                         this.showMode(1);
18794                         break;
18795                     case 'prev':
18796                     case 'next':
18797                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18798                         switch(this.viewMode){
18799                                 case 0:
18800                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18801                                         break;
18802                                 case 1:
18803                                 case 2:
18804                                         this.viewDate = this.moveYear(this.viewDate, dir);
18805                                         break;
18806                         }
18807                         this.fill();
18808                         break;
18809                     case 'today':
18810                         var date = new Date();
18811                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18812 //                        this.fill()
18813                         this.setValue(this.formatDate(this.date));
18814                         
18815                         this.hide();
18816                         break;
18817                 }
18818                 break;
18819             case 'span':
18820                 if (className.indexOf('disabled') < 0) {
18821                     this.viewDate.setUTCDate(1);
18822                     if (className.indexOf('month') > -1) {
18823                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18824                     } else {
18825                         var year = parseInt(html, 10) || 0;
18826                         this.viewDate.setUTCFullYear(year);
18827                         
18828                     }
18829                     
18830                     if(this.singleMode){
18831                         this.setValue(this.formatDate(this.viewDate));
18832                         this.hide();
18833                         return;
18834                     }
18835                     
18836                     this.showMode(-1);
18837                     this.fill();
18838                 }
18839                 break;
18840                 
18841             case 'td':
18842                 //Roo.log(className);
18843                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18844                     var day = parseInt(html, 10) || 1;
18845                     var year = this.viewDate.getUTCFullYear(),
18846                         month = this.viewDate.getUTCMonth();
18847
18848                     if (className.indexOf('old') > -1) {
18849                         if(month === 0 ){
18850                             month = 11;
18851                             year -= 1;
18852                         }else{
18853                             month -= 1;
18854                         }
18855                     } else if (className.indexOf('new') > -1) {
18856                         if (month == 11) {
18857                             month = 0;
18858                             year += 1;
18859                         } else {
18860                             month += 1;
18861                         }
18862                     }
18863                     //Roo.log([year,month,day]);
18864                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18865                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18866 //                    this.fill();
18867                     //Roo.log(this.formatDate(this.date));
18868                     this.setValue(this.formatDate(this.date));
18869                     this.hide();
18870                 }
18871                 break;
18872         }
18873     },
18874     
18875     setStartDate: function(startDate)
18876     {
18877         this.startDate = startDate || -Infinity;
18878         if (this.startDate !== -Infinity) {
18879             this.startDate = this.parseDate(this.startDate);
18880         }
18881         this.update();
18882         this.updateNavArrows();
18883     },
18884
18885     setEndDate: function(endDate)
18886     {
18887         this.endDate = endDate || Infinity;
18888         if (this.endDate !== Infinity) {
18889             this.endDate = this.parseDate(this.endDate);
18890         }
18891         this.update();
18892         this.updateNavArrows();
18893     },
18894     
18895     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18896     {
18897         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18898         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18899             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18900         }
18901         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18902             return parseInt(d, 10);
18903         });
18904         this.update();
18905         this.updateNavArrows();
18906     },
18907     
18908     updateNavArrows: function() 
18909     {
18910         if(this.singleMode){
18911             return;
18912         }
18913         
18914         var d = new Date(this.viewDate),
18915         year = d.getUTCFullYear(),
18916         month = d.getUTCMonth();
18917         
18918         Roo.each(this.picker().select('.prev', true).elements, function(v){
18919             v.show();
18920             switch (this.viewMode) {
18921                 case 0:
18922
18923                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18924                         v.hide();
18925                     }
18926                     break;
18927                 case 1:
18928                 case 2:
18929                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18930                         v.hide();
18931                     }
18932                     break;
18933             }
18934         });
18935         
18936         Roo.each(this.picker().select('.next', true).elements, function(v){
18937             v.show();
18938             switch (this.viewMode) {
18939                 case 0:
18940
18941                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18942                         v.hide();
18943                     }
18944                     break;
18945                 case 1:
18946                 case 2:
18947                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18948                         v.hide();
18949                     }
18950                     break;
18951             }
18952         })
18953     },
18954     
18955     moveMonth: function(date, dir)
18956     {
18957         if (!dir) {
18958             return date;
18959         }
18960         var new_date = new Date(date.valueOf()),
18961         day = new_date.getUTCDate(),
18962         month = new_date.getUTCMonth(),
18963         mag = Math.abs(dir),
18964         new_month, test;
18965         dir = dir > 0 ? 1 : -1;
18966         if (mag == 1){
18967             test = dir == -1
18968             // If going back one month, make sure month is not current month
18969             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18970             ? function(){
18971                 return new_date.getUTCMonth() == month;
18972             }
18973             // If going forward one month, make sure month is as expected
18974             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18975             : function(){
18976                 return new_date.getUTCMonth() != new_month;
18977             };
18978             new_month = month + dir;
18979             new_date.setUTCMonth(new_month);
18980             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18981             if (new_month < 0 || new_month > 11) {
18982                 new_month = (new_month + 12) % 12;
18983             }
18984         } else {
18985             // For magnitudes >1, move one month at a time...
18986             for (var i=0; i<mag; i++) {
18987                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18988                 new_date = this.moveMonth(new_date, dir);
18989             }
18990             // ...then reset the day, keeping it in the new month
18991             new_month = new_date.getUTCMonth();
18992             new_date.setUTCDate(day);
18993             test = function(){
18994                 return new_month != new_date.getUTCMonth();
18995             };
18996         }
18997         // Common date-resetting loop -- if date is beyond end of month, make it
18998         // end of month
18999         while (test()){
19000             new_date.setUTCDate(--day);
19001             new_date.setUTCMonth(new_month);
19002         }
19003         return new_date;
19004     },
19005
19006     moveYear: function(date, dir)
19007     {
19008         return this.moveMonth(date, dir*12);
19009     },
19010
19011     dateWithinRange: function(date)
19012     {
19013         return date >= this.startDate && date <= this.endDate;
19014     },
19015
19016     
19017     remove: function() 
19018     {
19019         this.picker().remove();
19020     },
19021     
19022     validateValue : function(value)
19023     {
19024         if(value.length < 1)  {
19025             if(this.allowBlank){
19026                 return true;
19027             }
19028             return false;
19029         }
19030         
19031         if(value.length < this.minLength){
19032             return false;
19033         }
19034         if(value.length > this.maxLength){
19035             return false;
19036         }
19037         if(this.vtype){
19038             var vt = Roo.form.VTypes;
19039             if(!vt[this.vtype](value, this)){
19040                 return false;
19041             }
19042         }
19043         if(typeof this.validator == "function"){
19044             var msg = this.validator(value);
19045             if(msg !== true){
19046                 return false;
19047             }
19048         }
19049         
19050         if(this.regex && !this.regex.test(value)){
19051             return false;
19052         }
19053         
19054         if(typeof(this.parseDate(value)) == 'undefined'){
19055             return false;
19056         }
19057         
19058         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19059             return false;
19060         }      
19061         
19062         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19063             return false;
19064         } 
19065         
19066         
19067         return true;
19068     }
19069    
19070 });
19071
19072 Roo.apply(Roo.bootstrap.DateField,  {
19073     
19074     head : {
19075         tag: 'thead',
19076         cn: [
19077         {
19078             tag: 'tr',
19079             cn: [
19080             {
19081                 tag: 'th',
19082                 cls: 'prev',
19083                 html: '<i class="fa fa-arrow-left"/>'
19084             },
19085             {
19086                 tag: 'th',
19087                 cls: 'switch',
19088                 colspan: '5'
19089             },
19090             {
19091                 tag: 'th',
19092                 cls: 'next',
19093                 html: '<i class="fa fa-arrow-right"/>'
19094             }
19095
19096             ]
19097         }
19098         ]
19099     },
19100     
19101     content : {
19102         tag: 'tbody',
19103         cn: [
19104         {
19105             tag: 'tr',
19106             cn: [
19107             {
19108                 tag: 'td',
19109                 colspan: '7'
19110             }
19111             ]
19112         }
19113         ]
19114     },
19115     
19116     footer : {
19117         tag: 'tfoot',
19118         cn: [
19119         {
19120             tag: 'tr',
19121             cn: [
19122             {
19123                 tag: 'th',
19124                 colspan: '7',
19125                 cls: 'today'
19126             }
19127                     
19128             ]
19129         }
19130         ]
19131     },
19132     
19133     dates:{
19134         en: {
19135             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19136             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19137             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19138             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19139             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19140             today: "Today"
19141         }
19142     },
19143     
19144     modes: [
19145     {
19146         clsName: 'days',
19147         navFnc: 'Month',
19148         navStep: 1
19149     },
19150     {
19151         clsName: 'months',
19152         navFnc: 'FullYear',
19153         navStep: 1
19154     },
19155     {
19156         clsName: 'years',
19157         navFnc: 'FullYear',
19158         navStep: 10
19159     }]
19160 });
19161
19162 Roo.apply(Roo.bootstrap.DateField,  {
19163   
19164     template : {
19165         tag: 'div',
19166         cls: 'datepicker dropdown-menu roo-dynamic',
19167         cn: [
19168         {
19169             tag: 'div',
19170             cls: 'datepicker-days',
19171             cn: [
19172             {
19173                 tag: 'table',
19174                 cls: 'table-condensed',
19175                 cn:[
19176                 Roo.bootstrap.DateField.head,
19177                 {
19178                     tag: 'tbody'
19179                 },
19180                 Roo.bootstrap.DateField.footer
19181                 ]
19182             }
19183             ]
19184         },
19185         {
19186             tag: 'div',
19187             cls: 'datepicker-months',
19188             cn: [
19189             {
19190                 tag: 'table',
19191                 cls: 'table-condensed',
19192                 cn:[
19193                 Roo.bootstrap.DateField.head,
19194                 Roo.bootstrap.DateField.content,
19195                 Roo.bootstrap.DateField.footer
19196                 ]
19197             }
19198             ]
19199         },
19200         {
19201             tag: 'div',
19202             cls: 'datepicker-years',
19203             cn: [
19204             {
19205                 tag: 'table',
19206                 cls: 'table-condensed',
19207                 cn:[
19208                 Roo.bootstrap.DateField.head,
19209                 Roo.bootstrap.DateField.content,
19210                 Roo.bootstrap.DateField.footer
19211                 ]
19212             }
19213             ]
19214         }
19215         ]
19216     }
19217 });
19218
19219  
19220
19221  /*
19222  * - LGPL
19223  *
19224  * TimeField
19225  * 
19226  */
19227
19228 /**
19229  * @class Roo.bootstrap.TimeField
19230  * @extends Roo.bootstrap.Input
19231  * Bootstrap DateField class
19232  * 
19233  * 
19234  * @constructor
19235  * Create a new TimeField
19236  * @param {Object} config The config object
19237  */
19238
19239 Roo.bootstrap.TimeField = function(config){
19240     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19241     this.addEvents({
19242             /**
19243              * @event show
19244              * Fires when this field show.
19245              * @param {Roo.bootstrap.DateField} thisthis
19246              * @param {Mixed} date The date value
19247              */
19248             show : true,
19249             /**
19250              * @event show
19251              * Fires when this field hide.
19252              * @param {Roo.bootstrap.DateField} this
19253              * @param {Mixed} date The date value
19254              */
19255             hide : true,
19256             /**
19257              * @event select
19258              * Fires when select a date.
19259              * @param {Roo.bootstrap.DateField} this
19260              * @param {Mixed} date The date value
19261              */
19262             select : true
19263         });
19264 };
19265
19266 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19267     
19268     /**
19269      * @cfg {String} format
19270      * The default time format string which can be overriden for localization support.  The format must be
19271      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19272      */
19273     format : "H:i",
19274        
19275     onRender: function(ct, position)
19276     {
19277         
19278         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19279                 
19280         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19281         
19282         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19283         
19284         this.pop = this.picker().select('>.datepicker-time',true).first();
19285         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19286         
19287         this.picker().on('mousedown', this.onMousedown, this);
19288         this.picker().on('click', this.onClick, this);
19289         
19290         this.picker().addClass('datepicker-dropdown');
19291     
19292         this.fillTime();
19293         this.update();
19294             
19295         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19296         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19297         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19298         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19299         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19300         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19301
19302     },
19303     
19304     fireKey: function(e){
19305         if (!this.picker().isVisible()){
19306             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19307                 this.show();
19308             }
19309             return;
19310         }
19311
19312         e.preventDefault();
19313         
19314         switch(e.keyCode){
19315             case 27: // escape
19316                 this.hide();
19317                 break;
19318             case 37: // left
19319             case 39: // right
19320                 this.onTogglePeriod();
19321                 break;
19322             case 38: // up
19323                 this.onIncrementMinutes();
19324                 break;
19325             case 40: // down
19326                 this.onDecrementMinutes();
19327                 break;
19328             case 13: // enter
19329             case 9: // tab
19330                 this.setTime();
19331                 break;
19332         }
19333     },
19334     
19335     onClick: function(e) {
19336         e.stopPropagation();
19337         e.preventDefault();
19338     },
19339     
19340     picker : function()
19341     {
19342         return this.el.select('.datepicker', true).first();
19343     },
19344     
19345     fillTime: function()
19346     {    
19347         var time = this.pop.select('tbody', true).first();
19348         
19349         time.dom.innerHTML = '';
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-up glyphicon glyphicon-chevron-up'
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-up glyphicon glyphicon-chevron-up'
19385                                 }
19386                             ]
19387                         }
19388                     ]
19389                 },
19390                 {
19391                     tag: 'td',
19392                     cls: 'separator'
19393                 }
19394             ]
19395         });
19396         
19397         time.createChild({
19398             tag: 'tr',
19399             cn: [
19400                 {
19401                     tag: 'td',
19402                     cn: [
19403                         {
19404                             tag: 'span',
19405                             cls: 'timepicker-hour',
19406                             html: '00'
19407                         }  
19408                     ]
19409                 },
19410                 {
19411                     tag: 'td',
19412                     cls: 'separator',
19413                     html: ':'
19414                 },
19415                 {
19416                     tag: 'td',
19417                     cn: [
19418                         {
19419                             tag: 'span',
19420                             cls: 'timepicker-minute',
19421                             html: '00'
19422                         }  
19423                     ]
19424                 },
19425                 {
19426                     tag: 'td',
19427                     cls: 'separator'
19428                 },
19429                 {
19430                     tag: 'td',
19431                     cn: [
19432                         {
19433                             tag: 'button',
19434                             type: 'button',
19435                             cls: 'btn btn-primary period',
19436                             html: 'AM'
19437                             
19438                         }
19439                     ]
19440                 }
19441             ]
19442         });
19443         
19444         time.createChild({
19445             tag: 'tr',
19446             cn: [
19447                 {
19448                     tag: 'td',
19449                     cn: [
19450                         {
19451                             tag: 'a',
19452                             href: '#',
19453                             cls: 'btn',
19454                             cn: [
19455                                 {
19456                                     tag: 'span',
19457                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19458                                 }
19459                             ]
19460                         }
19461                     ]
19462                 },
19463                 {
19464                     tag: 'td',
19465                     cls: 'separator'
19466                 },
19467                 {
19468                     tag: 'td',
19469                     cn: [
19470                         {
19471                             tag: 'a',
19472                             href: '#',
19473                             cls: 'btn',
19474                             cn: [
19475                                 {
19476                                     tag: 'span',
19477                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19478                                 }
19479                             ]
19480                         }
19481                     ]
19482                 },
19483                 {
19484                     tag: 'td',
19485                     cls: 'separator'
19486                 }
19487             ]
19488         });
19489         
19490     },
19491     
19492     update: function()
19493     {
19494         
19495         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19496         
19497         this.fill();
19498     },
19499     
19500     fill: function() 
19501     {
19502         var hours = this.time.getHours();
19503         var minutes = this.time.getMinutes();
19504         var period = 'AM';
19505         
19506         if(hours > 11){
19507             period = 'PM';
19508         }
19509         
19510         if(hours == 0){
19511             hours = 12;
19512         }
19513         
19514         
19515         if(hours > 12){
19516             hours = hours - 12;
19517         }
19518         
19519         if(hours < 10){
19520             hours = '0' + hours;
19521         }
19522         
19523         if(minutes < 10){
19524             minutes = '0' + minutes;
19525         }
19526         
19527         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19528         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19529         this.pop.select('button', true).first().dom.innerHTML = period;
19530         
19531     },
19532     
19533     place: function()
19534     {   
19535         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19536         
19537         var cls = ['bottom'];
19538         
19539         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19540             cls.pop();
19541             cls.push('top');
19542         }
19543         
19544         cls.push('right');
19545         
19546         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19547             cls.pop();
19548             cls.push('left');
19549         }
19550         
19551         this.picker().addClass(cls.join('-'));
19552         
19553         var _this = this;
19554         
19555         Roo.each(cls, function(c){
19556             if(c == 'bottom'){
19557                 _this.picker().setTop(_this.inputEl().getHeight());
19558                 return;
19559             }
19560             if(c == 'top'){
19561                 _this.picker().setTop(0 - _this.picker().getHeight());
19562                 return;
19563             }
19564             
19565             if(c == 'left'){
19566                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19567                 return;
19568             }
19569             if(c == 'right'){
19570                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19571                 return;
19572             }
19573         });
19574         
19575     },
19576   
19577     onFocus : function()
19578     {
19579         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19580         this.show();
19581     },
19582     
19583     onBlur : function()
19584     {
19585         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19586         this.hide();
19587     },
19588     
19589     show : function()
19590     {
19591         this.picker().show();
19592         this.pop.show();
19593         this.update();
19594         this.place();
19595         
19596         this.fireEvent('show', this, this.date);
19597     },
19598     
19599     hide : function()
19600     {
19601         this.picker().hide();
19602         this.pop.hide();
19603         
19604         this.fireEvent('hide', this, this.date);
19605     },
19606     
19607     setTime : function()
19608     {
19609         this.hide();
19610         this.setValue(this.time.format(this.format));
19611         
19612         this.fireEvent('select', this, this.date);
19613         
19614         
19615     },
19616     
19617     onMousedown: function(e){
19618         e.stopPropagation();
19619         e.preventDefault();
19620     },
19621     
19622     onIncrementHours: function()
19623     {
19624         Roo.log('onIncrementHours');
19625         this.time = this.time.add(Date.HOUR, 1);
19626         this.update();
19627         
19628     },
19629     
19630     onDecrementHours: function()
19631     {
19632         Roo.log('onDecrementHours');
19633         this.time = this.time.add(Date.HOUR, -1);
19634         this.update();
19635     },
19636     
19637     onIncrementMinutes: function()
19638     {
19639         Roo.log('onIncrementMinutes');
19640         this.time = this.time.add(Date.MINUTE, 1);
19641         this.update();
19642     },
19643     
19644     onDecrementMinutes: function()
19645     {
19646         Roo.log('onDecrementMinutes');
19647         this.time = this.time.add(Date.MINUTE, -1);
19648         this.update();
19649     },
19650     
19651     onTogglePeriod: function()
19652     {
19653         Roo.log('onTogglePeriod');
19654         this.time = this.time.add(Date.HOUR, 12);
19655         this.update();
19656     }
19657     
19658    
19659 });
19660
19661 Roo.apply(Roo.bootstrap.TimeField,  {
19662     
19663     content : {
19664         tag: 'tbody',
19665         cn: [
19666             {
19667                 tag: 'tr',
19668                 cn: [
19669                 {
19670                     tag: 'td',
19671                     colspan: '7'
19672                 }
19673                 ]
19674             }
19675         ]
19676     },
19677     
19678     footer : {
19679         tag: 'tfoot',
19680         cn: [
19681             {
19682                 tag: 'tr',
19683                 cn: [
19684                 {
19685                     tag: 'th',
19686                     colspan: '7',
19687                     cls: '',
19688                     cn: [
19689                         {
19690                             tag: 'button',
19691                             cls: 'btn btn-info ok',
19692                             html: 'OK'
19693                         }
19694                     ]
19695                 }
19696
19697                 ]
19698             }
19699         ]
19700     }
19701 });
19702
19703 Roo.apply(Roo.bootstrap.TimeField,  {
19704   
19705     template : {
19706         tag: 'div',
19707         cls: 'datepicker dropdown-menu',
19708         cn: [
19709             {
19710                 tag: 'div',
19711                 cls: 'datepicker-time',
19712                 cn: [
19713                 {
19714                     tag: 'table',
19715                     cls: 'table-condensed',
19716                     cn:[
19717                     Roo.bootstrap.TimeField.content,
19718                     Roo.bootstrap.TimeField.footer
19719                     ]
19720                 }
19721                 ]
19722             }
19723         ]
19724     }
19725 });
19726
19727  
19728
19729  /*
19730  * - LGPL
19731  *
19732  * MonthField
19733  * 
19734  */
19735
19736 /**
19737  * @class Roo.bootstrap.MonthField
19738  * @extends Roo.bootstrap.Input
19739  * Bootstrap MonthField class
19740  * 
19741  * @cfg {String} language default en
19742  * 
19743  * @constructor
19744  * Create a new MonthField
19745  * @param {Object} config The config object
19746  */
19747
19748 Roo.bootstrap.MonthField = function(config){
19749     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19750     
19751     this.addEvents({
19752         /**
19753          * @event show
19754          * Fires when this field show.
19755          * @param {Roo.bootstrap.MonthField} this
19756          * @param {Mixed} date The date value
19757          */
19758         show : true,
19759         /**
19760          * @event show
19761          * Fires when this field hide.
19762          * @param {Roo.bootstrap.MonthField} this
19763          * @param {Mixed} date The date value
19764          */
19765         hide : true,
19766         /**
19767          * @event select
19768          * Fires when select a date.
19769          * @param {Roo.bootstrap.MonthField} this
19770          * @param {String} oldvalue The old value
19771          * @param {String} newvalue The new value
19772          */
19773         select : true
19774     });
19775 };
19776
19777 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19778     
19779     onRender: function(ct, position)
19780     {
19781         
19782         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19783         
19784         this.language = this.language || 'en';
19785         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19786         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19787         
19788         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19789         this.isInline = false;
19790         this.isInput = true;
19791         this.component = this.el.select('.add-on', true).first() || false;
19792         this.component = (this.component && this.component.length === 0) ? false : this.component;
19793         this.hasInput = this.component && this.inputEL().length;
19794         
19795         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19796         
19797         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19798         
19799         this.picker().on('mousedown', this.onMousedown, this);
19800         this.picker().on('click', this.onClick, this);
19801         
19802         this.picker().addClass('datepicker-dropdown');
19803         
19804         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19805             v.setStyle('width', '189px');
19806         });
19807         
19808         this.fillMonths();
19809         
19810         this.update();
19811         
19812         if(this.isInline) {
19813             this.show();
19814         }
19815         
19816     },
19817     
19818     setValue: function(v, suppressEvent)
19819     {   
19820         var o = this.getValue();
19821         
19822         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19823         
19824         this.update();
19825
19826         if(suppressEvent !== true){
19827             this.fireEvent('select', this, o, v);
19828         }
19829         
19830     },
19831     
19832     getValue: function()
19833     {
19834         return this.value;
19835     },
19836     
19837     onClick: function(e) 
19838     {
19839         e.stopPropagation();
19840         e.preventDefault();
19841         
19842         var target = e.getTarget();
19843         
19844         if(target.nodeName.toLowerCase() === 'i'){
19845             target = Roo.get(target).dom.parentNode;
19846         }
19847         
19848         var nodeName = target.nodeName;
19849         var className = target.className;
19850         var html = target.innerHTML;
19851         
19852         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19853             return;
19854         }
19855         
19856         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19857         
19858         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19859         
19860         this.hide();
19861                         
19862     },
19863     
19864     picker : function()
19865     {
19866         return this.pickerEl;
19867     },
19868     
19869     fillMonths: function()
19870     {    
19871         var i = 0;
19872         var months = this.picker().select('>.datepicker-months td', true).first();
19873         
19874         months.dom.innerHTML = '';
19875         
19876         while (i < 12) {
19877             var month = {
19878                 tag: 'span',
19879                 cls: 'month',
19880                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19881             };
19882             
19883             months.createChild(month);
19884         }
19885         
19886     },
19887     
19888     update: function()
19889     {
19890         var _this = this;
19891         
19892         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19893             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19894         }
19895         
19896         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19897             e.removeClass('active');
19898             
19899             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19900                 e.addClass('active');
19901             }
19902         })
19903     },
19904     
19905     place: function()
19906     {
19907         if(this.isInline) {
19908             return;
19909         }
19910         
19911         this.picker().removeClass(['bottom', 'top']);
19912         
19913         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19914             /*
19915              * place to the top of element!
19916              *
19917              */
19918             
19919             this.picker().addClass('top');
19920             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19921             
19922             return;
19923         }
19924         
19925         this.picker().addClass('bottom');
19926         
19927         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19928     },
19929     
19930     onFocus : function()
19931     {
19932         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19933         this.show();
19934     },
19935     
19936     onBlur : function()
19937     {
19938         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19939         
19940         var d = this.inputEl().getValue();
19941         
19942         this.setValue(d);
19943                 
19944         this.hide();
19945     },
19946     
19947     show : function()
19948     {
19949         this.picker().show();
19950         this.picker().select('>.datepicker-months', true).first().show();
19951         this.update();
19952         this.place();
19953         
19954         this.fireEvent('show', this, this.date);
19955     },
19956     
19957     hide : function()
19958     {
19959         if(this.isInline) {
19960             return;
19961         }
19962         this.picker().hide();
19963         this.fireEvent('hide', this, this.date);
19964         
19965     },
19966     
19967     onMousedown: function(e)
19968     {
19969         e.stopPropagation();
19970         e.preventDefault();
19971     },
19972     
19973     keyup: function(e)
19974     {
19975         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19976         this.update();
19977     },
19978
19979     fireKey: function(e)
19980     {
19981         if (!this.picker().isVisible()){
19982             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19983                 this.show();
19984             }
19985             return;
19986         }
19987         
19988         var dir;
19989         
19990         switch(e.keyCode){
19991             case 27: // escape
19992                 this.hide();
19993                 e.preventDefault();
19994                 break;
19995             case 37: // left
19996             case 39: // right
19997                 dir = e.keyCode == 37 ? -1 : 1;
19998                 
19999                 this.vIndex = this.vIndex + dir;
20000                 
20001                 if(this.vIndex < 0){
20002                     this.vIndex = 0;
20003                 }
20004                 
20005                 if(this.vIndex > 11){
20006                     this.vIndex = 11;
20007                 }
20008                 
20009                 if(isNaN(this.vIndex)){
20010                     this.vIndex = 0;
20011                 }
20012                 
20013                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20014                 
20015                 break;
20016             case 38: // up
20017             case 40: // down
20018                 
20019                 dir = e.keyCode == 38 ? -1 : 1;
20020                 
20021                 this.vIndex = this.vIndex + dir * 4;
20022                 
20023                 if(this.vIndex < 0){
20024                     this.vIndex = 0;
20025                 }
20026                 
20027                 if(this.vIndex > 11){
20028                     this.vIndex = 11;
20029                 }
20030                 
20031                 if(isNaN(this.vIndex)){
20032                     this.vIndex = 0;
20033                 }
20034                 
20035                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20036                 break;
20037                 
20038             case 13: // enter
20039                 
20040                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20041                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20042                 }
20043                 
20044                 this.hide();
20045                 e.preventDefault();
20046                 break;
20047             case 9: // tab
20048                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20049                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20050                 }
20051                 this.hide();
20052                 break;
20053             case 16: // shift
20054             case 17: // ctrl
20055             case 18: // alt
20056                 break;
20057             default :
20058                 this.hide();
20059                 
20060         }
20061     },
20062     
20063     remove: function() 
20064     {
20065         this.picker().remove();
20066     }
20067    
20068 });
20069
20070 Roo.apply(Roo.bootstrap.MonthField,  {
20071     
20072     content : {
20073         tag: 'tbody',
20074         cn: [
20075         {
20076             tag: 'tr',
20077             cn: [
20078             {
20079                 tag: 'td',
20080                 colspan: '7'
20081             }
20082             ]
20083         }
20084         ]
20085     },
20086     
20087     dates:{
20088         en: {
20089             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20090             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20091         }
20092     }
20093 });
20094
20095 Roo.apply(Roo.bootstrap.MonthField,  {
20096   
20097     template : {
20098         tag: 'div',
20099         cls: 'datepicker dropdown-menu roo-dynamic',
20100         cn: [
20101             {
20102                 tag: 'div',
20103                 cls: 'datepicker-months',
20104                 cn: [
20105                 {
20106                     tag: 'table',
20107                     cls: 'table-condensed',
20108                     cn:[
20109                         Roo.bootstrap.DateField.content
20110                     ]
20111                 }
20112                 ]
20113             }
20114         ]
20115     }
20116 });
20117
20118  
20119
20120  
20121  /*
20122  * - LGPL
20123  *
20124  * CheckBox
20125  * 
20126  */
20127
20128 /**
20129  * @class Roo.bootstrap.CheckBox
20130  * @extends Roo.bootstrap.Input
20131  * Bootstrap CheckBox class
20132  * 
20133  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20134  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20135  * @cfg {String} boxLabel The text that appears beside the checkbox
20136  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20137  * @cfg {Boolean} checked initnal the element
20138  * @cfg {Boolean} inline inline the element (default false)
20139  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20140  * @cfg {String} tooltip label tooltip
20141  * 
20142  * @constructor
20143  * Create a new CheckBox
20144  * @param {Object} config The config object
20145  */
20146
20147 Roo.bootstrap.CheckBox = function(config){
20148     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20149    
20150     this.addEvents({
20151         /**
20152         * @event check
20153         * Fires when the element is checked or unchecked.
20154         * @param {Roo.bootstrap.CheckBox} this This input
20155         * @param {Boolean} checked The new checked value
20156         */
20157        check : true,
20158        /**
20159         * @event click
20160         * Fires when the element is click.
20161         * @param {Roo.bootstrap.CheckBox} this This input
20162         */
20163        click : true
20164     });
20165     
20166 };
20167
20168 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20169   
20170     inputType: 'checkbox',
20171     inputValue: 1,
20172     valueOff: 0,
20173     boxLabel: false,
20174     checked: false,
20175     weight : false,
20176     inline: false,
20177     tooltip : '',
20178     
20179     getAutoCreate : function()
20180     {
20181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20182         
20183         var id = Roo.id();
20184         
20185         var cfg = {};
20186         
20187         cfg.cls = 'form-group ' + this.inputType; //input-group
20188         
20189         if(this.inline){
20190             cfg.cls += ' ' + this.inputType + '-inline';
20191         }
20192         
20193         var input =  {
20194             tag: 'input',
20195             id : id,
20196             type : this.inputType,
20197             value : this.inputValue,
20198             cls : 'roo-' + this.inputType, //'form-box',
20199             placeholder : this.placeholder || ''
20200             
20201         };
20202         
20203         if(this.inputType != 'radio'){
20204             var hidden =  {
20205                 tag: 'input',
20206                 type : 'hidden',
20207                 cls : 'roo-hidden-value',
20208                 value : this.checked ? this.inputValue : this.valueOff
20209             };
20210         }
20211         
20212             
20213         if (this.weight) { // Validity check?
20214             cfg.cls += " " + this.inputType + "-" + this.weight;
20215         }
20216         
20217         if (this.disabled) {
20218             input.disabled=true;
20219         }
20220         
20221         if(this.checked){
20222             input.checked = this.checked;
20223         }
20224         
20225         if (this.name) {
20226             
20227             input.name = this.name;
20228             
20229             if(this.inputType != 'radio'){
20230                 hidden.name = this.name;
20231                 input.name = '_hidden_' + this.name;
20232             }
20233         }
20234         
20235         if (this.size) {
20236             input.cls += ' input-' + this.size;
20237         }
20238         
20239         var settings=this;
20240         
20241         ['xs','sm','md','lg'].map(function(size){
20242             if (settings[size]) {
20243                 cfg.cls += ' col-' + size + '-' + settings[size];
20244             }
20245         });
20246         
20247         var inputblock = input;
20248          
20249         if (this.before || this.after) {
20250             
20251             inputblock = {
20252                 cls : 'input-group',
20253                 cn :  [] 
20254             };
20255             
20256             if (this.before) {
20257                 inputblock.cn.push({
20258                     tag :'span',
20259                     cls : 'input-group-addon',
20260                     html : this.before
20261                 });
20262             }
20263             
20264             inputblock.cn.push(input);
20265             
20266             if(this.inputType != 'radio'){
20267                 inputblock.cn.push(hidden);
20268             }
20269             
20270             if (this.after) {
20271                 inputblock.cn.push({
20272                     tag :'span',
20273                     cls : 'input-group-addon',
20274                     html : this.after
20275                 });
20276             }
20277             
20278         }
20279         
20280         if (align ==='left' && this.fieldLabel.length) {
20281 //                Roo.log("left and has label");
20282             cfg.cn = [
20283                 {
20284                     tag: 'label',
20285                     'for' :  id,
20286                     cls : 'control-label',
20287                     html : this.fieldLabel
20288                 },
20289                 {
20290                     cls : "", 
20291                     cn: [
20292                         inputblock
20293                     ]
20294                 }
20295             ];
20296             
20297             if(this.labelWidth > 12){
20298                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20299             }
20300             
20301             if(this.labelWidth < 13 && this.labelmd == 0){
20302                 this.labelmd = this.labelWidth;
20303             }
20304             
20305             if(this.labellg > 0){
20306                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20307                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20308             }
20309             
20310             if(this.labelmd > 0){
20311                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20312                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20313             }
20314             
20315             if(this.labelsm > 0){
20316                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20317                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20318             }
20319             
20320             if(this.labelxs > 0){
20321                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20322                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20323             }
20324             
20325         } else if ( this.fieldLabel.length) {
20326 //                Roo.log(" label");
20327                 cfg.cn = [
20328                    
20329                     {
20330                         tag: this.boxLabel ? 'span' : 'label',
20331                         'for': id,
20332                         cls: 'control-label box-input-label',
20333                         //cls : 'input-group-addon',
20334                         html : this.fieldLabel
20335                     },
20336                     
20337                     inputblock
20338                     
20339                 ];
20340
20341         } else {
20342             
20343 //                Roo.log(" no label && no align");
20344                 cfg.cn = [  inputblock ] ;
20345                 
20346                 
20347         }
20348         
20349         if(this.boxLabel){
20350              var boxLabelCfg = {
20351                 tag: 'label',
20352                 //'for': id, // box label is handled by onclick - so no for...
20353                 cls: 'box-label',
20354                 html: this.boxLabel
20355             };
20356             
20357             if(this.tooltip){
20358                 boxLabelCfg.tooltip = this.tooltip;
20359             }
20360              
20361             cfg.cn.push(boxLabelCfg);
20362         }
20363         
20364         if(this.inputType != 'radio'){
20365             cfg.cn.push(hidden);
20366         }
20367         
20368         return cfg;
20369         
20370     },
20371     
20372     /**
20373      * return the real input element.
20374      */
20375     inputEl: function ()
20376     {
20377         return this.el.select('input.roo-' + this.inputType,true).first();
20378     },
20379     hiddenEl: function ()
20380     {
20381         return this.el.select('input.roo-hidden-value',true).first();
20382     },
20383     
20384     labelEl: function()
20385     {
20386         return this.el.select('label.control-label',true).first();
20387     },
20388     /* depricated... */
20389     
20390     label: function()
20391     {
20392         return this.labelEl();
20393     },
20394     
20395     boxLabelEl: function()
20396     {
20397         return this.el.select('label.box-label',true).first();
20398     },
20399     
20400     initEvents : function()
20401     {
20402 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20403         
20404         this.inputEl().on('click', this.onClick,  this);
20405         
20406         if (this.boxLabel) { 
20407             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20408         }
20409         
20410         this.startValue = this.getValue();
20411         
20412         if(this.groupId){
20413             Roo.bootstrap.CheckBox.register(this);
20414         }
20415     },
20416     
20417     onClick : function(e)
20418     {   
20419         if(this.fireEvent('click', this, e) !== false){
20420             this.setChecked(!this.checked);
20421         }
20422         
20423     },
20424     
20425     setChecked : function(state,suppressEvent)
20426     {
20427         this.startValue = this.getValue();
20428
20429         if(this.inputType == 'radio'){
20430             
20431             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20432                 e.dom.checked = false;
20433             });
20434             
20435             this.inputEl().dom.checked = true;
20436             
20437             this.inputEl().dom.value = this.inputValue;
20438             
20439             if(suppressEvent !== true){
20440                 this.fireEvent('check', this, true);
20441             }
20442             
20443             this.validate();
20444             
20445             return;
20446         }
20447         
20448         this.checked = state;
20449         
20450         this.inputEl().dom.checked = state;
20451         
20452         
20453         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20454         
20455         if(suppressEvent !== true){
20456             this.fireEvent('check', this, state);
20457         }
20458         
20459         this.validate();
20460     },
20461     
20462     getValue : function()
20463     {
20464         if(this.inputType == 'radio'){
20465             return this.getGroupValue();
20466         }
20467         
20468         return this.hiddenEl().dom.value;
20469         
20470     },
20471     
20472     getGroupValue : function()
20473     {
20474         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20475             return '';
20476         }
20477         
20478         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20479     },
20480     
20481     setValue : function(v,suppressEvent)
20482     {
20483         if(this.inputType == 'radio'){
20484             this.setGroupValue(v, suppressEvent);
20485             return;
20486         }
20487         
20488         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20489         
20490         this.validate();
20491     },
20492     
20493     setGroupValue : function(v, suppressEvent)
20494     {
20495         this.startValue = this.getValue();
20496         
20497         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20498             e.dom.checked = false;
20499             
20500             if(e.dom.value == v){
20501                 e.dom.checked = true;
20502             }
20503         });
20504         
20505         if(suppressEvent !== true){
20506             this.fireEvent('check', this, true);
20507         }
20508
20509         this.validate();
20510         
20511         return;
20512     },
20513     
20514     validate : function()
20515     {
20516         if(
20517                 this.disabled || 
20518                 (this.inputType == 'radio' && this.validateRadio()) ||
20519                 (this.inputType == 'checkbox' && this.validateCheckbox())
20520         ){
20521             this.markValid();
20522             return true;
20523         }
20524         
20525         this.markInvalid();
20526         return false;
20527     },
20528     
20529     validateRadio : function()
20530     {
20531         if(this.allowBlank){
20532             return true;
20533         }
20534         
20535         var valid = false;
20536         
20537         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20538             if(!e.dom.checked){
20539                 return;
20540             }
20541             
20542             valid = true;
20543             
20544             return false;
20545         });
20546         
20547         return valid;
20548     },
20549     
20550     validateCheckbox : function()
20551     {
20552         if(!this.groupId){
20553             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20554             //return (this.getValue() == this.inputValue) ? true : false;
20555         }
20556         
20557         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20558         
20559         if(!group){
20560             return false;
20561         }
20562         
20563         var r = false;
20564         
20565         for(var i in group){
20566             if(group[i].el.isVisible(true)){
20567                 r = false;
20568                 break;
20569             }
20570             
20571             r = true;
20572         }
20573         
20574         for(var i in group){
20575             if(r){
20576                 break;
20577             }
20578             
20579             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20580         }
20581         
20582         return r;
20583     },
20584     
20585     /**
20586      * Mark this field as valid
20587      */
20588     markValid : function()
20589     {
20590         var _this = this;
20591         
20592         this.fireEvent('valid', this);
20593         
20594         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20595         
20596         if(this.groupId){
20597             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20598         }
20599         
20600         if(label){
20601             label.markValid();
20602         }
20603
20604         if(this.inputType == 'radio'){
20605             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20606                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20607                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20608             });
20609             
20610             return;
20611         }
20612
20613         if(!this.groupId){
20614             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20615             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20616             return;
20617         }
20618         
20619         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20620         
20621         if(!group){
20622             return;
20623         }
20624         
20625         for(var i in group){
20626             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20627             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20628         }
20629     },
20630     
20631      /**
20632      * Mark this field as invalid
20633      * @param {String} msg The validation message
20634      */
20635     markInvalid : function(msg)
20636     {
20637         if(this.allowBlank){
20638             return;
20639         }
20640         
20641         var _this = this;
20642         
20643         this.fireEvent('invalid', this, msg);
20644         
20645         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20646         
20647         if(this.groupId){
20648             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20649         }
20650         
20651         if(label){
20652             label.markInvalid();
20653         }
20654             
20655         if(this.inputType == 'radio'){
20656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20657                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20658                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20659             });
20660             
20661             return;
20662         }
20663         
20664         if(!this.groupId){
20665             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20666             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20667             return;
20668         }
20669         
20670         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20671         
20672         if(!group){
20673             return;
20674         }
20675         
20676         for(var i in group){
20677             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20678             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20679         }
20680         
20681     },
20682     
20683     clearInvalid : function()
20684     {
20685         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20686         
20687         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20688         
20689         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20690         
20691         if (label && label.iconEl) {
20692             label.iconEl.removeClass(label.validClass);
20693             label.iconEl.removeClass(label.invalidClass);
20694         }
20695     },
20696     
20697     disable : function()
20698     {
20699         if(this.inputType != 'radio'){
20700             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20701             return;
20702         }
20703         
20704         var _this = this;
20705         
20706         if(this.rendered){
20707             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20708                 _this.getActionEl().addClass(this.disabledClass);
20709                 e.dom.disabled = true;
20710             });
20711         }
20712         
20713         this.disabled = true;
20714         this.fireEvent("disable", this);
20715         return this;
20716     },
20717
20718     enable : function()
20719     {
20720         if(this.inputType != 'radio'){
20721             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20722             return;
20723         }
20724         
20725         var _this = this;
20726         
20727         if(this.rendered){
20728             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20729                 _this.getActionEl().removeClass(this.disabledClass);
20730                 e.dom.disabled = false;
20731             });
20732         }
20733         
20734         this.disabled = false;
20735         this.fireEvent("enable", this);
20736         return this;
20737     }
20738
20739 });
20740
20741 Roo.apply(Roo.bootstrap.CheckBox, {
20742     
20743     groups: {},
20744     
20745      /**
20746     * register a CheckBox Group
20747     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20748     */
20749     register : function(checkbox)
20750     {
20751         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20752             this.groups[checkbox.groupId] = {};
20753         }
20754         
20755         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20756             return;
20757         }
20758         
20759         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20760         
20761     },
20762     /**
20763     * fetch a CheckBox Group based on the group ID
20764     * @param {string} the group ID
20765     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20766     */
20767     get: function(groupId) {
20768         if (typeof(this.groups[groupId]) == 'undefined') {
20769             return false;
20770         }
20771         
20772         return this.groups[groupId] ;
20773     }
20774     
20775     
20776 });
20777 /*
20778  * - LGPL
20779  *
20780  * RadioItem
20781  * 
20782  */
20783
20784 /**
20785  * @class Roo.bootstrap.Radio
20786  * @extends Roo.bootstrap.Component
20787  * Bootstrap Radio class
20788  * @cfg {String} boxLabel - the label associated
20789  * @cfg {String} value - the value of radio
20790  * 
20791  * @constructor
20792  * Create a new Radio
20793  * @param {Object} config The config object
20794  */
20795 Roo.bootstrap.Radio = function(config){
20796     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20797     
20798 };
20799
20800 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20801     
20802     boxLabel : '',
20803     
20804     value : '',
20805     
20806     getAutoCreate : function()
20807     {
20808         var cfg = {
20809             tag : 'div',
20810             cls : 'form-group radio',
20811             cn : [
20812                 {
20813                     tag : 'label',
20814                     cls : 'box-label',
20815                     html : this.boxLabel
20816                 }
20817             ]
20818         };
20819         
20820         return cfg;
20821     },
20822     
20823     initEvents : function() 
20824     {
20825         this.parent().register(this);
20826         
20827         this.el.on('click', this.onClick, this);
20828         
20829     },
20830     
20831     onClick : function()
20832     {
20833         this.setChecked(true);
20834     },
20835     
20836     setChecked : function(state, suppressEvent)
20837     {
20838         this.parent().setValue(this.value, suppressEvent);
20839         
20840     },
20841     
20842     setBoxLabel : function(v)
20843     {
20844         this.boxLabel = v;
20845         
20846         if(this.rendered){
20847             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20848         }
20849     }
20850     
20851 });
20852  
20853
20854  /*
20855  * - LGPL
20856  *
20857  * Input
20858  * 
20859  */
20860
20861 /**
20862  * @class Roo.bootstrap.SecurePass
20863  * @extends Roo.bootstrap.Input
20864  * Bootstrap SecurePass class
20865  *
20866  * 
20867  * @constructor
20868  * Create a new SecurePass
20869  * @param {Object} config The config object
20870  */
20871  
20872 Roo.bootstrap.SecurePass = function (config) {
20873     // these go here, so the translation tool can replace them..
20874     this.errors = {
20875         PwdEmpty: "Please type a password, and then retype it to confirm.",
20876         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20877         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20878         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20879         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20880         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20881         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20882         TooWeak: "Your password is Too Weak."
20883     },
20884     this.meterLabel = "Password strength:";
20885     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20886     this.meterClass = [
20887         "roo-password-meter-tooweak", 
20888         "roo-password-meter-weak", 
20889         "roo-password-meter-medium", 
20890         "roo-password-meter-strong", 
20891         "roo-password-meter-grey"
20892     ];
20893     
20894     this.errors = {};
20895     
20896     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20897 }
20898
20899 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20900     /**
20901      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20902      * {
20903      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20904      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20905      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20906      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20907      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20908      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20909      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20910      * })
20911      */
20912     // private
20913     
20914     meterWidth: 300,
20915     errorMsg :'',    
20916     errors: false,
20917     imageRoot: '/',
20918     /**
20919      * @cfg {String/Object} Label for the strength meter (defaults to
20920      * 'Password strength:')
20921      */
20922     // private
20923     meterLabel: '',
20924     /**
20925      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20926      * ['Weak', 'Medium', 'Strong'])
20927      */
20928     // private    
20929     pwdStrengths: false,    
20930     // private
20931     strength: 0,
20932     // private
20933     _lastPwd: null,
20934     // private
20935     kCapitalLetter: 0,
20936     kSmallLetter: 1,
20937     kDigit: 2,
20938     kPunctuation: 3,
20939     
20940     insecure: false,
20941     // private
20942     initEvents: function ()
20943     {
20944         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20945
20946         if (this.el.is('input[type=password]') && Roo.isSafari) {
20947             this.el.on('keydown', this.SafariOnKeyDown, this);
20948         }
20949
20950         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20951     },
20952     // private
20953     onRender: function (ct, position)
20954     {
20955         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20956         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20957         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20958
20959         this.trigger.createChild({
20960                    cn: [
20961                     {
20962                     //id: 'PwdMeter',
20963                     tag: 'div',
20964                     cls: 'roo-password-meter-grey col-xs-12',
20965                     style: {
20966                         //width: 0,
20967                         //width: this.meterWidth + 'px'                                                
20968                         }
20969                     },
20970                     {                            
20971                          cls: 'roo-password-meter-text'                          
20972                     }
20973                 ]            
20974         });
20975
20976          
20977         if (this.hideTrigger) {
20978             this.trigger.setDisplayed(false);
20979         }
20980         this.setSize(this.width || '', this.height || '');
20981     },
20982     // private
20983     onDestroy: function ()
20984     {
20985         if (this.trigger) {
20986             this.trigger.removeAllListeners();
20987             this.trigger.remove();
20988         }
20989         if (this.wrap) {
20990             this.wrap.remove();
20991         }
20992         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20993     },
20994     // private
20995     checkStrength: function ()
20996     {
20997         var pwd = this.inputEl().getValue();
20998         if (pwd == this._lastPwd) {
20999             return;
21000         }
21001
21002         var strength;
21003         if (this.ClientSideStrongPassword(pwd)) {
21004             strength = 3;
21005         } else if (this.ClientSideMediumPassword(pwd)) {
21006             strength = 2;
21007         } else if (this.ClientSideWeakPassword(pwd)) {
21008             strength = 1;
21009         } else {
21010             strength = 0;
21011         }
21012         
21013         Roo.log('strength1: ' + strength);
21014         
21015         //var pm = this.trigger.child('div/div/div').dom;
21016         var pm = this.trigger.child('div/div');
21017         pm.removeClass(this.meterClass);
21018         pm.addClass(this.meterClass[strength]);
21019                 
21020         
21021         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21022                 
21023         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21024         
21025         this._lastPwd = pwd;
21026     },
21027     reset: function ()
21028     {
21029         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21030         
21031         this._lastPwd = '';
21032         
21033         var pm = this.trigger.child('div/div');
21034         pm.removeClass(this.meterClass);
21035         pm.addClass('roo-password-meter-grey');        
21036         
21037         
21038         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21039         
21040         pt.innerHTML = '';
21041         this.inputEl().dom.type='password';
21042     },
21043     // private
21044     validateValue: function (value)
21045     {
21046         
21047         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21048             return false;
21049         }
21050         if (value.length == 0) {
21051             if (this.allowBlank) {
21052                 this.clearInvalid();
21053                 return true;
21054             }
21055
21056             this.markInvalid(this.errors.PwdEmpty);
21057             this.errorMsg = this.errors.PwdEmpty;
21058             return false;
21059         }
21060         
21061         if(this.insecure){
21062             return true;
21063         }
21064         
21065         if ('[\x21-\x7e]*'.match(value)) {
21066             this.markInvalid(this.errors.PwdBadChar);
21067             this.errorMsg = this.errors.PwdBadChar;
21068             return false;
21069         }
21070         if (value.length < 6) {
21071             this.markInvalid(this.errors.PwdShort);
21072             this.errorMsg = this.errors.PwdShort;
21073             return false;
21074         }
21075         if (value.length > 16) {
21076             this.markInvalid(this.errors.PwdLong);
21077             this.errorMsg = this.errors.PwdLong;
21078             return false;
21079         }
21080         var strength;
21081         if (this.ClientSideStrongPassword(value)) {
21082             strength = 3;
21083         } else if (this.ClientSideMediumPassword(value)) {
21084             strength = 2;
21085         } else if (this.ClientSideWeakPassword(value)) {
21086             strength = 1;
21087         } else {
21088             strength = 0;
21089         }
21090
21091         
21092         if (strength < 2) {
21093             //this.markInvalid(this.errors.TooWeak);
21094             this.errorMsg = this.errors.TooWeak;
21095             //return false;
21096         }
21097         
21098         
21099         console.log('strength2: ' + strength);
21100         
21101         //var pm = this.trigger.child('div/div/div').dom;
21102         
21103         var pm = this.trigger.child('div/div');
21104         pm.removeClass(this.meterClass);
21105         pm.addClass(this.meterClass[strength]);
21106                 
21107         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21108                 
21109         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21110         
21111         this.errorMsg = ''; 
21112         return true;
21113     },
21114     // private
21115     CharacterSetChecks: function (type)
21116     {
21117         this.type = type;
21118         this.fResult = false;
21119     },
21120     // private
21121     isctype: function (character, type)
21122     {
21123         switch (type) {  
21124             case this.kCapitalLetter:
21125                 if (character >= 'A' && character <= 'Z') {
21126                     return true;
21127                 }
21128                 break;
21129             
21130             case this.kSmallLetter:
21131                 if (character >= 'a' && character <= 'z') {
21132                     return true;
21133                 }
21134                 break;
21135             
21136             case this.kDigit:
21137                 if (character >= '0' && character <= '9') {
21138                     return true;
21139                 }
21140                 break;
21141             
21142             case this.kPunctuation:
21143                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21144                     return true;
21145                 }
21146                 break;
21147             
21148             default:
21149                 return false;
21150         }
21151
21152     },
21153     // private
21154     IsLongEnough: function (pwd, size)
21155     {
21156         return !(pwd == null || isNaN(size) || pwd.length < size);
21157     },
21158     // private
21159     SpansEnoughCharacterSets: function (word, nb)
21160     {
21161         if (!this.IsLongEnough(word, nb))
21162         {
21163             return false;
21164         }
21165
21166         var characterSetChecks = new Array(
21167             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21168             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21169         );
21170         
21171         for (var index = 0; index < word.length; ++index) {
21172             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21173                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21174                     characterSetChecks[nCharSet].fResult = true;
21175                     break;
21176                 }
21177             }
21178         }
21179
21180         var nCharSets = 0;
21181         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21182             if (characterSetChecks[nCharSet].fResult) {
21183                 ++nCharSets;
21184             }
21185         }
21186
21187         if (nCharSets < nb) {
21188             return false;
21189         }
21190         return true;
21191     },
21192     // private
21193     ClientSideStrongPassword: function (pwd)
21194     {
21195         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21196     },
21197     // private
21198     ClientSideMediumPassword: function (pwd)
21199     {
21200         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21201     },
21202     // private
21203     ClientSideWeakPassword: function (pwd)
21204     {
21205         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21206     }
21207           
21208 })//<script type="text/javascript">
21209
21210 /*
21211  * Based  Ext JS Library 1.1.1
21212  * Copyright(c) 2006-2007, Ext JS, LLC.
21213  * LGPL
21214  *
21215  */
21216  
21217 /**
21218  * @class Roo.HtmlEditorCore
21219  * @extends Roo.Component
21220  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21221  *
21222  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21223  */
21224
21225 Roo.HtmlEditorCore = function(config){
21226     
21227     
21228     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21229     
21230     
21231     this.addEvents({
21232         /**
21233          * @event initialize
21234          * Fires when the editor is fully initialized (including the iframe)
21235          * @param {Roo.HtmlEditorCore} this
21236          */
21237         initialize: true,
21238         /**
21239          * @event activate
21240          * Fires when the editor is first receives the focus. Any insertion must wait
21241          * until after this event.
21242          * @param {Roo.HtmlEditorCore} this
21243          */
21244         activate: true,
21245          /**
21246          * @event beforesync
21247          * Fires before the textarea is updated with content from the editor iframe. Return false
21248          * to cancel the sync.
21249          * @param {Roo.HtmlEditorCore} this
21250          * @param {String} html
21251          */
21252         beforesync: true,
21253          /**
21254          * @event beforepush
21255          * Fires before the iframe editor is updated with content from the textarea. Return false
21256          * to cancel the push.
21257          * @param {Roo.HtmlEditorCore} this
21258          * @param {String} html
21259          */
21260         beforepush: true,
21261          /**
21262          * @event sync
21263          * Fires when the textarea is updated with content from the editor iframe.
21264          * @param {Roo.HtmlEditorCore} this
21265          * @param {String} html
21266          */
21267         sync: true,
21268          /**
21269          * @event push
21270          * Fires when the iframe editor is updated with content from the textarea.
21271          * @param {Roo.HtmlEditorCore} this
21272          * @param {String} html
21273          */
21274         push: true,
21275         
21276         /**
21277          * @event editorevent
21278          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21279          * @param {Roo.HtmlEditorCore} this
21280          */
21281         editorevent: true
21282         
21283     });
21284     
21285     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21286     
21287     // defaults : white / black...
21288     this.applyBlacklists();
21289     
21290     
21291     
21292 };
21293
21294
21295 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21296
21297
21298      /**
21299      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21300      */
21301     
21302     owner : false,
21303     
21304      /**
21305      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21306      *                        Roo.resizable.
21307      */
21308     resizable : false,
21309      /**
21310      * @cfg {Number} height (in pixels)
21311      */   
21312     height: 300,
21313    /**
21314      * @cfg {Number} width (in pixels)
21315      */   
21316     width: 500,
21317     
21318     /**
21319      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21320      * 
21321      */
21322     stylesheets: false,
21323     
21324     // id of frame..
21325     frameId: false,
21326     
21327     // private properties
21328     validationEvent : false,
21329     deferHeight: true,
21330     initialized : false,
21331     activated : false,
21332     sourceEditMode : false,
21333     onFocus : Roo.emptyFn,
21334     iframePad:3,
21335     hideMode:'offsets',
21336     
21337     clearUp: true,
21338     
21339     // blacklist + whitelisted elements..
21340     black: false,
21341     white: false,
21342      
21343     bodyCls : '',
21344
21345     /**
21346      * Protected method that will not generally be called directly. It
21347      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21348      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21349      */
21350     getDocMarkup : function(){
21351         // body styles..
21352         var st = '';
21353         
21354         // inherit styels from page...?? 
21355         if (this.stylesheets === false) {
21356             
21357             Roo.get(document.head).select('style').each(function(node) {
21358                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21359             });
21360             
21361             Roo.get(document.head).select('link').each(function(node) { 
21362                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21363             });
21364             
21365         } else if (!this.stylesheets.length) {
21366                 // simple..
21367                 st = '<style type="text/css">' +
21368                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21369                    '</style>';
21370         } else { 
21371             st = '<style type="text/css">' +
21372                     this.stylesheets +
21373                 '</style>';
21374         }
21375         
21376         st +=  '<style type="text/css">' +
21377             'IMG { cursor: pointer } ' +
21378         '</style>';
21379
21380         var cls = 'roo-htmleditor-body';
21381         
21382         if(this.bodyCls.length){
21383             cls += ' ' + this.bodyCls;
21384         }
21385         
21386         return '<html><head>' + st  +
21387             //<style type="text/css">' +
21388             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21389             //'</style>' +
21390             ' </head><body class="' +  cls + '"></body></html>';
21391     },
21392
21393     // private
21394     onRender : function(ct, position)
21395     {
21396         var _t = this;
21397         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21398         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21399         
21400         
21401         this.el.dom.style.border = '0 none';
21402         this.el.dom.setAttribute('tabIndex', -1);
21403         this.el.addClass('x-hidden hide');
21404         
21405         
21406         
21407         if(Roo.isIE){ // fix IE 1px bogus margin
21408             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21409         }
21410        
21411         
21412         this.frameId = Roo.id();
21413         
21414          
21415         
21416         var iframe = this.owner.wrap.createChild({
21417             tag: 'iframe',
21418             cls: 'form-control', // bootstrap..
21419             id: this.frameId,
21420             name: this.frameId,
21421             frameBorder : 'no',
21422             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21423         }, this.el
21424         );
21425         
21426         
21427         this.iframe = iframe.dom;
21428
21429          this.assignDocWin();
21430         
21431         this.doc.designMode = 'on';
21432        
21433         this.doc.open();
21434         this.doc.write(this.getDocMarkup());
21435         this.doc.close();
21436
21437         
21438         var task = { // must defer to wait for browser to be ready
21439             run : function(){
21440                 //console.log("run task?" + this.doc.readyState);
21441                 this.assignDocWin();
21442                 if(this.doc.body || this.doc.readyState == 'complete'){
21443                     try {
21444                         this.doc.designMode="on";
21445                     } catch (e) {
21446                         return;
21447                     }
21448                     Roo.TaskMgr.stop(task);
21449                     this.initEditor.defer(10, this);
21450                 }
21451             },
21452             interval : 10,
21453             duration: 10000,
21454             scope: this
21455         };
21456         Roo.TaskMgr.start(task);
21457
21458     },
21459
21460     // private
21461     onResize : function(w, h)
21462     {
21463          Roo.log('resize: ' +w + ',' + h );
21464         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21465         if(!this.iframe){
21466             return;
21467         }
21468         if(typeof w == 'number'){
21469             
21470             this.iframe.style.width = w + 'px';
21471         }
21472         if(typeof h == 'number'){
21473             
21474             this.iframe.style.height = h + 'px';
21475             if(this.doc){
21476                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21477             }
21478         }
21479         
21480     },
21481
21482     /**
21483      * Toggles the editor between standard and source edit mode.
21484      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21485      */
21486     toggleSourceEdit : function(sourceEditMode){
21487         
21488         this.sourceEditMode = sourceEditMode === true;
21489         
21490         if(this.sourceEditMode){
21491  
21492             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21493             
21494         }else{
21495             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21496             //this.iframe.className = '';
21497             this.deferFocus();
21498         }
21499         //this.setSize(this.owner.wrap.getSize());
21500         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21501     },
21502
21503     
21504   
21505
21506     /**
21507      * Protected method that will not generally be called directly. If you need/want
21508      * custom HTML cleanup, this is the method you should override.
21509      * @param {String} html The HTML to be cleaned
21510      * return {String} The cleaned HTML
21511      */
21512     cleanHtml : function(html){
21513         html = String(html);
21514         if(html.length > 5){
21515             if(Roo.isSafari){ // strip safari nonsense
21516                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21517             }
21518         }
21519         if(html == '&nbsp;'){
21520             html = '';
21521         }
21522         return html;
21523     },
21524
21525     /**
21526      * HTML Editor -> Textarea
21527      * Protected method that will not generally be called directly. Syncs the contents
21528      * of the editor iframe with the textarea.
21529      */
21530     syncValue : function(){
21531         if(this.initialized){
21532             var bd = (this.doc.body || this.doc.documentElement);
21533             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21534             var html = bd.innerHTML;
21535             if(Roo.isSafari){
21536                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21537                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21538                 if(m && m[1]){
21539                     html = '<div style="'+m[0]+'">' + html + '</div>';
21540                 }
21541             }
21542             html = this.cleanHtml(html);
21543             // fix up the special chars.. normaly like back quotes in word...
21544             // however we do not want to do this with chinese..
21545             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21546                 var cc = b.charCodeAt();
21547                 if (
21548                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21549                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21550                     (cc >= 0xf900 && cc < 0xfb00 )
21551                 ) {
21552                         return b;
21553                 }
21554                 return "&#"+cc+";" 
21555             });
21556             if(this.owner.fireEvent('beforesync', this, html) !== false){
21557                 this.el.dom.value = html;
21558                 this.owner.fireEvent('sync', this, html);
21559             }
21560         }
21561     },
21562
21563     /**
21564      * Protected method that will not generally be called directly. Pushes the value of the textarea
21565      * into the iframe editor.
21566      */
21567     pushValue : function(){
21568         if(this.initialized){
21569             var v = this.el.dom.value.trim();
21570             
21571 //            if(v.length < 1){
21572 //                v = '&#160;';
21573 //            }
21574             
21575             if(this.owner.fireEvent('beforepush', this, v) !== false){
21576                 var d = (this.doc.body || this.doc.documentElement);
21577                 d.innerHTML = v;
21578                 this.cleanUpPaste();
21579                 this.el.dom.value = d.innerHTML;
21580                 this.owner.fireEvent('push', this, v);
21581             }
21582         }
21583     },
21584
21585     // private
21586     deferFocus : function(){
21587         this.focus.defer(10, this);
21588     },
21589
21590     // doc'ed in Field
21591     focus : function(){
21592         if(this.win && !this.sourceEditMode){
21593             this.win.focus();
21594         }else{
21595             this.el.focus();
21596         }
21597     },
21598     
21599     assignDocWin: function()
21600     {
21601         var iframe = this.iframe;
21602         
21603          if(Roo.isIE){
21604             this.doc = iframe.contentWindow.document;
21605             this.win = iframe.contentWindow;
21606         } else {
21607 //            if (!Roo.get(this.frameId)) {
21608 //                return;
21609 //            }
21610 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21611 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21612             
21613             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21614                 return;
21615             }
21616             
21617             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21618             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21619         }
21620     },
21621     
21622     // private
21623     initEditor : function(){
21624         //console.log("INIT EDITOR");
21625         this.assignDocWin();
21626         
21627         
21628         
21629         this.doc.designMode="on";
21630         this.doc.open();
21631         this.doc.write(this.getDocMarkup());
21632         this.doc.close();
21633         
21634         var dbody = (this.doc.body || this.doc.documentElement);
21635         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21636         // this copies styles from the containing element into thsi one..
21637         // not sure why we need all of this..
21638         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21639         
21640         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21641         //ss['background-attachment'] = 'fixed'; // w3c
21642         dbody.bgProperties = 'fixed'; // ie
21643         //Roo.DomHelper.applyStyles(dbody, ss);
21644         Roo.EventManager.on(this.doc, {
21645             //'mousedown': this.onEditorEvent,
21646             'mouseup': this.onEditorEvent,
21647             'dblclick': this.onEditorEvent,
21648             'click': this.onEditorEvent,
21649             'keyup': this.onEditorEvent,
21650             buffer:100,
21651             scope: this
21652         });
21653         if(Roo.isGecko){
21654             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21655         }
21656         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21657             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21658         }
21659         this.initialized = true;
21660
21661         this.owner.fireEvent('initialize', this);
21662         this.pushValue();
21663     },
21664
21665     // private
21666     onDestroy : function(){
21667         
21668         
21669         
21670         if(this.rendered){
21671             
21672             //for (var i =0; i < this.toolbars.length;i++) {
21673             //    // fixme - ask toolbars for heights?
21674             //    this.toolbars[i].onDestroy();
21675            // }
21676             
21677             //this.wrap.dom.innerHTML = '';
21678             //this.wrap.remove();
21679         }
21680     },
21681
21682     // private
21683     onFirstFocus : function(){
21684         
21685         this.assignDocWin();
21686         
21687         
21688         this.activated = true;
21689          
21690     
21691         if(Roo.isGecko){ // prevent silly gecko errors
21692             this.win.focus();
21693             var s = this.win.getSelection();
21694             if(!s.focusNode || s.focusNode.nodeType != 3){
21695                 var r = s.getRangeAt(0);
21696                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21697                 r.collapse(true);
21698                 this.deferFocus();
21699             }
21700             try{
21701                 this.execCmd('useCSS', true);
21702                 this.execCmd('styleWithCSS', false);
21703             }catch(e){}
21704         }
21705         this.owner.fireEvent('activate', this);
21706     },
21707
21708     // private
21709     adjustFont: function(btn){
21710         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21711         //if(Roo.isSafari){ // safari
21712         //    adjust *= 2;
21713        // }
21714         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21715         if(Roo.isSafari){ // safari
21716             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21717             v =  (v < 10) ? 10 : v;
21718             v =  (v > 48) ? 48 : v;
21719             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21720             
21721         }
21722         
21723         
21724         v = Math.max(1, v+adjust);
21725         
21726         this.execCmd('FontSize', v  );
21727     },
21728
21729     onEditorEvent : function(e)
21730     {
21731         this.owner.fireEvent('editorevent', this, e);
21732       //  this.updateToolbar();
21733         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21734     },
21735
21736     insertTag : function(tg)
21737     {
21738         // could be a bit smarter... -> wrap the current selected tRoo..
21739         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21740             
21741             range = this.createRange(this.getSelection());
21742             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21743             wrappingNode.appendChild(range.extractContents());
21744             range.insertNode(wrappingNode);
21745
21746             return;
21747             
21748             
21749             
21750         }
21751         this.execCmd("formatblock",   tg);
21752         
21753     },
21754     
21755     insertText : function(txt)
21756     {
21757         
21758         
21759         var range = this.createRange();
21760         range.deleteContents();
21761                //alert(Sender.getAttribute('label'));
21762                
21763         range.insertNode(this.doc.createTextNode(txt));
21764     } ,
21765     
21766      
21767
21768     /**
21769      * Executes a Midas editor command on the editor document and performs necessary focus and
21770      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21771      * @param {String} cmd The Midas command
21772      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21773      */
21774     relayCmd : function(cmd, value){
21775         this.win.focus();
21776         this.execCmd(cmd, value);
21777         this.owner.fireEvent('editorevent', this);
21778         //this.updateToolbar();
21779         this.owner.deferFocus();
21780     },
21781
21782     /**
21783      * Executes a Midas editor command directly on the editor document.
21784      * For visual commands, you should use {@link #relayCmd} instead.
21785      * <b>This should only be called after the editor is initialized.</b>
21786      * @param {String} cmd The Midas command
21787      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21788      */
21789     execCmd : function(cmd, value){
21790         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21791         this.syncValue();
21792     },
21793  
21794  
21795    
21796     /**
21797      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21798      * to insert tRoo.
21799      * @param {String} text | dom node.. 
21800      */
21801     insertAtCursor : function(text)
21802     {
21803         
21804         if(!this.activated){
21805             return;
21806         }
21807         /*
21808         if(Roo.isIE){
21809             this.win.focus();
21810             var r = this.doc.selection.createRange();
21811             if(r){
21812                 r.collapse(true);
21813                 r.pasteHTML(text);
21814                 this.syncValue();
21815                 this.deferFocus();
21816             
21817             }
21818             return;
21819         }
21820         */
21821         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21822             this.win.focus();
21823             
21824             
21825             // from jquery ui (MIT licenced)
21826             var range, node;
21827             var win = this.win;
21828             
21829             if (win.getSelection && win.getSelection().getRangeAt) {
21830                 range = win.getSelection().getRangeAt(0);
21831                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21832                 range.insertNode(node);
21833             } else if (win.document.selection && win.document.selection.createRange) {
21834                 // no firefox support
21835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21836                 win.document.selection.createRange().pasteHTML(txt);
21837             } else {
21838                 // no firefox support
21839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21840                 this.execCmd('InsertHTML', txt);
21841             } 
21842             
21843             this.syncValue();
21844             
21845             this.deferFocus();
21846         }
21847     },
21848  // private
21849     mozKeyPress : function(e){
21850         if(e.ctrlKey){
21851             var c = e.getCharCode(), cmd;
21852           
21853             if(c > 0){
21854                 c = String.fromCharCode(c).toLowerCase();
21855                 switch(c){
21856                     case 'b':
21857                         cmd = 'bold';
21858                         break;
21859                     case 'i':
21860                         cmd = 'italic';
21861                         break;
21862                     
21863                     case 'u':
21864                         cmd = 'underline';
21865                         break;
21866                     
21867                     case 'v':
21868                         this.cleanUpPaste.defer(100, this);
21869                         return;
21870                         
21871                 }
21872                 if(cmd){
21873                     this.win.focus();
21874                     this.execCmd(cmd);
21875                     this.deferFocus();
21876                     e.preventDefault();
21877                 }
21878                 
21879             }
21880         }
21881     },
21882
21883     // private
21884     fixKeys : function(){ // load time branching for fastest keydown performance
21885         if(Roo.isIE){
21886             return function(e){
21887                 var k = e.getKey(), r;
21888                 if(k == e.TAB){
21889                     e.stopEvent();
21890                     r = this.doc.selection.createRange();
21891                     if(r){
21892                         r.collapse(true);
21893                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21894                         this.deferFocus();
21895                     }
21896                     return;
21897                 }
21898                 
21899                 if(k == e.ENTER){
21900                     r = this.doc.selection.createRange();
21901                     if(r){
21902                         var target = r.parentElement();
21903                         if(!target || target.tagName.toLowerCase() != 'li'){
21904                             e.stopEvent();
21905                             r.pasteHTML('<br />');
21906                             r.collapse(false);
21907                             r.select();
21908                         }
21909                     }
21910                 }
21911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21912                     this.cleanUpPaste.defer(100, this);
21913                     return;
21914                 }
21915                 
21916                 
21917             };
21918         }else if(Roo.isOpera){
21919             return function(e){
21920                 var k = e.getKey();
21921                 if(k == e.TAB){
21922                     e.stopEvent();
21923                     this.win.focus();
21924                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21925                     this.deferFocus();
21926                 }
21927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21928                     this.cleanUpPaste.defer(100, this);
21929                     return;
21930                 }
21931                 
21932             };
21933         }else if(Roo.isSafari){
21934             return function(e){
21935                 var k = e.getKey();
21936                 
21937                 if(k == e.TAB){
21938                     e.stopEvent();
21939                     this.execCmd('InsertText','\t');
21940                     this.deferFocus();
21941                     return;
21942                 }
21943                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21944                     this.cleanUpPaste.defer(100, this);
21945                     return;
21946                 }
21947                 
21948              };
21949         }
21950     }(),
21951     
21952     getAllAncestors: function()
21953     {
21954         var p = this.getSelectedNode();
21955         var a = [];
21956         if (!p) {
21957             a.push(p); // push blank onto stack..
21958             p = this.getParentElement();
21959         }
21960         
21961         
21962         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21963             a.push(p);
21964             p = p.parentNode;
21965         }
21966         a.push(this.doc.body);
21967         return a;
21968     },
21969     lastSel : false,
21970     lastSelNode : false,
21971     
21972     
21973     getSelection : function() 
21974     {
21975         this.assignDocWin();
21976         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21977     },
21978     
21979     getSelectedNode: function() 
21980     {
21981         // this may only work on Gecko!!!
21982         
21983         // should we cache this!!!!
21984         
21985         
21986         
21987          
21988         var range = this.createRange(this.getSelection()).cloneRange();
21989         
21990         if (Roo.isIE) {
21991             var parent = range.parentElement();
21992             while (true) {
21993                 var testRange = range.duplicate();
21994                 testRange.moveToElementText(parent);
21995                 if (testRange.inRange(range)) {
21996                     break;
21997                 }
21998                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21999                     break;
22000                 }
22001                 parent = parent.parentElement;
22002             }
22003             return parent;
22004         }
22005         
22006         // is ancestor a text element.
22007         var ac =  range.commonAncestorContainer;
22008         if (ac.nodeType == 3) {
22009             ac = ac.parentNode;
22010         }
22011         
22012         var ar = ac.childNodes;
22013          
22014         var nodes = [];
22015         var other_nodes = [];
22016         var has_other_nodes = false;
22017         for (var i=0;i<ar.length;i++) {
22018             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22019                 continue;
22020             }
22021             // fullly contained node.
22022             
22023             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22024                 nodes.push(ar[i]);
22025                 continue;
22026             }
22027             
22028             // probably selected..
22029             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22030                 other_nodes.push(ar[i]);
22031                 continue;
22032             }
22033             // outer..
22034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22035                 continue;
22036             }
22037             
22038             
22039             has_other_nodes = true;
22040         }
22041         if (!nodes.length && other_nodes.length) {
22042             nodes= other_nodes;
22043         }
22044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22045             return false;
22046         }
22047         
22048         return nodes[0];
22049     },
22050     createRange: function(sel)
22051     {
22052         // this has strange effects when using with 
22053         // top toolbar - not sure if it's a great idea.
22054         //this.editor.contentWindow.focus();
22055         if (typeof sel != "undefined") {
22056             try {
22057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22058             } catch(e) {
22059                 return this.doc.createRange();
22060             }
22061         } else {
22062             return this.doc.createRange();
22063         }
22064     },
22065     getParentElement: function()
22066     {
22067         
22068         this.assignDocWin();
22069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22070         
22071         var range = this.createRange(sel);
22072          
22073         try {
22074             var p = range.commonAncestorContainer;
22075             while (p.nodeType == 3) { // text node
22076                 p = p.parentNode;
22077             }
22078             return p;
22079         } catch (e) {
22080             return null;
22081         }
22082     
22083     },
22084     /***
22085      *
22086      * Range intersection.. the hard stuff...
22087      *  '-1' = before
22088      *  '0' = hits..
22089      *  '1' = after.
22090      *         [ -- selected range --- ]
22091      *   [fail]                        [fail]
22092      *
22093      *    basically..
22094      *      if end is before start or  hits it. fail.
22095      *      if start is after end or hits it fail.
22096      *
22097      *   if either hits (but other is outside. - then it's not 
22098      *   
22099      *    
22100      **/
22101     
22102     
22103     // @see http://www.thismuchiknow.co.uk/?p=64.
22104     rangeIntersectsNode : function(range, node)
22105     {
22106         var nodeRange = node.ownerDocument.createRange();
22107         try {
22108             nodeRange.selectNode(node);
22109         } catch (e) {
22110             nodeRange.selectNodeContents(node);
22111         }
22112     
22113         var rangeStartRange = range.cloneRange();
22114         rangeStartRange.collapse(true);
22115     
22116         var rangeEndRange = range.cloneRange();
22117         rangeEndRange.collapse(false);
22118     
22119         var nodeStartRange = nodeRange.cloneRange();
22120         nodeStartRange.collapse(true);
22121     
22122         var nodeEndRange = nodeRange.cloneRange();
22123         nodeEndRange.collapse(false);
22124     
22125         return rangeStartRange.compareBoundaryPoints(
22126                  Range.START_TO_START, nodeEndRange) == -1 &&
22127                rangeEndRange.compareBoundaryPoints(
22128                  Range.START_TO_START, nodeStartRange) == 1;
22129         
22130          
22131     },
22132     rangeCompareNode : function(range, node)
22133     {
22134         var nodeRange = node.ownerDocument.createRange();
22135         try {
22136             nodeRange.selectNode(node);
22137         } catch (e) {
22138             nodeRange.selectNodeContents(node);
22139         }
22140         
22141         
22142         range.collapse(true);
22143     
22144         nodeRange.collapse(true);
22145      
22146         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22147         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22148          
22149         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22150         
22151         var nodeIsBefore   =  ss == 1;
22152         var nodeIsAfter    = ee == -1;
22153         
22154         if (nodeIsBefore && nodeIsAfter) {
22155             return 0; // outer
22156         }
22157         if (!nodeIsBefore && nodeIsAfter) {
22158             return 1; //right trailed.
22159         }
22160         
22161         if (nodeIsBefore && !nodeIsAfter) {
22162             return 2;  // left trailed.
22163         }
22164         // fully contined.
22165         return 3;
22166     },
22167
22168     // private? - in a new class?
22169     cleanUpPaste :  function()
22170     {
22171         // cleans up the whole document..
22172         Roo.log('cleanuppaste');
22173         
22174         this.cleanUpChildren(this.doc.body);
22175         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22176         if (clean != this.doc.body.innerHTML) {
22177             this.doc.body.innerHTML = clean;
22178         }
22179         
22180     },
22181     
22182     cleanWordChars : function(input) {// change the chars to hex code
22183         var he = Roo.HtmlEditorCore;
22184         
22185         var output = input;
22186         Roo.each(he.swapCodes, function(sw) { 
22187             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22188             
22189             output = output.replace(swapper, sw[1]);
22190         });
22191         
22192         return output;
22193     },
22194     
22195     
22196     cleanUpChildren : function (n)
22197     {
22198         if (!n.childNodes.length) {
22199             return;
22200         }
22201         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22202            this.cleanUpChild(n.childNodes[i]);
22203         }
22204     },
22205     
22206     
22207         
22208     
22209     cleanUpChild : function (node)
22210     {
22211         var ed = this;
22212         //console.log(node);
22213         if (node.nodeName == "#text") {
22214             // clean up silly Windows -- stuff?
22215             return; 
22216         }
22217         if (node.nodeName == "#comment") {
22218             node.parentNode.removeChild(node);
22219             // clean up silly Windows -- stuff?
22220             return; 
22221         }
22222         var lcname = node.tagName.toLowerCase();
22223         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22224         // whitelist of tags..
22225         
22226         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22227             // remove node.
22228             node.parentNode.removeChild(node);
22229             return;
22230             
22231         }
22232         
22233         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22234         
22235         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22236         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22237         
22238         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22239         //    remove_keep_children = true;
22240         //}
22241         
22242         if (remove_keep_children) {
22243             this.cleanUpChildren(node);
22244             // inserts everything just before this node...
22245             while (node.childNodes.length) {
22246                 var cn = node.childNodes[0];
22247                 node.removeChild(cn);
22248                 node.parentNode.insertBefore(cn, node);
22249             }
22250             node.parentNode.removeChild(node);
22251             return;
22252         }
22253         
22254         if (!node.attributes || !node.attributes.length) {
22255             this.cleanUpChildren(node);
22256             return;
22257         }
22258         
22259         function cleanAttr(n,v)
22260         {
22261             
22262             if (v.match(/^\./) || v.match(/^\//)) {
22263                 return;
22264             }
22265             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22266                 return;
22267             }
22268             if (v.match(/^#/)) {
22269                 return;
22270             }
22271 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22272             node.removeAttribute(n);
22273             
22274         }
22275         
22276         var cwhite = this.cwhite;
22277         var cblack = this.cblack;
22278             
22279         function cleanStyle(n,v)
22280         {
22281             if (v.match(/expression/)) { //XSS?? should we even bother..
22282                 node.removeAttribute(n);
22283                 return;
22284             }
22285             
22286             var parts = v.split(/;/);
22287             var clean = [];
22288             
22289             Roo.each(parts, function(p) {
22290                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22291                 if (!p.length) {
22292                     return true;
22293                 }
22294                 var l = p.split(':').shift().replace(/\s+/g,'');
22295                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22296                 
22297                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22298 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22299                     //node.removeAttribute(n);
22300                     return true;
22301                 }
22302                 //Roo.log()
22303                 // only allow 'c whitelisted system attributes'
22304                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22305 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22306                     //node.removeAttribute(n);
22307                     return true;
22308                 }
22309                 
22310                 
22311                  
22312                 
22313                 clean.push(p);
22314                 return true;
22315             });
22316             if (clean.length) { 
22317                 node.setAttribute(n, clean.join(';'));
22318             } else {
22319                 node.removeAttribute(n);
22320             }
22321             
22322         }
22323         
22324         
22325         for (var i = node.attributes.length-1; i > -1 ; i--) {
22326             var a = node.attributes[i];
22327             //console.log(a);
22328             
22329             if (a.name.toLowerCase().substr(0,2)=='on')  {
22330                 node.removeAttribute(a.name);
22331                 continue;
22332             }
22333             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22334                 node.removeAttribute(a.name);
22335                 continue;
22336             }
22337             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22338                 cleanAttr(a.name,a.value); // fixme..
22339                 continue;
22340             }
22341             if (a.name == 'style') {
22342                 cleanStyle(a.name,a.value);
22343                 continue;
22344             }
22345             /// clean up MS crap..
22346             // tecnically this should be a list of valid class'es..
22347             
22348             
22349             if (a.name == 'class') {
22350                 if (a.value.match(/^Mso/)) {
22351                     node.className = '';
22352                 }
22353                 
22354                 if (a.value.match(/^body$/)) {
22355                     node.className = '';
22356                 }
22357                 continue;
22358             }
22359             
22360             // style cleanup!?
22361             // class cleanup?
22362             
22363         }
22364         
22365         
22366         this.cleanUpChildren(node);
22367         
22368         
22369     },
22370     
22371     /**
22372      * Clean up MS wordisms...
22373      */
22374     cleanWord : function(node)
22375     {
22376         
22377         
22378         if (!node) {
22379             this.cleanWord(this.doc.body);
22380             return;
22381         }
22382         if (node.nodeName == "#text") {
22383             // clean up silly Windows -- stuff?
22384             return; 
22385         }
22386         if (node.nodeName == "#comment") {
22387             node.parentNode.removeChild(node);
22388             // clean up silly Windows -- stuff?
22389             return; 
22390         }
22391         
22392         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22393             node.parentNode.removeChild(node);
22394             return;
22395         }
22396         
22397         // remove - but keep children..
22398         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22399             while (node.childNodes.length) {
22400                 var cn = node.childNodes[0];
22401                 node.removeChild(cn);
22402                 node.parentNode.insertBefore(cn, node);
22403             }
22404             node.parentNode.removeChild(node);
22405             this.iterateChildren(node, this.cleanWord);
22406             return;
22407         }
22408         // clean styles
22409         if (node.className.length) {
22410             
22411             var cn = node.className.split(/\W+/);
22412             var cna = [];
22413             Roo.each(cn, function(cls) {
22414                 if (cls.match(/Mso[a-zA-Z]+/)) {
22415                     return;
22416                 }
22417                 cna.push(cls);
22418             });
22419             node.className = cna.length ? cna.join(' ') : '';
22420             if (!cna.length) {
22421                 node.removeAttribute("class");
22422             }
22423         }
22424         
22425         if (node.hasAttribute("lang")) {
22426             node.removeAttribute("lang");
22427         }
22428         
22429         if (node.hasAttribute("style")) {
22430             
22431             var styles = node.getAttribute("style").split(";");
22432             var nstyle = [];
22433             Roo.each(styles, function(s) {
22434                 if (!s.match(/:/)) {
22435                     return;
22436                 }
22437                 var kv = s.split(":");
22438                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22439                     return;
22440                 }
22441                 // what ever is left... we allow.
22442                 nstyle.push(s);
22443             });
22444             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22445             if (!nstyle.length) {
22446                 node.removeAttribute('style');
22447             }
22448         }
22449         this.iterateChildren(node, this.cleanWord);
22450         
22451         
22452         
22453     },
22454     /**
22455      * iterateChildren of a Node, calling fn each time, using this as the scole..
22456      * @param {DomNode} node node to iterate children of.
22457      * @param {Function} fn method of this class to call on each item.
22458      */
22459     iterateChildren : function(node, fn)
22460     {
22461         if (!node.childNodes.length) {
22462                 return;
22463         }
22464         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22465            fn.call(this, node.childNodes[i])
22466         }
22467     },
22468     
22469     
22470     /**
22471      * cleanTableWidths.
22472      *
22473      * Quite often pasting from word etc.. results in tables with column and widths.
22474      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22475      *
22476      */
22477     cleanTableWidths : function(node)
22478     {
22479          
22480          
22481         if (!node) {
22482             this.cleanTableWidths(this.doc.body);
22483             return;
22484         }
22485         
22486         // ignore list...
22487         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22488             return; 
22489         }
22490         Roo.log(node.tagName);
22491         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22492             this.iterateChildren(node, this.cleanTableWidths);
22493             return;
22494         }
22495         if (node.hasAttribute('width')) {
22496             node.removeAttribute('width');
22497         }
22498         
22499          
22500         if (node.hasAttribute("style")) {
22501             // pretty basic...
22502             
22503             var styles = node.getAttribute("style").split(";");
22504             var nstyle = [];
22505             Roo.each(styles, function(s) {
22506                 if (!s.match(/:/)) {
22507                     return;
22508                 }
22509                 var kv = s.split(":");
22510                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22511                     return;
22512                 }
22513                 // what ever is left... we allow.
22514                 nstyle.push(s);
22515             });
22516             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22517             if (!nstyle.length) {
22518                 node.removeAttribute('style');
22519             }
22520         }
22521         
22522         this.iterateChildren(node, this.cleanTableWidths);
22523         
22524         
22525     },
22526     
22527     
22528     
22529     
22530     domToHTML : function(currentElement, depth, nopadtext) {
22531         
22532         depth = depth || 0;
22533         nopadtext = nopadtext || false;
22534     
22535         if (!currentElement) {
22536             return this.domToHTML(this.doc.body);
22537         }
22538         
22539         //Roo.log(currentElement);
22540         var j;
22541         var allText = false;
22542         var nodeName = currentElement.nodeName;
22543         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22544         
22545         if  (nodeName == '#text') {
22546             
22547             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22548         }
22549         
22550         
22551         var ret = '';
22552         if (nodeName != 'BODY') {
22553              
22554             var i = 0;
22555             // Prints the node tagName, such as <A>, <IMG>, etc
22556             if (tagName) {
22557                 var attr = [];
22558                 for(i = 0; i < currentElement.attributes.length;i++) {
22559                     // quoting?
22560                     var aname = currentElement.attributes.item(i).name;
22561                     if (!currentElement.attributes.item(i).value.length) {
22562                         continue;
22563                     }
22564                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22565                 }
22566                 
22567                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22568             } 
22569             else {
22570                 
22571                 // eack
22572             }
22573         } else {
22574             tagName = false;
22575         }
22576         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22577             return ret;
22578         }
22579         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22580             nopadtext = true;
22581         }
22582         
22583         
22584         // Traverse the tree
22585         i = 0;
22586         var currentElementChild = currentElement.childNodes.item(i);
22587         var allText = true;
22588         var innerHTML  = '';
22589         lastnode = '';
22590         while (currentElementChild) {
22591             // Formatting code (indent the tree so it looks nice on the screen)
22592             var nopad = nopadtext;
22593             if (lastnode == 'SPAN') {
22594                 nopad  = true;
22595             }
22596             // text
22597             if  (currentElementChild.nodeName == '#text') {
22598                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22599                 toadd = nopadtext ? toadd : toadd.trim();
22600                 if (!nopad && toadd.length > 80) {
22601                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22602                 }
22603                 innerHTML  += toadd;
22604                 
22605                 i++;
22606                 currentElementChild = currentElement.childNodes.item(i);
22607                 lastNode = '';
22608                 continue;
22609             }
22610             allText = false;
22611             
22612             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22613                 
22614             // Recursively traverse the tree structure of the child node
22615             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22616             lastnode = currentElementChild.nodeName;
22617             i++;
22618             currentElementChild=currentElement.childNodes.item(i);
22619         }
22620         
22621         ret += innerHTML;
22622         
22623         if (!allText) {
22624                 // The remaining code is mostly for formatting the tree
22625             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22626         }
22627         
22628         
22629         if (tagName) {
22630             ret+= "</"+tagName+">";
22631         }
22632         return ret;
22633         
22634     },
22635         
22636     applyBlacklists : function()
22637     {
22638         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22639         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22640         
22641         this.white = [];
22642         this.black = [];
22643         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22644             if (b.indexOf(tag) > -1) {
22645                 return;
22646             }
22647             this.white.push(tag);
22648             
22649         }, this);
22650         
22651         Roo.each(w, function(tag) {
22652             if (b.indexOf(tag) > -1) {
22653                 return;
22654             }
22655             if (this.white.indexOf(tag) > -1) {
22656                 return;
22657             }
22658             this.white.push(tag);
22659             
22660         }, this);
22661         
22662         
22663         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22664             if (w.indexOf(tag) > -1) {
22665                 return;
22666             }
22667             this.black.push(tag);
22668             
22669         }, this);
22670         
22671         Roo.each(b, function(tag) {
22672             if (w.indexOf(tag) > -1) {
22673                 return;
22674             }
22675             if (this.black.indexOf(tag) > -1) {
22676                 return;
22677             }
22678             this.black.push(tag);
22679             
22680         }, this);
22681         
22682         
22683         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22684         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22685         
22686         this.cwhite = [];
22687         this.cblack = [];
22688         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22689             if (b.indexOf(tag) > -1) {
22690                 return;
22691             }
22692             this.cwhite.push(tag);
22693             
22694         }, this);
22695         
22696         Roo.each(w, function(tag) {
22697             if (b.indexOf(tag) > -1) {
22698                 return;
22699             }
22700             if (this.cwhite.indexOf(tag) > -1) {
22701                 return;
22702             }
22703             this.cwhite.push(tag);
22704             
22705         }, this);
22706         
22707         
22708         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22709             if (w.indexOf(tag) > -1) {
22710                 return;
22711             }
22712             this.cblack.push(tag);
22713             
22714         }, this);
22715         
22716         Roo.each(b, function(tag) {
22717             if (w.indexOf(tag) > -1) {
22718                 return;
22719             }
22720             if (this.cblack.indexOf(tag) > -1) {
22721                 return;
22722             }
22723             this.cblack.push(tag);
22724             
22725         }, this);
22726     },
22727     
22728     setStylesheets : function(stylesheets)
22729     {
22730         if(typeof(stylesheets) == 'string'){
22731             Roo.get(this.iframe.contentDocument.head).createChild({
22732                 tag : 'link',
22733                 rel : 'stylesheet',
22734                 type : 'text/css',
22735                 href : stylesheets
22736             });
22737             
22738             return;
22739         }
22740         var _this = this;
22741      
22742         Roo.each(stylesheets, function(s) {
22743             if(!s.length){
22744                 return;
22745             }
22746             
22747             Roo.get(_this.iframe.contentDocument.head).createChild({
22748                 tag : 'link',
22749                 rel : 'stylesheet',
22750                 type : 'text/css',
22751                 href : s
22752             });
22753         });
22754
22755         
22756     },
22757     
22758     removeStylesheets : function()
22759     {
22760         var _this = this;
22761         
22762         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22763             s.remove();
22764         });
22765     },
22766     
22767     setStyle : function(style)
22768     {
22769         Roo.get(this.iframe.contentDocument.head).createChild({
22770             tag : 'style',
22771             type : 'text/css',
22772             html : style
22773         });
22774
22775         return;
22776     }
22777     
22778     // hide stuff that is not compatible
22779     /**
22780      * @event blur
22781      * @hide
22782      */
22783     /**
22784      * @event change
22785      * @hide
22786      */
22787     /**
22788      * @event focus
22789      * @hide
22790      */
22791     /**
22792      * @event specialkey
22793      * @hide
22794      */
22795     /**
22796      * @cfg {String} fieldClass @hide
22797      */
22798     /**
22799      * @cfg {String} focusClass @hide
22800      */
22801     /**
22802      * @cfg {String} autoCreate @hide
22803      */
22804     /**
22805      * @cfg {String} inputType @hide
22806      */
22807     /**
22808      * @cfg {String} invalidClass @hide
22809      */
22810     /**
22811      * @cfg {String} invalidText @hide
22812      */
22813     /**
22814      * @cfg {String} msgFx @hide
22815      */
22816     /**
22817      * @cfg {String} validateOnBlur @hide
22818      */
22819 });
22820
22821 Roo.HtmlEditorCore.white = [
22822         'area', 'br', 'img', 'input', 'hr', 'wbr',
22823         
22824        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22825        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22826        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22827        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22828        'table',   'ul',         'xmp', 
22829        
22830        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22831       'thead',   'tr', 
22832      
22833       'dir', 'menu', 'ol', 'ul', 'dl',
22834        
22835       'embed',  'object'
22836 ];
22837
22838
22839 Roo.HtmlEditorCore.black = [
22840     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22841         'applet', // 
22842         'base',   'basefont', 'bgsound', 'blink',  'body', 
22843         'frame',  'frameset', 'head',    'html',   'ilayer', 
22844         'iframe', 'layer',  'link',     'meta',    'object',   
22845         'script', 'style' ,'title',  'xml' // clean later..
22846 ];
22847 Roo.HtmlEditorCore.clean = [
22848     'script', 'style', 'title', 'xml'
22849 ];
22850 Roo.HtmlEditorCore.remove = [
22851     'font'
22852 ];
22853 // attributes..
22854
22855 Roo.HtmlEditorCore.ablack = [
22856     'on'
22857 ];
22858     
22859 Roo.HtmlEditorCore.aclean = [ 
22860     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22861 ];
22862
22863 // protocols..
22864 Roo.HtmlEditorCore.pwhite= [
22865         'http',  'https',  'mailto'
22866 ];
22867
22868 // white listed style attributes.
22869 Roo.HtmlEditorCore.cwhite= [
22870       //  'text-align', /// default is to allow most things..
22871       
22872          
22873 //        'font-size'//??
22874 ];
22875
22876 // black listed style attributes.
22877 Roo.HtmlEditorCore.cblack= [
22878       //  'font-size' -- this can be set by the project 
22879 ];
22880
22881
22882 Roo.HtmlEditorCore.swapCodes   =[ 
22883     [    8211, "--" ], 
22884     [    8212, "--" ], 
22885     [    8216,  "'" ],  
22886     [    8217, "'" ],  
22887     [    8220, '"' ],  
22888     [    8221, '"' ],  
22889     [    8226, "*" ],  
22890     [    8230, "..." ]
22891 ]; 
22892
22893     /*
22894  * - LGPL
22895  *
22896  * HtmlEditor
22897  * 
22898  */
22899
22900 /**
22901  * @class Roo.bootstrap.HtmlEditor
22902  * @extends Roo.bootstrap.TextArea
22903  * Bootstrap HtmlEditor class
22904
22905  * @constructor
22906  * Create a new HtmlEditor
22907  * @param {Object} config The config object
22908  */
22909
22910 Roo.bootstrap.HtmlEditor = function(config){
22911     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22912     if (!this.toolbars) {
22913         this.toolbars = [];
22914     }
22915     
22916     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22917     this.addEvents({
22918             /**
22919              * @event initialize
22920              * Fires when the editor is fully initialized (including the iframe)
22921              * @param {HtmlEditor} this
22922              */
22923             initialize: true,
22924             /**
22925              * @event activate
22926              * Fires when the editor is first receives the focus. Any insertion must wait
22927              * until after this event.
22928              * @param {HtmlEditor} this
22929              */
22930             activate: true,
22931              /**
22932              * @event beforesync
22933              * Fires before the textarea is updated with content from the editor iframe. Return false
22934              * to cancel the sync.
22935              * @param {HtmlEditor} this
22936              * @param {String} html
22937              */
22938             beforesync: true,
22939              /**
22940              * @event beforepush
22941              * Fires before the iframe editor is updated with content from the textarea. Return false
22942              * to cancel the push.
22943              * @param {HtmlEditor} this
22944              * @param {String} html
22945              */
22946             beforepush: true,
22947              /**
22948              * @event sync
22949              * Fires when the textarea is updated with content from the editor iframe.
22950              * @param {HtmlEditor} this
22951              * @param {String} html
22952              */
22953             sync: true,
22954              /**
22955              * @event push
22956              * Fires when the iframe editor is updated with content from the textarea.
22957              * @param {HtmlEditor} this
22958              * @param {String} html
22959              */
22960             push: true,
22961              /**
22962              * @event editmodechange
22963              * Fires when the editor switches edit modes
22964              * @param {HtmlEditor} this
22965              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22966              */
22967             editmodechange: true,
22968             /**
22969              * @event editorevent
22970              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22971              * @param {HtmlEditor} this
22972              */
22973             editorevent: true,
22974             /**
22975              * @event firstfocus
22976              * Fires when on first focus - needed by toolbars..
22977              * @param {HtmlEditor} this
22978              */
22979             firstfocus: true,
22980             /**
22981              * @event autosave
22982              * Auto save the htmlEditor value as a file into Events
22983              * @param {HtmlEditor} this
22984              */
22985             autosave: true,
22986             /**
22987              * @event savedpreview
22988              * preview the saved version of htmlEditor
22989              * @param {HtmlEditor} this
22990              */
22991             savedpreview: true
22992         });
22993 };
22994
22995
22996 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22997     
22998     
22999       /**
23000      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23001      */
23002     toolbars : false,
23003     
23004      /**
23005     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23006     */
23007     btns : [],
23008    
23009      /**
23010      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23011      *                        Roo.resizable.
23012      */
23013     resizable : false,
23014      /**
23015      * @cfg {Number} height (in pixels)
23016      */   
23017     height: 300,
23018    /**
23019      * @cfg {Number} width (in pixels)
23020      */   
23021     width: false,
23022     
23023     /**
23024      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23025      * 
23026      */
23027     stylesheets: false,
23028     
23029     // id of frame..
23030     frameId: false,
23031     
23032     // private properties
23033     validationEvent : false,
23034     deferHeight: true,
23035     initialized : false,
23036     activated : false,
23037     
23038     onFocus : Roo.emptyFn,
23039     iframePad:3,
23040     hideMode:'offsets',
23041     
23042     tbContainer : false,
23043     
23044     bodyCls : '',
23045     
23046     toolbarContainer :function() {
23047         return this.wrap.select('.x-html-editor-tb',true).first();
23048     },
23049
23050     /**
23051      * Protected method that will not generally be called directly. It
23052      * is called when the editor creates its toolbar. Override this method if you need to
23053      * add custom toolbar buttons.
23054      * @param {HtmlEditor} editor
23055      */
23056     createToolbar : function(){
23057         Roo.log('renewing');
23058         Roo.log("create toolbars");
23059         
23060         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23061         this.toolbars[0].render(this.toolbarContainer());
23062         
23063         return;
23064         
23065 //        if (!editor.toolbars || !editor.toolbars.length) {
23066 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23067 //        }
23068 //        
23069 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23070 //            editor.toolbars[i] = Roo.factory(
23071 //                    typeof(editor.toolbars[i]) == 'string' ?
23072 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23073 //                Roo.bootstrap.HtmlEditor);
23074 //            editor.toolbars[i].init(editor);
23075 //        }
23076     },
23077
23078      
23079     // private
23080     onRender : function(ct, position)
23081     {
23082        // Roo.log("Call onRender: " + this.xtype);
23083         var _t = this;
23084         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23085       
23086         this.wrap = this.inputEl().wrap({
23087             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23088         });
23089         
23090         this.editorcore.onRender(ct, position);
23091          
23092         if (this.resizable) {
23093             this.resizeEl = new Roo.Resizable(this.wrap, {
23094                 pinned : true,
23095                 wrap: true,
23096                 dynamic : true,
23097                 minHeight : this.height,
23098                 height: this.height,
23099                 handles : this.resizable,
23100                 width: this.width,
23101                 listeners : {
23102                     resize : function(r, w, h) {
23103                         _t.onResize(w,h); // -something
23104                     }
23105                 }
23106             });
23107             
23108         }
23109         this.createToolbar(this);
23110        
23111         
23112         if(!this.width && this.resizable){
23113             this.setSize(this.wrap.getSize());
23114         }
23115         if (this.resizeEl) {
23116             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23117             // should trigger onReize..
23118         }
23119         
23120     },
23121
23122     // private
23123     onResize : function(w, h)
23124     {
23125         Roo.log('resize: ' +w + ',' + h );
23126         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23127         var ew = false;
23128         var eh = false;
23129         
23130         if(this.inputEl() ){
23131             if(typeof w == 'number'){
23132                 var aw = w - this.wrap.getFrameWidth('lr');
23133                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23134                 ew = aw;
23135             }
23136             if(typeof h == 'number'){
23137                  var tbh = -11;  // fixme it needs to tool bar size!
23138                 for (var i =0; i < this.toolbars.length;i++) {
23139                     // fixme - ask toolbars for heights?
23140                     tbh += this.toolbars[i].el.getHeight();
23141                     //if (this.toolbars[i].footer) {
23142                     //    tbh += this.toolbars[i].footer.el.getHeight();
23143                     //}
23144                 }
23145               
23146                 
23147                 
23148                 
23149                 
23150                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23151                 ah -= 5; // knock a few pixes off for look..
23152                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23153                 var eh = ah;
23154             }
23155         }
23156         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23157         this.editorcore.onResize(ew,eh);
23158         
23159     },
23160
23161     /**
23162      * Toggles the editor between standard and source edit mode.
23163      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23164      */
23165     toggleSourceEdit : function(sourceEditMode)
23166     {
23167         this.editorcore.toggleSourceEdit(sourceEditMode);
23168         
23169         if(this.editorcore.sourceEditMode){
23170             Roo.log('editor - showing textarea');
23171             
23172 //            Roo.log('in');
23173 //            Roo.log(this.syncValue());
23174             this.syncValue();
23175             this.inputEl().removeClass(['hide', 'x-hidden']);
23176             this.inputEl().dom.removeAttribute('tabIndex');
23177             this.inputEl().focus();
23178         }else{
23179             Roo.log('editor - hiding textarea');
23180 //            Roo.log('out')
23181 //            Roo.log(this.pushValue()); 
23182             this.pushValue();
23183             
23184             this.inputEl().addClass(['hide', 'x-hidden']);
23185             this.inputEl().dom.setAttribute('tabIndex', -1);
23186             //this.deferFocus();
23187         }
23188          
23189         if(this.resizable){
23190             this.setSize(this.wrap.getSize());
23191         }
23192         
23193         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23194     },
23195  
23196     // private (for BoxComponent)
23197     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23198
23199     // private (for BoxComponent)
23200     getResizeEl : function(){
23201         return this.wrap;
23202     },
23203
23204     // private (for BoxComponent)
23205     getPositionEl : function(){
23206         return this.wrap;
23207     },
23208
23209     // private
23210     initEvents : function(){
23211         this.originalValue = this.getValue();
23212     },
23213
23214 //    /**
23215 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23216 //     * @method
23217 //     */
23218 //    markInvalid : Roo.emptyFn,
23219 //    /**
23220 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23221 //     * @method
23222 //     */
23223 //    clearInvalid : Roo.emptyFn,
23224
23225     setValue : function(v){
23226         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23227         this.editorcore.pushValue();
23228     },
23229
23230      
23231     // private
23232     deferFocus : function(){
23233         this.focus.defer(10, this);
23234     },
23235
23236     // doc'ed in Field
23237     focus : function(){
23238         this.editorcore.focus();
23239         
23240     },
23241       
23242
23243     // private
23244     onDestroy : function(){
23245         
23246         
23247         
23248         if(this.rendered){
23249             
23250             for (var i =0; i < this.toolbars.length;i++) {
23251                 // fixme - ask toolbars for heights?
23252                 this.toolbars[i].onDestroy();
23253             }
23254             
23255             this.wrap.dom.innerHTML = '';
23256             this.wrap.remove();
23257         }
23258     },
23259
23260     // private
23261     onFirstFocus : function(){
23262         //Roo.log("onFirstFocus");
23263         this.editorcore.onFirstFocus();
23264          for (var i =0; i < this.toolbars.length;i++) {
23265             this.toolbars[i].onFirstFocus();
23266         }
23267         
23268     },
23269     
23270     // private
23271     syncValue : function()
23272     {   
23273         this.editorcore.syncValue();
23274     },
23275     
23276     pushValue : function()
23277     {   
23278         this.editorcore.pushValue();
23279     }
23280      
23281     
23282     // hide stuff that is not compatible
23283     /**
23284      * @event blur
23285      * @hide
23286      */
23287     /**
23288      * @event change
23289      * @hide
23290      */
23291     /**
23292      * @event focus
23293      * @hide
23294      */
23295     /**
23296      * @event specialkey
23297      * @hide
23298      */
23299     /**
23300      * @cfg {String} fieldClass @hide
23301      */
23302     /**
23303      * @cfg {String} focusClass @hide
23304      */
23305     /**
23306      * @cfg {String} autoCreate @hide
23307      */
23308     /**
23309      * @cfg {String} inputType @hide
23310      */
23311     /**
23312      * @cfg {String} invalidClass @hide
23313      */
23314     /**
23315      * @cfg {String} invalidText @hide
23316      */
23317     /**
23318      * @cfg {String} msgFx @hide
23319      */
23320     /**
23321      * @cfg {String} validateOnBlur @hide
23322      */
23323 });
23324  
23325     
23326    
23327    
23328    
23329       
23330 Roo.namespace('Roo.bootstrap.htmleditor');
23331 /**
23332  * @class Roo.bootstrap.HtmlEditorToolbar1
23333  * Basic Toolbar
23334  * 
23335  * Usage:
23336  *
23337  new Roo.bootstrap.HtmlEditor({
23338     ....
23339     toolbars : [
23340         new Roo.bootstrap.HtmlEditorToolbar1({
23341             disable : { fonts: 1 , format: 1, ..., ... , ...],
23342             btns : [ .... ]
23343         })
23344     }
23345      
23346  * 
23347  * @cfg {Object} disable List of elements to disable..
23348  * @cfg {Array} btns List of additional buttons.
23349  * 
23350  * 
23351  * NEEDS Extra CSS? 
23352  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23353  */
23354  
23355 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23356 {
23357     
23358     Roo.apply(this, config);
23359     
23360     // default disabled, based on 'good practice'..
23361     this.disable = this.disable || {};
23362     Roo.applyIf(this.disable, {
23363         fontSize : true,
23364         colors : true,
23365         specialElements : true
23366     });
23367     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23368     
23369     this.editor = config.editor;
23370     this.editorcore = config.editor.editorcore;
23371     
23372     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23373     
23374     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23375     // dont call parent... till later.
23376 }
23377 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23378      
23379     bar : true,
23380     
23381     editor : false,
23382     editorcore : false,
23383     
23384     
23385     formats : [
23386         "p" ,  
23387         "h1","h2","h3","h4","h5","h6", 
23388         "pre", "code", 
23389         "abbr", "acronym", "address", "cite", "samp", "var",
23390         'div','span'
23391     ],
23392     
23393     onRender : function(ct, position)
23394     {
23395        // Roo.log("Call onRender: " + this.xtype);
23396         
23397        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23398        Roo.log(this.el);
23399        this.el.dom.style.marginBottom = '0';
23400        var _this = this;
23401        var editorcore = this.editorcore;
23402        var editor= this.editor;
23403        
23404        var children = [];
23405        var btn = function(id,cmd , toggle, handler, html){
23406        
23407             var  event = toggle ? 'toggle' : 'click';
23408        
23409             var a = {
23410                 size : 'sm',
23411                 xtype: 'Button',
23412                 xns: Roo.bootstrap,
23413                 glyphicon : id,
23414                 cmd : id || cmd,
23415                 enableToggle:toggle !== false,
23416                 html : html || '',
23417                 pressed : toggle ? false : null,
23418                 listeners : {}
23419             };
23420             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23421                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23422             };
23423             children.push(a);
23424             return a;
23425        }
23426        
23427     //    var cb_box = function...
23428         
23429         var style = {
23430                 xtype: 'Button',
23431                 size : 'sm',
23432                 xns: Roo.bootstrap,
23433                 glyphicon : 'font',
23434                 //html : 'submit'
23435                 menu : {
23436                     xtype: 'Menu',
23437                     xns: Roo.bootstrap,
23438                     items:  []
23439                 }
23440         };
23441         Roo.each(this.formats, function(f) {
23442             style.menu.items.push({
23443                 xtype :'MenuItem',
23444                 xns: Roo.bootstrap,
23445                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23446                 tagname : f,
23447                 listeners : {
23448                     click : function()
23449                     {
23450                         editorcore.insertTag(this.tagname);
23451                         editor.focus();
23452                     }
23453                 }
23454                 
23455             });
23456         });
23457         children.push(style);   
23458         
23459         btn('bold',false,true);
23460         btn('italic',false,true);
23461         btn('align-left', 'justifyleft',true);
23462         btn('align-center', 'justifycenter',true);
23463         btn('align-right' , 'justifyright',true);
23464         btn('link', false, false, function(btn) {
23465             //Roo.log("create link?");
23466             var url = prompt(this.createLinkText, this.defaultLinkValue);
23467             if(url && url != 'http:/'+'/'){
23468                 this.editorcore.relayCmd('createlink', url);
23469             }
23470         }),
23471         btn('list','insertunorderedlist',true);
23472         btn('pencil', false,true, function(btn){
23473                 Roo.log(this);
23474                 this.toggleSourceEdit(btn.pressed);
23475         });
23476         
23477         if (this.editor.btns.length > 0) {
23478             for (var i = 0; i<this.editor.btns.length; i++) {
23479                 children.push(this.editor.btns[i]);
23480             }
23481         }
23482         
23483         /*
23484         var cog = {
23485                 xtype: 'Button',
23486                 size : 'sm',
23487                 xns: Roo.bootstrap,
23488                 glyphicon : 'cog',
23489                 //html : 'submit'
23490                 menu : {
23491                     xtype: 'Menu',
23492                     xns: Roo.bootstrap,
23493                     items:  []
23494                 }
23495         };
23496         
23497         cog.menu.items.push({
23498             xtype :'MenuItem',
23499             xns: Roo.bootstrap,
23500             html : Clean styles,
23501             tagname : f,
23502             listeners : {
23503                 click : function()
23504                 {
23505                     editorcore.insertTag(this.tagname);
23506                     editor.focus();
23507                 }
23508             }
23509             
23510         });
23511        */
23512         
23513          
23514        this.xtype = 'NavSimplebar';
23515         
23516         for(var i=0;i< children.length;i++) {
23517             
23518             this.buttons.add(this.addxtypeChild(children[i]));
23519             
23520         }
23521         
23522         editor.on('editorevent', this.updateToolbar, this);
23523     },
23524     onBtnClick : function(id)
23525     {
23526        this.editorcore.relayCmd(id);
23527        this.editorcore.focus();
23528     },
23529     
23530     /**
23531      * Protected method that will not generally be called directly. It triggers
23532      * a toolbar update by reading the markup state of the current selection in the editor.
23533      */
23534     updateToolbar: function(){
23535
23536         if(!this.editorcore.activated){
23537             this.editor.onFirstFocus(); // is this neeed?
23538             return;
23539         }
23540
23541         var btns = this.buttons; 
23542         var doc = this.editorcore.doc;
23543         btns.get('bold').setActive(doc.queryCommandState('bold'));
23544         btns.get('italic').setActive(doc.queryCommandState('italic'));
23545         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23546         
23547         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23548         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23549         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23550         
23551         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23552         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23553          /*
23554         
23555         var ans = this.editorcore.getAllAncestors();
23556         if (this.formatCombo) {
23557             
23558             
23559             var store = this.formatCombo.store;
23560             this.formatCombo.setValue("");
23561             for (var i =0; i < ans.length;i++) {
23562                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23563                     // select it..
23564                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23565                     break;
23566                 }
23567             }
23568         }
23569         
23570         
23571         
23572         // hides menus... - so this cant be on a menu...
23573         Roo.bootstrap.MenuMgr.hideAll();
23574         */
23575         Roo.bootstrap.MenuMgr.hideAll();
23576         //this.editorsyncValue();
23577     },
23578     onFirstFocus: function() {
23579         this.buttons.each(function(item){
23580            item.enable();
23581         });
23582     },
23583     toggleSourceEdit : function(sourceEditMode){
23584         
23585           
23586         if(sourceEditMode){
23587             Roo.log("disabling buttons");
23588            this.buttons.each( function(item){
23589                 if(item.cmd != 'pencil'){
23590                     item.disable();
23591                 }
23592             });
23593           
23594         }else{
23595             Roo.log("enabling buttons");
23596             if(this.editorcore.initialized){
23597                 this.buttons.each( function(item){
23598                     item.enable();
23599                 });
23600             }
23601             
23602         }
23603         Roo.log("calling toggole on editor");
23604         // tell the editor that it's been pressed..
23605         this.editor.toggleSourceEdit(sourceEditMode);
23606        
23607     }
23608 });
23609
23610
23611
23612
23613
23614 /**
23615  * @class Roo.bootstrap.Table.AbstractSelectionModel
23616  * @extends Roo.util.Observable
23617  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23618  * implemented by descendant classes.  This class should not be directly instantiated.
23619  * @constructor
23620  */
23621 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23622     this.locked = false;
23623     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23624 };
23625
23626
23627 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23628     /** @ignore Called by the grid automatically. Do not call directly. */
23629     init : function(grid){
23630         this.grid = grid;
23631         this.initEvents();
23632     },
23633
23634     /**
23635      * Locks the selections.
23636      */
23637     lock : function(){
23638         this.locked = true;
23639     },
23640
23641     /**
23642      * Unlocks the selections.
23643      */
23644     unlock : function(){
23645         this.locked = false;
23646     },
23647
23648     /**
23649      * Returns true if the selections are locked.
23650      * @return {Boolean}
23651      */
23652     isLocked : function(){
23653         return this.locked;
23654     }
23655 });
23656 /**
23657  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23658  * @class Roo.bootstrap.Table.RowSelectionModel
23659  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23660  * It supports multiple selections and keyboard selection/navigation. 
23661  * @constructor
23662  * @param {Object} config
23663  */
23664
23665 Roo.bootstrap.Table.RowSelectionModel = function(config){
23666     Roo.apply(this, config);
23667     this.selections = new Roo.util.MixedCollection(false, function(o){
23668         return o.id;
23669     });
23670
23671     this.last = false;
23672     this.lastActive = false;
23673
23674     this.addEvents({
23675         /**
23676              * @event selectionchange
23677              * Fires when the selection changes
23678              * @param {SelectionModel} this
23679              */
23680             "selectionchange" : true,
23681         /**
23682              * @event afterselectionchange
23683              * Fires after the selection changes (eg. by key press or clicking)
23684              * @param {SelectionModel} this
23685              */
23686             "afterselectionchange" : true,
23687         /**
23688              * @event beforerowselect
23689              * Fires when a row is selected being selected, return false to cancel.
23690              * @param {SelectionModel} this
23691              * @param {Number} rowIndex The selected index
23692              * @param {Boolean} keepExisting False if other selections will be cleared
23693              */
23694             "beforerowselect" : true,
23695         /**
23696              * @event rowselect
23697              * Fires when a row is selected.
23698              * @param {SelectionModel} this
23699              * @param {Number} rowIndex The selected index
23700              * @param {Roo.data.Record} r The record
23701              */
23702             "rowselect" : true,
23703         /**
23704              * @event rowdeselect
23705              * Fires when a row is deselected.
23706              * @param {SelectionModel} this
23707              * @param {Number} rowIndex The selected index
23708              */
23709         "rowdeselect" : true
23710     });
23711     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23712     this.locked = false;
23713  };
23714
23715 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23716     /**
23717      * @cfg {Boolean} singleSelect
23718      * True to allow selection of only one row at a time (defaults to false)
23719      */
23720     singleSelect : false,
23721
23722     // private
23723     initEvents : function()
23724     {
23725
23726         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23727         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23728         //}else{ // allow click to work like normal
23729          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23730         //}
23731         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23732         this.grid.on("rowclick", this.handleMouseDown, this);
23733         
23734         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23735             "up" : function(e){
23736                 if(!e.shiftKey){
23737                     this.selectPrevious(e.shiftKey);
23738                 }else if(this.last !== false && this.lastActive !== false){
23739                     var last = this.last;
23740                     this.selectRange(this.last,  this.lastActive-1);
23741                     this.grid.getView().focusRow(this.lastActive);
23742                     if(last !== false){
23743                         this.last = last;
23744                     }
23745                 }else{
23746                     this.selectFirstRow();
23747                 }
23748                 this.fireEvent("afterselectionchange", this);
23749             },
23750             "down" : function(e){
23751                 if(!e.shiftKey){
23752                     this.selectNext(e.shiftKey);
23753                 }else if(this.last !== false && this.lastActive !== false){
23754                     var last = this.last;
23755                     this.selectRange(this.last,  this.lastActive+1);
23756                     this.grid.getView().focusRow(this.lastActive);
23757                     if(last !== false){
23758                         this.last = last;
23759                     }
23760                 }else{
23761                     this.selectFirstRow();
23762                 }
23763                 this.fireEvent("afterselectionchange", this);
23764             },
23765             scope: this
23766         });
23767         this.grid.store.on('load', function(){
23768             this.selections.clear();
23769         },this);
23770         /*
23771         var view = this.grid.view;
23772         view.on("refresh", this.onRefresh, this);
23773         view.on("rowupdated", this.onRowUpdated, this);
23774         view.on("rowremoved", this.onRemove, this);
23775         */
23776     },
23777
23778     // private
23779     onRefresh : function()
23780     {
23781         var ds = this.grid.store, i, v = this.grid.view;
23782         var s = this.selections;
23783         s.each(function(r){
23784             if((i = ds.indexOfId(r.id)) != -1){
23785                 v.onRowSelect(i);
23786             }else{
23787                 s.remove(r);
23788             }
23789         });
23790     },
23791
23792     // private
23793     onRemove : function(v, index, r){
23794         this.selections.remove(r);
23795     },
23796
23797     // private
23798     onRowUpdated : function(v, index, r){
23799         if(this.isSelected(r)){
23800             v.onRowSelect(index);
23801         }
23802     },
23803
23804     /**
23805      * Select records.
23806      * @param {Array} records The records to select
23807      * @param {Boolean} keepExisting (optional) True to keep existing selections
23808      */
23809     selectRecords : function(records, keepExisting)
23810     {
23811         if(!keepExisting){
23812             this.clearSelections();
23813         }
23814             var ds = this.grid.store;
23815         for(var i = 0, len = records.length; i < len; i++){
23816             this.selectRow(ds.indexOf(records[i]), true);
23817         }
23818     },
23819
23820     /**
23821      * Gets the number of selected rows.
23822      * @return {Number}
23823      */
23824     getCount : function(){
23825         return this.selections.length;
23826     },
23827
23828     /**
23829      * Selects the first row in the grid.
23830      */
23831     selectFirstRow : function(){
23832         this.selectRow(0);
23833     },
23834
23835     /**
23836      * Select the last row.
23837      * @param {Boolean} keepExisting (optional) True to keep existing selections
23838      */
23839     selectLastRow : function(keepExisting){
23840         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23841         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23842     },
23843
23844     /**
23845      * Selects the row immediately following the last selected row.
23846      * @param {Boolean} keepExisting (optional) True to keep existing selections
23847      */
23848     selectNext : function(keepExisting)
23849     {
23850             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23851             this.selectRow(this.last+1, keepExisting);
23852             this.grid.getView().focusRow(this.last);
23853         }
23854     },
23855
23856     /**
23857      * Selects the row that precedes the last selected row.
23858      * @param {Boolean} keepExisting (optional) True to keep existing selections
23859      */
23860     selectPrevious : function(keepExisting){
23861         if(this.last){
23862             this.selectRow(this.last-1, keepExisting);
23863             this.grid.getView().focusRow(this.last);
23864         }
23865     },
23866
23867     /**
23868      * Returns the selected records
23869      * @return {Array} Array of selected records
23870      */
23871     getSelections : function(){
23872         return [].concat(this.selections.items);
23873     },
23874
23875     /**
23876      * Returns the first selected record.
23877      * @return {Record}
23878      */
23879     getSelected : function(){
23880         return this.selections.itemAt(0);
23881     },
23882
23883
23884     /**
23885      * Clears all selections.
23886      */
23887     clearSelections : function(fast)
23888     {
23889         if(this.locked) {
23890             return;
23891         }
23892         if(fast !== true){
23893                 var ds = this.grid.store;
23894             var s = this.selections;
23895             s.each(function(r){
23896                 this.deselectRow(ds.indexOfId(r.id));
23897             }, this);
23898             s.clear();
23899         }else{
23900             this.selections.clear();
23901         }
23902         this.last = false;
23903     },
23904
23905
23906     /**
23907      * Selects all rows.
23908      */
23909     selectAll : function(){
23910         if(this.locked) {
23911             return;
23912         }
23913         this.selections.clear();
23914         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23915             this.selectRow(i, true);
23916         }
23917     },
23918
23919     /**
23920      * Returns True if there is a selection.
23921      * @return {Boolean}
23922      */
23923     hasSelection : function(){
23924         return this.selections.length > 0;
23925     },
23926
23927     /**
23928      * Returns True if the specified row is selected.
23929      * @param {Number/Record} record The record or index of the record to check
23930      * @return {Boolean}
23931      */
23932     isSelected : function(index){
23933             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23934         return (r && this.selections.key(r.id) ? true : false);
23935     },
23936
23937     /**
23938      * Returns True if the specified record id is selected.
23939      * @param {String} id The id of record to check
23940      * @return {Boolean}
23941      */
23942     isIdSelected : function(id){
23943         return (this.selections.key(id) ? true : false);
23944     },
23945
23946
23947     // private
23948     handleMouseDBClick : function(e, t){
23949         
23950     },
23951     // private
23952     handleMouseDown : function(e, t)
23953     {
23954             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23955         if(this.isLocked() || rowIndex < 0 ){
23956             return;
23957         };
23958         if(e.shiftKey && this.last !== false){
23959             var last = this.last;
23960             this.selectRange(last, rowIndex, e.ctrlKey);
23961             this.last = last; // reset the last
23962             t.focus();
23963     
23964         }else{
23965             var isSelected = this.isSelected(rowIndex);
23966             //Roo.log("select row:" + rowIndex);
23967             if(isSelected){
23968                 this.deselectRow(rowIndex);
23969             } else {
23970                         this.selectRow(rowIndex, true);
23971             }
23972     
23973             /*
23974                 if(e.button !== 0 && isSelected){
23975                 alert('rowIndex 2: ' + rowIndex);
23976                     view.focusRow(rowIndex);
23977                 }else if(e.ctrlKey && isSelected){
23978                     this.deselectRow(rowIndex);
23979                 }else if(!isSelected){
23980                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23981                     view.focusRow(rowIndex);
23982                 }
23983             */
23984         }
23985         this.fireEvent("afterselectionchange", this);
23986     },
23987     // private
23988     handleDragableRowClick :  function(grid, rowIndex, e) 
23989     {
23990         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23991             this.selectRow(rowIndex, false);
23992             grid.view.focusRow(rowIndex);
23993              this.fireEvent("afterselectionchange", this);
23994         }
23995     },
23996     
23997     /**
23998      * Selects multiple rows.
23999      * @param {Array} rows Array of the indexes of the row to select
24000      * @param {Boolean} keepExisting (optional) True to keep existing selections
24001      */
24002     selectRows : function(rows, keepExisting){
24003         if(!keepExisting){
24004             this.clearSelections();
24005         }
24006         for(var i = 0, len = rows.length; i < len; i++){
24007             this.selectRow(rows[i], true);
24008         }
24009     },
24010
24011     /**
24012      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24013      * @param {Number} startRow The index of the first row in the range
24014      * @param {Number} endRow The index of the last row in the range
24015      * @param {Boolean} keepExisting (optional) True to retain existing selections
24016      */
24017     selectRange : function(startRow, endRow, keepExisting){
24018         if(this.locked) {
24019             return;
24020         }
24021         if(!keepExisting){
24022             this.clearSelections();
24023         }
24024         if(startRow <= endRow){
24025             for(var i = startRow; i <= endRow; i++){
24026                 this.selectRow(i, true);
24027             }
24028         }else{
24029             for(var i = startRow; i >= endRow; i--){
24030                 this.selectRow(i, true);
24031             }
24032         }
24033     },
24034
24035     /**
24036      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24037      * @param {Number} startRow The index of the first row in the range
24038      * @param {Number} endRow The index of the last row in the range
24039      */
24040     deselectRange : function(startRow, endRow, preventViewNotify){
24041         if(this.locked) {
24042             return;
24043         }
24044         for(var i = startRow; i <= endRow; i++){
24045             this.deselectRow(i, preventViewNotify);
24046         }
24047     },
24048
24049     /**
24050      * Selects a row.
24051      * @param {Number} row The index of the row to select
24052      * @param {Boolean} keepExisting (optional) True to keep existing selections
24053      */
24054     selectRow : function(index, keepExisting, preventViewNotify)
24055     {
24056             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24057             return;
24058         }
24059         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24060             if(!keepExisting || this.singleSelect){
24061                 this.clearSelections();
24062             }
24063             
24064             var r = this.grid.store.getAt(index);
24065             //console.log('selectRow - record id :' + r.id);
24066             
24067             this.selections.add(r);
24068             this.last = this.lastActive = index;
24069             if(!preventViewNotify){
24070                 var proxy = new Roo.Element(
24071                                 this.grid.getRowDom(index)
24072                 );
24073                 proxy.addClass('bg-info info');
24074             }
24075             this.fireEvent("rowselect", this, index, r);
24076             this.fireEvent("selectionchange", this);
24077         }
24078     },
24079
24080     /**
24081      * Deselects a row.
24082      * @param {Number} row The index of the row to deselect
24083      */
24084     deselectRow : function(index, preventViewNotify)
24085     {
24086         if(this.locked) {
24087             return;
24088         }
24089         if(this.last == index){
24090             this.last = false;
24091         }
24092         if(this.lastActive == index){
24093             this.lastActive = false;
24094         }
24095         
24096         var r = this.grid.store.getAt(index);
24097         if (!r) {
24098             return;
24099         }
24100         
24101         this.selections.remove(r);
24102         //.console.log('deselectRow - record id :' + r.id);
24103         if(!preventViewNotify){
24104         
24105             var proxy = new Roo.Element(
24106                 this.grid.getRowDom(index)
24107             );
24108             proxy.removeClass('bg-info info');
24109         }
24110         this.fireEvent("rowdeselect", this, index);
24111         this.fireEvent("selectionchange", this);
24112     },
24113
24114     // private
24115     restoreLast : function(){
24116         if(this._last){
24117             this.last = this._last;
24118         }
24119     },
24120
24121     // private
24122     acceptsNav : function(row, col, cm){
24123         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24124     },
24125
24126     // private
24127     onEditorKey : function(field, e){
24128         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24129         if(k == e.TAB){
24130             e.stopEvent();
24131             ed.completeEdit();
24132             if(e.shiftKey){
24133                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24134             }else{
24135                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24136             }
24137         }else if(k == e.ENTER && !e.ctrlKey){
24138             e.stopEvent();
24139             ed.completeEdit();
24140             if(e.shiftKey){
24141                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24142             }else{
24143                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24144             }
24145         }else if(k == e.ESC){
24146             ed.cancelEdit();
24147         }
24148         if(newCell){
24149             g.startEditing(newCell[0], newCell[1]);
24150         }
24151     }
24152 });
24153 /*
24154  * Based on:
24155  * Ext JS Library 1.1.1
24156  * Copyright(c) 2006-2007, Ext JS, LLC.
24157  *
24158  * Originally Released Under LGPL - original licence link has changed is not relivant.
24159  *
24160  * Fork - LGPL
24161  * <script type="text/javascript">
24162  */
24163  
24164 /**
24165  * @class Roo.bootstrap.PagingToolbar
24166  * @extends Roo.bootstrap.NavSimplebar
24167  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24168  * @constructor
24169  * Create a new PagingToolbar
24170  * @param {Object} config The config object
24171  * @param {Roo.data.Store} store
24172  */
24173 Roo.bootstrap.PagingToolbar = function(config)
24174 {
24175     // old args format still supported... - xtype is prefered..
24176         // created from xtype...
24177     
24178     this.ds = config.dataSource;
24179     
24180     if (config.store && !this.ds) {
24181         this.store= Roo.factory(config.store, Roo.data);
24182         this.ds = this.store;
24183         this.ds.xmodule = this.xmodule || false;
24184     }
24185     
24186     this.toolbarItems = [];
24187     if (config.items) {
24188         this.toolbarItems = config.items;
24189     }
24190     
24191     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24192     
24193     this.cursor = 0;
24194     
24195     if (this.ds) { 
24196         this.bind(this.ds);
24197     }
24198     
24199     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24200     
24201 };
24202
24203 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24204     /**
24205      * @cfg {Roo.data.Store} dataSource
24206      * The underlying data store providing the paged data
24207      */
24208     /**
24209      * @cfg {String/HTMLElement/Element} container
24210      * container The id or element that will contain the toolbar
24211      */
24212     /**
24213      * @cfg {Boolean} displayInfo
24214      * True to display the displayMsg (defaults to false)
24215      */
24216     /**
24217      * @cfg {Number} pageSize
24218      * The number of records to display per page (defaults to 20)
24219      */
24220     pageSize: 20,
24221     /**
24222      * @cfg {String} displayMsg
24223      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24224      */
24225     displayMsg : 'Displaying {0} - {1} of {2}',
24226     /**
24227      * @cfg {String} emptyMsg
24228      * The message to display when no records are found (defaults to "No data to display")
24229      */
24230     emptyMsg : 'No data to display',
24231     /**
24232      * Customizable piece of the default paging text (defaults to "Page")
24233      * @type String
24234      */
24235     beforePageText : "Page",
24236     /**
24237      * Customizable piece of the default paging text (defaults to "of %0")
24238      * @type String
24239      */
24240     afterPageText : "of {0}",
24241     /**
24242      * Customizable piece of the default paging text (defaults to "First Page")
24243      * @type String
24244      */
24245     firstText : "First Page",
24246     /**
24247      * Customizable piece of the default paging text (defaults to "Previous Page")
24248      * @type String
24249      */
24250     prevText : "Previous Page",
24251     /**
24252      * Customizable piece of the default paging text (defaults to "Next Page")
24253      * @type String
24254      */
24255     nextText : "Next Page",
24256     /**
24257      * Customizable piece of the default paging text (defaults to "Last Page")
24258      * @type String
24259      */
24260     lastText : "Last Page",
24261     /**
24262      * Customizable piece of the default paging text (defaults to "Refresh")
24263      * @type String
24264      */
24265     refreshText : "Refresh",
24266
24267     buttons : false,
24268     // private
24269     onRender : function(ct, position) 
24270     {
24271         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24272         this.navgroup.parentId = this.id;
24273         this.navgroup.onRender(this.el, null);
24274         // add the buttons to the navgroup
24275         
24276         if(this.displayInfo){
24277             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24278             this.displayEl = this.el.select('.x-paging-info', true).first();
24279 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24280 //            this.displayEl = navel.el.select('span',true).first();
24281         }
24282         
24283         var _this = this;
24284         
24285         if(this.buttons){
24286             Roo.each(_this.buttons, function(e){ // this might need to use render????
24287                Roo.factory(e).onRender(_this.el, null);
24288             });
24289         }
24290             
24291         Roo.each(_this.toolbarItems, function(e) {
24292             _this.navgroup.addItem(e);
24293         });
24294         
24295         
24296         this.first = this.navgroup.addItem({
24297             tooltip: this.firstText,
24298             cls: "prev",
24299             icon : 'fa fa-backward',
24300             disabled: true,
24301             preventDefault: true,
24302             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24303         });
24304         
24305         this.prev =  this.navgroup.addItem({
24306             tooltip: this.prevText,
24307             cls: "prev",
24308             icon : 'fa fa-step-backward',
24309             disabled: true,
24310             preventDefault: true,
24311             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24312         });
24313     //this.addSeparator();
24314         
24315         
24316         var field = this.navgroup.addItem( {
24317             tagtype : 'span',
24318             cls : 'x-paging-position',
24319             
24320             html : this.beforePageText  +
24321                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24322                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24323          } ); //?? escaped?
24324         
24325         this.field = field.el.select('input', true).first();
24326         this.field.on("keydown", this.onPagingKeydown, this);
24327         this.field.on("focus", function(){this.dom.select();});
24328     
24329     
24330         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24331         //this.field.setHeight(18);
24332         //this.addSeparator();
24333         this.next = this.navgroup.addItem({
24334             tooltip: this.nextText,
24335             cls: "next",
24336             html : ' <i class="fa fa-step-forward">',
24337             disabled: true,
24338             preventDefault: true,
24339             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24340         });
24341         this.last = this.navgroup.addItem({
24342             tooltip: this.lastText,
24343             icon : 'fa fa-forward',
24344             cls: "next",
24345             disabled: true,
24346             preventDefault: true,
24347             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24348         });
24349     //this.addSeparator();
24350         this.loading = this.navgroup.addItem({
24351             tooltip: this.refreshText,
24352             icon: 'fa fa-refresh',
24353             preventDefault: true,
24354             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24355         });
24356         
24357     },
24358
24359     // private
24360     updateInfo : function(){
24361         if(this.displayEl){
24362             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24363             var msg = count == 0 ?
24364                 this.emptyMsg :
24365                 String.format(
24366                     this.displayMsg,
24367                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24368                 );
24369             this.displayEl.update(msg);
24370         }
24371     },
24372
24373     // private
24374     onLoad : function(ds, r, o)
24375     {
24376         this.cursor = o.params ? o.params.start : 0;
24377         var d = this.getPageData(),
24378             ap = d.activePage,
24379             ps = d.pages;
24380         
24381         
24382         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24383         this.field.dom.value = ap;
24384         this.first.setDisabled(ap == 1);
24385         this.prev.setDisabled(ap == 1);
24386         this.next.setDisabled(ap == ps);
24387         this.last.setDisabled(ap == ps);
24388         this.loading.enable();
24389         this.updateInfo();
24390     },
24391
24392     // private
24393     getPageData : function(){
24394         var total = this.ds.getTotalCount();
24395         return {
24396             total : total,
24397             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24398             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24399         };
24400     },
24401
24402     // private
24403     onLoadError : function(){
24404         this.loading.enable();
24405     },
24406
24407     // private
24408     onPagingKeydown : function(e){
24409         var k = e.getKey();
24410         var d = this.getPageData();
24411         if(k == e.RETURN){
24412             var v = this.field.dom.value, pageNum;
24413             if(!v || isNaN(pageNum = parseInt(v, 10))){
24414                 this.field.dom.value = d.activePage;
24415                 return;
24416             }
24417             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24418             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24419             e.stopEvent();
24420         }
24421         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))
24422         {
24423           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24424           this.field.dom.value = pageNum;
24425           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24426           e.stopEvent();
24427         }
24428         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24429         {
24430           var v = this.field.dom.value, pageNum; 
24431           var increment = (e.shiftKey) ? 10 : 1;
24432           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24433                 increment *= -1;
24434           }
24435           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24436             this.field.dom.value = d.activePage;
24437             return;
24438           }
24439           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24440           {
24441             this.field.dom.value = parseInt(v, 10) + increment;
24442             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24443             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24444           }
24445           e.stopEvent();
24446         }
24447     },
24448
24449     // private
24450     beforeLoad : function(){
24451         if(this.loading){
24452             this.loading.disable();
24453         }
24454     },
24455
24456     // private
24457     onClick : function(which){
24458         
24459         var ds = this.ds;
24460         if (!ds) {
24461             return;
24462         }
24463         
24464         switch(which){
24465             case "first":
24466                 ds.load({params:{start: 0, limit: this.pageSize}});
24467             break;
24468             case "prev":
24469                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24470             break;
24471             case "next":
24472                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24473             break;
24474             case "last":
24475                 var total = ds.getTotalCount();
24476                 var extra = total % this.pageSize;
24477                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24478                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24479             break;
24480             case "refresh":
24481                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24482             break;
24483         }
24484     },
24485
24486     /**
24487      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24488      * @param {Roo.data.Store} store The data store to unbind
24489      */
24490     unbind : function(ds){
24491         ds.un("beforeload", this.beforeLoad, this);
24492         ds.un("load", this.onLoad, this);
24493         ds.un("loadexception", this.onLoadError, this);
24494         ds.un("remove", this.updateInfo, this);
24495         ds.un("add", this.updateInfo, this);
24496         this.ds = undefined;
24497     },
24498
24499     /**
24500      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24501      * @param {Roo.data.Store} store The data store to bind
24502      */
24503     bind : function(ds){
24504         ds.on("beforeload", this.beforeLoad, this);
24505         ds.on("load", this.onLoad, this);
24506         ds.on("loadexception", this.onLoadError, this);
24507         ds.on("remove", this.updateInfo, this);
24508         ds.on("add", this.updateInfo, this);
24509         this.ds = ds;
24510     }
24511 });/*
24512  * - LGPL
24513  *
24514  * element
24515  * 
24516  */
24517
24518 /**
24519  * @class Roo.bootstrap.MessageBar
24520  * @extends Roo.bootstrap.Component
24521  * Bootstrap MessageBar class
24522  * @cfg {String} html contents of the MessageBar
24523  * @cfg {String} weight (info | success | warning | danger) default info
24524  * @cfg {String} beforeClass insert the bar before the given class
24525  * @cfg {Boolean} closable (true | false) default false
24526  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24527  * 
24528  * @constructor
24529  * Create a new Element
24530  * @param {Object} config The config object
24531  */
24532
24533 Roo.bootstrap.MessageBar = function(config){
24534     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24535 };
24536
24537 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24538     
24539     html: '',
24540     weight: 'info',
24541     closable: false,
24542     fixed: false,
24543     beforeClass: 'bootstrap-sticky-wrap',
24544     
24545     getAutoCreate : function(){
24546         
24547         var cfg = {
24548             tag: 'div',
24549             cls: 'alert alert-dismissable alert-' + this.weight,
24550             cn: [
24551                 {
24552                     tag: 'span',
24553                     cls: 'message',
24554                     html: this.html || ''
24555                 }
24556             ]
24557         };
24558         
24559         if(this.fixed){
24560             cfg.cls += ' alert-messages-fixed';
24561         }
24562         
24563         if(this.closable){
24564             cfg.cn.push({
24565                 tag: 'button',
24566                 cls: 'close',
24567                 html: 'x'
24568             });
24569         }
24570         
24571         return cfg;
24572     },
24573     
24574     onRender : function(ct, position)
24575     {
24576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24577         
24578         if(!this.el){
24579             var cfg = Roo.apply({},  this.getAutoCreate());
24580             cfg.id = Roo.id();
24581             
24582             if (this.cls) {
24583                 cfg.cls += ' ' + this.cls;
24584             }
24585             if (this.style) {
24586                 cfg.style = this.style;
24587             }
24588             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24589             
24590             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24591         }
24592         
24593         this.el.select('>button.close').on('click', this.hide, this);
24594         
24595     },
24596     
24597     show : function()
24598     {
24599         if (!this.rendered) {
24600             this.render();
24601         }
24602         
24603         this.el.show();
24604         
24605         this.fireEvent('show', this);
24606         
24607     },
24608     
24609     hide : function()
24610     {
24611         if (!this.rendered) {
24612             this.render();
24613         }
24614         
24615         this.el.hide();
24616         
24617         this.fireEvent('hide', this);
24618     },
24619     
24620     update : function()
24621     {
24622 //        var e = this.el.dom.firstChild;
24623 //        
24624 //        if(this.closable){
24625 //            e = e.nextSibling;
24626 //        }
24627 //        
24628 //        e.data = this.html || '';
24629
24630         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24631     }
24632    
24633 });
24634
24635  
24636
24637      /*
24638  * - LGPL
24639  *
24640  * Graph
24641  * 
24642  */
24643
24644
24645 /**
24646  * @class Roo.bootstrap.Graph
24647  * @extends Roo.bootstrap.Component
24648  * Bootstrap Graph class
24649 > Prameters
24650  -sm {number} sm 4
24651  -md {number} md 5
24652  @cfg {String} graphtype  bar | vbar | pie
24653  @cfg {number} g_x coodinator | centre x (pie)
24654  @cfg {number} g_y coodinator | centre y (pie)
24655  @cfg {number} g_r radius (pie)
24656  @cfg {number} g_height height of the chart (respected by all elements in the set)
24657  @cfg {number} g_width width of the chart (respected by all elements in the set)
24658  @cfg {Object} title The title of the chart
24659     
24660  -{Array}  values
24661  -opts (object) options for the chart 
24662      o {
24663      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24664      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24665      o vgutter (number)
24666      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.
24667      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24668      o to
24669      o stretch (boolean)
24670      o }
24671  -opts (object) options for the pie
24672      o{
24673      o cut
24674      o startAngle (number)
24675      o endAngle (number)
24676      } 
24677  *
24678  * @constructor
24679  * Create a new Input
24680  * @param {Object} config The config object
24681  */
24682
24683 Roo.bootstrap.Graph = function(config){
24684     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24685     
24686     this.addEvents({
24687         // img events
24688         /**
24689          * @event click
24690          * The img click event for the img.
24691          * @param {Roo.EventObject} e
24692          */
24693         "click" : true
24694     });
24695 };
24696
24697 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24698     
24699     sm: 4,
24700     md: 5,
24701     graphtype: 'bar',
24702     g_height: 250,
24703     g_width: 400,
24704     g_x: 50,
24705     g_y: 50,
24706     g_r: 30,
24707     opts:{
24708         //g_colors: this.colors,
24709         g_type: 'soft',
24710         g_gutter: '20%'
24711
24712     },
24713     title : false,
24714
24715     getAutoCreate : function(){
24716         
24717         var cfg = {
24718             tag: 'div',
24719             html : null
24720         };
24721         
24722         
24723         return  cfg;
24724     },
24725
24726     onRender : function(ct,position){
24727         
24728         
24729         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24730         
24731         if (typeof(Raphael) == 'undefined') {
24732             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24733             return;
24734         }
24735         
24736         this.raphael = Raphael(this.el.dom);
24737         
24738                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24739                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24740                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24741                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24742                 /*
24743                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24744                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24745                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24746                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24747                 
24748                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24749                 r.barchart(330, 10, 300, 220, data1);
24750                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24751                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24752                 */
24753                 
24754                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24755                 // r.barchart(30, 30, 560, 250,  xdata, {
24756                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24757                 //     axis : "0 0 1 1",
24758                 //     axisxlabels :  xdata
24759                 //     //yvalues : cols,
24760                    
24761                 // });
24762 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24763 //        
24764 //        this.load(null,xdata,{
24765 //                axis : "0 0 1 1",
24766 //                axisxlabels :  xdata
24767 //                });
24768
24769     },
24770
24771     load : function(graphtype,xdata,opts)
24772     {
24773         this.raphael.clear();
24774         if(!graphtype) {
24775             graphtype = this.graphtype;
24776         }
24777         if(!opts){
24778             opts = this.opts;
24779         }
24780         var r = this.raphael,
24781             fin = function () {
24782                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24783             },
24784             fout = function () {
24785                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24786             },
24787             pfin = function() {
24788                 this.sector.stop();
24789                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24790
24791                 if (this.label) {
24792                     this.label[0].stop();
24793                     this.label[0].attr({ r: 7.5 });
24794                     this.label[1].attr({ "font-weight": 800 });
24795                 }
24796             },
24797             pfout = function() {
24798                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24799
24800                 if (this.label) {
24801                     this.label[0].animate({ r: 5 }, 500, "bounce");
24802                     this.label[1].attr({ "font-weight": 400 });
24803                 }
24804             };
24805
24806         switch(graphtype){
24807             case 'bar':
24808                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24809                 break;
24810             case 'hbar':
24811                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24812                 break;
24813             case 'pie':
24814 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24815 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24816 //            
24817                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24818                 
24819                 break;
24820
24821         }
24822         
24823         if(this.title){
24824             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24825         }
24826         
24827     },
24828     
24829     setTitle: function(o)
24830     {
24831         this.title = o;
24832     },
24833     
24834     initEvents: function() {
24835         
24836         if(!this.href){
24837             this.el.on('click', this.onClick, this);
24838         }
24839     },
24840     
24841     onClick : function(e)
24842     {
24843         Roo.log('img onclick');
24844         this.fireEvent('click', this, e);
24845     }
24846    
24847 });
24848
24849  
24850 /*
24851  * - LGPL
24852  *
24853  * numberBox
24854  * 
24855  */
24856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24857
24858 /**
24859  * @class Roo.bootstrap.dash.NumberBox
24860  * @extends Roo.bootstrap.Component
24861  * Bootstrap NumberBox class
24862  * @cfg {String} headline Box headline
24863  * @cfg {String} content Box content
24864  * @cfg {String} icon Box icon
24865  * @cfg {String} footer Footer text
24866  * @cfg {String} fhref Footer href
24867  * 
24868  * @constructor
24869  * Create a new NumberBox
24870  * @param {Object} config The config object
24871  */
24872
24873
24874 Roo.bootstrap.dash.NumberBox = function(config){
24875     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24876     
24877 };
24878
24879 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24880     
24881     headline : '',
24882     content : '',
24883     icon : '',
24884     footer : '',
24885     fhref : '',
24886     ficon : '',
24887     
24888     getAutoCreate : function(){
24889         
24890         var cfg = {
24891             tag : 'div',
24892             cls : 'small-box ',
24893             cn : [
24894                 {
24895                     tag : 'div',
24896                     cls : 'inner',
24897                     cn :[
24898                         {
24899                             tag : 'h3',
24900                             cls : 'roo-headline',
24901                             html : this.headline
24902                         },
24903                         {
24904                             tag : 'p',
24905                             cls : 'roo-content',
24906                             html : this.content
24907                         }
24908                     ]
24909                 }
24910             ]
24911         };
24912         
24913         if(this.icon){
24914             cfg.cn.push({
24915                 tag : 'div',
24916                 cls : 'icon',
24917                 cn :[
24918                     {
24919                         tag : 'i',
24920                         cls : 'ion ' + this.icon
24921                     }
24922                 ]
24923             });
24924         }
24925         
24926         if(this.footer){
24927             var footer = {
24928                 tag : 'a',
24929                 cls : 'small-box-footer',
24930                 href : this.fhref || '#',
24931                 html : this.footer
24932             };
24933             
24934             cfg.cn.push(footer);
24935             
24936         }
24937         
24938         return  cfg;
24939     },
24940
24941     onRender : function(ct,position){
24942         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24943
24944
24945        
24946                 
24947     },
24948
24949     setHeadline: function (value)
24950     {
24951         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24952     },
24953     
24954     setFooter: function (value, href)
24955     {
24956         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24957         
24958         if(href){
24959             this.el.select('a.small-box-footer',true).first().attr('href', href);
24960         }
24961         
24962     },
24963
24964     setContent: function (value)
24965     {
24966         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24967     },
24968
24969     initEvents: function() 
24970     {   
24971         
24972     }
24973     
24974 });
24975
24976  
24977 /*
24978  * - LGPL
24979  *
24980  * TabBox
24981  * 
24982  */
24983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24984
24985 /**
24986  * @class Roo.bootstrap.dash.TabBox
24987  * @extends Roo.bootstrap.Component
24988  * Bootstrap TabBox class
24989  * @cfg {String} title Title of the TabBox
24990  * @cfg {String} icon Icon of the TabBox
24991  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24992  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24993  * 
24994  * @constructor
24995  * Create a new TabBox
24996  * @param {Object} config The config object
24997  */
24998
24999
25000 Roo.bootstrap.dash.TabBox = function(config){
25001     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25002     this.addEvents({
25003         // raw events
25004         /**
25005          * @event addpane
25006          * When a pane is added
25007          * @param {Roo.bootstrap.dash.TabPane} pane
25008          */
25009         "addpane" : true,
25010         /**
25011          * @event activatepane
25012          * When a pane is activated
25013          * @param {Roo.bootstrap.dash.TabPane} pane
25014          */
25015         "activatepane" : true
25016         
25017          
25018     });
25019     
25020     this.panes = [];
25021 };
25022
25023 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25024
25025     title : '',
25026     icon : false,
25027     showtabs : true,
25028     tabScrollable : false,
25029     
25030     getChildContainer : function()
25031     {
25032         return this.el.select('.tab-content', true).first();
25033     },
25034     
25035     getAutoCreate : function(){
25036         
25037         var header = {
25038             tag: 'li',
25039             cls: 'pull-left header',
25040             html: this.title,
25041             cn : []
25042         };
25043         
25044         if(this.icon){
25045             header.cn.push({
25046                 tag: 'i',
25047                 cls: 'fa ' + this.icon
25048             });
25049         }
25050         
25051         var h = {
25052             tag: 'ul',
25053             cls: 'nav nav-tabs pull-right',
25054             cn: [
25055                 header
25056             ]
25057         };
25058         
25059         if(this.tabScrollable){
25060             h = {
25061                 tag: 'div',
25062                 cls: 'tab-header',
25063                 cn: [
25064                     {
25065                         tag: 'ul',
25066                         cls: 'nav nav-tabs pull-right',
25067                         cn: [
25068                             header
25069                         ]
25070                     }
25071                 ]
25072             };
25073         }
25074         
25075         var cfg = {
25076             tag: 'div',
25077             cls: 'nav-tabs-custom',
25078             cn: [
25079                 h,
25080                 {
25081                     tag: 'div',
25082                     cls: 'tab-content no-padding',
25083                     cn: []
25084                 }
25085             ]
25086         };
25087
25088         return  cfg;
25089     },
25090     initEvents : function()
25091     {
25092         //Roo.log('add add pane handler');
25093         this.on('addpane', this.onAddPane, this);
25094     },
25095      /**
25096      * Updates the box title
25097      * @param {String} html to set the title to.
25098      */
25099     setTitle : function(value)
25100     {
25101         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25102     },
25103     onAddPane : function(pane)
25104     {
25105         this.panes.push(pane);
25106         //Roo.log('addpane');
25107         //Roo.log(pane);
25108         // tabs are rendere left to right..
25109         if(!this.showtabs){
25110             return;
25111         }
25112         
25113         var ctr = this.el.select('.nav-tabs', true).first();
25114          
25115          
25116         var existing = ctr.select('.nav-tab',true);
25117         var qty = existing.getCount();;
25118         
25119         
25120         var tab = ctr.createChild({
25121             tag : 'li',
25122             cls : 'nav-tab' + (qty ? '' : ' active'),
25123             cn : [
25124                 {
25125                     tag : 'a',
25126                     href:'#',
25127                     html : pane.title
25128                 }
25129             ]
25130         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25131         pane.tab = tab;
25132         
25133         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25134         if (!qty) {
25135             pane.el.addClass('active');
25136         }
25137         
25138                 
25139     },
25140     onTabClick : function(ev,un,ob,pane)
25141     {
25142         //Roo.log('tab - prev default');
25143         ev.preventDefault();
25144         
25145         
25146         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25147         pane.tab.addClass('active');
25148         //Roo.log(pane.title);
25149         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25150         // technically we should have a deactivate event.. but maybe add later.
25151         // and it should not de-activate the selected tab...
25152         this.fireEvent('activatepane', pane);
25153         pane.el.addClass('active');
25154         pane.fireEvent('activate');
25155         
25156         
25157     },
25158     
25159     getActivePane : function()
25160     {
25161         var r = false;
25162         Roo.each(this.panes, function(p) {
25163             if(p.el.hasClass('active')){
25164                 r = p;
25165                 return false;
25166             }
25167             
25168             return;
25169         });
25170         
25171         return r;
25172     }
25173     
25174     
25175 });
25176
25177  
25178 /*
25179  * - LGPL
25180  *
25181  * Tab pane
25182  * 
25183  */
25184 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25185 /**
25186  * @class Roo.bootstrap.TabPane
25187  * @extends Roo.bootstrap.Component
25188  * Bootstrap TabPane class
25189  * @cfg {Boolean} active (false | true) Default false
25190  * @cfg {String} title title of panel
25191
25192  * 
25193  * @constructor
25194  * Create a new TabPane
25195  * @param {Object} config The config object
25196  */
25197
25198 Roo.bootstrap.dash.TabPane = function(config){
25199     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25200     
25201     this.addEvents({
25202         // raw events
25203         /**
25204          * @event activate
25205          * When a pane is activated
25206          * @param {Roo.bootstrap.dash.TabPane} pane
25207          */
25208         "activate" : true
25209          
25210     });
25211 };
25212
25213 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25214     
25215     active : false,
25216     title : '',
25217     
25218     // the tabBox that this is attached to.
25219     tab : false,
25220      
25221     getAutoCreate : function() 
25222     {
25223         var cfg = {
25224             tag: 'div',
25225             cls: 'tab-pane'
25226         };
25227         
25228         if(this.active){
25229             cfg.cls += ' active';
25230         }
25231         
25232         return cfg;
25233     },
25234     initEvents  : function()
25235     {
25236         //Roo.log('trigger add pane handler');
25237         this.parent().fireEvent('addpane', this)
25238     },
25239     
25240      /**
25241      * Updates the tab title 
25242      * @param {String} html to set the title to.
25243      */
25244     setTitle: function(str)
25245     {
25246         if (!this.tab) {
25247             return;
25248         }
25249         this.title = str;
25250         this.tab.select('a', true).first().dom.innerHTML = str;
25251         
25252     }
25253     
25254     
25255     
25256 });
25257
25258  
25259
25260
25261  /*
25262  * - LGPL
25263  *
25264  * menu
25265  * 
25266  */
25267 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25268
25269 /**
25270  * @class Roo.bootstrap.menu.Menu
25271  * @extends Roo.bootstrap.Component
25272  * Bootstrap Menu class - container for Menu
25273  * @cfg {String} html Text of the menu
25274  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25275  * @cfg {String} icon Font awesome icon
25276  * @cfg {String} pos Menu align to (top | bottom) default bottom
25277  * 
25278  * 
25279  * @constructor
25280  * Create a new Menu
25281  * @param {Object} config The config object
25282  */
25283
25284
25285 Roo.bootstrap.menu.Menu = function(config){
25286     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25287     
25288     this.addEvents({
25289         /**
25290          * @event beforeshow
25291          * Fires before this menu is displayed
25292          * @param {Roo.bootstrap.menu.Menu} this
25293          */
25294         beforeshow : true,
25295         /**
25296          * @event beforehide
25297          * Fires before this menu is hidden
25298          * @param {Roo.bootstrap.menu.Menu} this
25299          */
25300         beforehide : true,
25301         /**
25302          * @event show
25303          * Fires after this menu is displayed
25304          * @param {Roo.bootstrap.menu.Menu} this
25305          */
25306         show : true,
25307         /**
25308          * @event hide
25309          * Fires after this menu is hidden
25310          * @param {Roo.bootstrap.menu.Menu} this
25311          */
25312         hide : true,
25313         /**
25314          * @event click
25315          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25316          * @param {Roo.bootstrap.menu.Menu} this
25317          * @param {Roo.EventObject} e
25318          */
25319         click : true
25320     });
25321     
25322 };
25323
25324 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25325     
25326     submenu : false,
25327     html : '',
25328     weight : 'default',
25329     icon : false,
25330     pos : 'bottom',
25331     
25332     
25333     getChildContainer : function() {
25334         if(this.isSubMenu){
25335             return this.el;
25336         }
25337         
25338         return this.el.select('ul.dropdown-menu', true).first();  
25339     },
25340     
25341     getAutoCreate : function()
25342     {
25343         var text = [
25344             {
25345                 tag : 'span',
25346                 cls : 'roo-menu-text',
25347                 html : this.html
25348             }
25349         ];
25350         
25351         if(this.icon){
25352             text.unshift({
25353                 tag : 'i',
25354                 cls : 'fa ' + this.icon
25355             })
25356         }
25357         
25358         
25359         var cfg = {
25360             tag : 'div',
25361             cls : 'btn-group',
25362             cn : [
25363                 {
25364                     tag : 'button',
25365                     cls : 'dropdown-button btn btn-' + this.weight,
25366                     cn : text
25367                 },
25368                 {
25369                     tag : 'button',
25370                     cls : 'dropdown-toggle btn btn-' + this.weight,
25371                     cn : [
25372                         {
25373                             tag : 'span',
25374                             cls : 'caret'
25375                         }
25376                     ]
25377                 },
25378                 {
25379                     tag : 'ul',
25380                     cls : 'dropdown-menu'
25381                 }
25382             ]
25383             
25384         };
25385         
25386         if(this.pos == 'top'){
25387             cfg.cls += ' dropup';
25388         }
25389         
25390         if(this.isSubMenu){
25391             cfg = {
25392                 tag : 'ul',
25393                 cls : 'dropdown-menu'
25394             }
25395         }
25396         
25397         return cfg;
25398     },
25399     
25400     onRender : function(ct, position)
25401     {
25402         this.isSubMenu = ct.hasClass('dropdown-submenu');
25403         
25404         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25405     },
25406     
25407     initEvents : function() 
25408     {
25409         if(this.isSubMenu){
25410             return;
25411         }
25412         
25413         this.hidden = true;
25414         
25415         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25416         this.triggerEl.on('click', this.onTriggerPress, this);
25417         
25418         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25419         this.buttonEl.on('click', this.onClick, this);
25420         
25421     },
25422     
25423     list : function()
25424     {
25425         if(this.isSubMenu){
25426             return this.el;
25427         }
25428         
25429         return this.el.select('ul.dropdown-menu', true).first();
25430     },
25431     
25432     onClick : function(e)
25433     {
25434         this.fireEvent("click", this, e);
25435     },
25436     
25437     onTriggerPress  : function(e)
25438     {   
25439         if (this.isVisible()) {
25440             this.hide();
25441         } else {
25442             this.show();
25443         }
25444     },
25445     
25446     isVisible : function(){
25447         return !this.hidden;
25448     },
25449     
25450     show : function()
25451     {
25452         this.fireEvent("beforeshow", this);
25453         
25454         this.hidden = false;
25455         this.el.addClass('open');
25456         
25457         Roo.get(document).on("mouseup", this.onMouseUp, this);
25458         
25459         this.fireEvent("show", this);
25460         
25461         
25462     },
25463     
25464     hide : function()
25465     {
25466         this.fireEvent("beforehide", this);
25467         
25468         this.hidden = true;
25469         this.el.removeClass('open');
25470         
25471         Roo.get(document).un("mouseup", this.onMouseUp);
25472         
25473         this.fireEvent("hide", this);
25474     },
25475     
25476     onMouseUp : function()
25477     {
25478         this.hide();
25479     }
25480     
25481 });
25482
25483  
25484  /*
25485  * - LGPL
25486  *
25487  * menu item
25488  * 
25489  */
25490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25491
25492 /**
25493  * @class Roo.bootstrap.menu.Item
25494  * @extends Roo.bootstrap.Component
25495  * Bootstrap MenuItem class
25496  * @cfg {Boolean} submenu (true | false) default false
25497  * @cfg {String} html text of the item
25498  * @cfg {String} href the link
25499  * @cfg {Boolean} disable (true | false) default false
25500  * @cfg {Boolean} preventDefault (true | false) default true
25501  * @cfg {String} icon Font awesome icon
25502  * @cfg {String} pos Submenu align to (left | right) default right 
25503  * 
25504  * 
25505  * @constructor
25506  * Create a new Item
25507  * @param {Object} config The config object
25508  */
25509
25510
25511 Roo.bootstrap.menu.Item = function(config){
25512     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25513     this.addEvents({
25514         /**
25515          * @event mouseover
25516          * Fires when the mouse is hovering over this menu
25517          * @param {Roo.bootstrap.menu.Item} this
25518          * @param {Roo.EventObject} e
25519          */
25520         mouseover : true,
25521         /**
25522          * @event mouseout
25523          * Fires when the mouse exits this menu
25524          * @param {Roo.bootstrap.menu.Item} this
25525          * @param {Roo.EventObject} e
25526          */
25527         mouseout : true,
25528         // raw events
25529         /**
25530          * @event click
25531          * The raw click event for the entire grid.
25532          * @param {Roo.EventObject} e
25533          */
25534         click : true
25535     });
25536 };
25537
25538 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25539     
25540     submenu : false,
25541     href : '',
25542     html : '',
25543     preventDefault: true,
25544     disable : false,
25545     icon : false,
25546     pos : 'right',
25547     
25548     getAutoCreate : function()
25549     {
25550         var text = [
25551             {
25552                 tag : 'span',
25553                 cls : 'roo-menu-item-text',
25554                 html : this.html
25555             }
25556         ];
25557         
25558         if(this.icon){
25559             text.unshift({
25560                 tag : 'i',
25561                 cls : 'fa ' + this.icon
25562             })
25563         }
25564         
25565         var cfg = {
25566             tag : 'li',
25567             cn : [
25568                 {
25569                     tag : 'a',
25570                     href : this.href || '#',
25571                     cn : text
25572                 }
25573             ]
25574         };
25575         
25576         if(this.disable){
25577             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25578         }
25579         
25580         if(this.submenu){
25581             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25582             
25583             if(this.pos == 'left'){
25584                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25585             }
25586         }
25587         
25588         return cfg;
25589     },
25590     
25591     initEvents : function() 
25592     {
25593         this.el.on('mouseover', this.onMouseOver, this);
25594         this.el.on('mouseout', this.onMouseOut, this);
25595         
25596         this.el.select('a', true).first().on('click', this.onClick, this);
25597         
25598     },
25599     
25600     onClick : function(e)
25601     {
25602         if(this.preventDefault){
25603             e.preventDefault();
25604         }
25605         
25606         this.fireEvent("click", this, e);
25607     },
25608     
25609     onMouseOver : function(e)
25610     {
25611         if(this.submenu && this.pos == 'left'){
25612             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25613         }
25614         
25615         this.fireEvent("mouseover", this, e);
25616     },
25617     
25618     onMouseOut : function(e)
25619     {
25620         this.fireEvent("mouseout", this, e);
25621     }
25622 });
25623
25624  
25625
25626  /*
25627  * - LGPL
25628  *
25629  * menu separator
25630  * 
25631  */
25632 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25633
25634 /**
25635  * @class Roo.bootstrap.menu.Separator
25636  * @extends Roo.bootstrap.Component
25637  * Bootstrap Separator class
25638  * 
25639  * @constructor
25640  * Create a new Separator
25641  * @param {Object} config The config object
25642  */
25643
25644
25645 Roo.bootstrap.menu.Separator = function(config){
25646     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25647 };
25648
25649 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25650     
25651     getAutoCreate : function(){
25652         var cfg = {
25653             tag : 'li',
25654             cls: 'divider'
25655         };
25656         
25657         return cfg;
25658     }
25659    
25660 });
25661
25662  
25663
25664  /*
25665  * - LGPL
25666  *
25667  * Tooltip
25668  * 
25669  */
25670
25671 /**
25672  * @class Roo.bootstrap.Tooltip
25673  * Bootstrap Tooltip class
25674  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25675  * to determine which dom element triggers the tooltip.
25676  * 
25677  * It needs to add support for additional attributes like tooltip-position
25678  * 
25679  * @constructor
25680  * Create a new Toolti
25681  * @param {Object} config The config object
25682  */
25683
25684 Roo.bootstrap.Tooltip = function(config){
25685     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25686     
25687     this.alignment = Roo.bootstrap.Tooltip.alignment;
25688     
25689     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25690         this.alignment = config.alignment;
25691     }
25692     
25693 };
25694
25695 Roo.apply(Roo.bootstrap.Tooltip, {
25696     /**
25697      * @function init initialize tooltip monitoring.
25698      * @static
25699      */
25700     currentEl : false,
25701     currentTip : false,
25702     currentRegion : false,
25703     
25704     //  init : delay?
25705     
25706     init : function()
25707     {
25708         Roo.get(document).on('mouseover', this.enter ,this);
25709         Roo.get(document).on('mouseout', this.leave, this);
25710          
25711         
25712         this.currentTip = new Roo.bootstrap.Tooltip();
25713     },
25714     
25715     enter : function(ev)
25716     {
25717         var dom = ev.getTarget();
25718         
25719         //Roo.log(['enter',dom]);
25720         var el = Roo.fly(dom);
25721         if (this.currentEl) {
25722             //Roo.log(dom);
25723             //Roo.log(this.currentEl);
25724             //Roo.log(this.currentEl.contains(dom));
25725             if (this.currentEl == el) {
25726                 return;
25727             }
25728             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25729                 return;
25730             }
25731
25732         }
25733         
25734         if (this.currentTip.el) {
25735             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25736         }    
25737         //Roo.log(ev);
25738         
25739         if(!el || el.dom == document){
25740             return;
25741         }
25742         
25743         var bindEl = el;
25744         
25745         // you can not look for children, as if el is the body.. then everythign is the child..
25746         if (!el.attr('tooltip')) { //
25747             if (!el.select("[tooltip]").elements.length) {
25748                 return;
25749             }
25750             // is the mouse over this child...?
25751             bindEl = el.select("[tooltip]").first();
25752             var xy = ev.getXY();
25753             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25754                 //Roo.log("not in region.");
25755                 return;
25756             }
25757             //Roo.log("child element over..");
25758             
25759         }
25760         this.currentEl = bindEl;
25761         this.currentTip.bind(bindEl);
25762         this.currentRegion = Roo.lib.Region.getRegion(dom);
25763         this.currentTip.enter();
25764         
25765     },
25766     leave : function(ev)
25767     {
25768         var dom = ev.getTarget();
25769         //Roo.log(['leave',dom]);
25770         if (!this.currentEl) {
25771             return;
25772         }
25773         
25774         
25775         if (dom != this.currentEl.dom) {
25776             return;
25777         }
25778         var xy = ev.getXY();
25779         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25780             return;
25781         }
25782         // only activate leave if mouse cursor is outside... bounding box..
25783         
25784         
25785         
25786         
25787         if (this.currentTip) {
25788             this.currentTip.leave();
25789         }
25790         //Roo.log('clear currentEl');
25791         this.currentEl = false;
25792         
25793         
25794     },
25795     alignment : {
25796         'left' : ['r-l', [-2,0], 'right'],
25797         'right' : ['l-r', [2,0], 'left'],
25798         'bottom' : ['t-b', [0,2], 'top'],
25799         'top' : [ 'b-t', [0,-2], 'bottom']
25800     }
25801     
25802 });
25803
25804
25805 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25806     
25807     
25808     bindEl : false,
25809     
25810     delay : null, // can be { show : 300 , hide: 500}
25811     
25812     timeout : null,
25813     
25814     hoverState : null, //???
25815     
25816     placement : 'bottom', 
25817     
25818     alignment : false,
25819     
25820     getAutoCreate : function(){
25821     
25822         var cfg = {
25823            cls : 'tooltip',
25824            role : 'tooltip',
25825            cn : [
25826                 {
25827                     cls : 'tooltip-arrow'
25828                 },
25829                 {
25830                     cls : 'tooltip-inner'
25831                 }
25832            ]
25833         };
25834         
25835         return cfg;
25836     },
25837     bind : function(el)
25838     {
25839         this.bindEl = el;
25840     },
25841       
25842     
25843     enter : function () {
25844        
25845         if (this.timeout != null) {
25846             clearTimeout(this.timeout);
25847         }
25848         
25849         this.hoverState = 'in';
25850          //Roo.log("enter - show");
25851         if (!this.delay || !this.delay.show) {
25852             this.show();
25853             return;
25854         }
25855         var _t = this;
25856         this.timeout = setTimeout(function () {
25857             if (_t.hoverState == 'in') {
25858                 _t.show();
25859             }
25860         }, this.delay.show);
25861     },
25862     leave : function()
25863     {
25864         clearTimeout(this.timeout);
25865     
25866         this.hoverState = 'out';
25867          if (!this.delay || !this.delay.hide) {
25868             this.hide();
25869             return;
25870         }
25871        
25872         var _t = this;
25873         this.timeout = setTimeout(function () {
25874             //Roo.log("leave - timeout");
25875             
25876             if (_t.hoverState == 'out') {
25877                 _t.hide();
25878                 Roo.bootstrap.Tooltip.currentEl = false;
25879             }
25880         }, delay);
25881     },
25882     
25883     show : function (msg)
25884     {
25885         if (!this.el) {
25886             this.render(document.body);
25887         }
25888         // set content.
25889         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25890         
25891         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25892         
25893         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25894         
25895         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25896         
25897         var placement = typeof this.placement == 'function' ?
25898             this.placement.call(this, this.el, on_el) :
25899             this.placement;
25900             
25901         var autoToken = /\s?auto?\s?/i;
25902         var autoPlace = autoToken.test(placement);
25903         if (autoPlace) {
25904             placement = placement.replace(autoToken, '') || 'top';
25905         }
25906         
25907         //this.el.detach()
25908         //this.el.setXY([0,0]);
25909         this.el.show();
25910         //this.el.dom.style.display='block';
25911         
25912         //this.el.appendTo(on_el);
25913         
25914         var p = this.getPosition();
25915         var box = this.el.getBox();
25916         
25917         if (autoPlace) {
25918             // fixme..
25919         }
25920         
25921         var align = this.alignment[placement];
25922         
25923         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25924         
25925         if(placement == 'top' || placement == 'bottom'){
25926             if(xy[0] < 0){
25927                 placement = 'right';
25928             }
25929             
25930             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25931                 placement = 'left';
25932             }
25933             
25934             var scroll = Roo.select('body', true).first().getScroll();
25935             
25936             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25937                 placement = 'top';
25938             }
25939             
25940         }
25941         
25942         this.el.alignTo(this.bindEl, align[0],align[1]);
25943         //var arrow = this.el.select('.arrow',true).first();
25944         //arrow.set(align[2], 
25945         
25946         this.el.addClass(placement);
25947         
25948         this.el.addClass('in fade');
25949         
25950         this.hoverState = null;
25951         
25952         if (this.el.hasClass('fade')) {
25953             // fade it?
25954         }
25955         
25956     },
25957     hide : function()
25958     {
25959          
25960         if (!this.el) {
25961             return;
25962         }
25963         //this.el.setXY([0,0]);
25964         this.el.removeClass('in');
25965         //this.el.hide();
25966         
25967     }
25968     
25969 });
25970  
25971
25972  /*
25973  * - LGPL
25974  *
25975  * Location Picker
25976  * 
25977  */
25978
25979 /**
25980  * @class Roo.bootstrap.LocationPicker
25981  * @extends Roo.bootstrap.Component
25982  * Bootstrap LocationPicker class
25983  * @cfg {Number} latitude Position when init default 0
25984  * @cfg {Number} longitude Position when init default 0
25985  * @cfg {Number} zoom default 15
25986  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25987  * @cfg {Boolean} mapTypeControl default false
25988  * @cfg {Boolean} disableDoubleClickZoom default false
25989  * @cfg {Boolean} scrollwheel default true
25990  * @cfg {Boolean} streetViewControl default false
25991  * @cfg {Number} radius default 0
25992  * @cfg {String} locationName
25993  * @cfg {Boolean} draggable default true
25994  * @cfg {Boolean} enableAutocomplete default false
25995  * @cfg {Boolean} enableReverseGeocode default true
25996  * @cfg {String} markerTitle
25997  * 
25998  * @constructor
25999  * Create a new LocationPicker
26000  * @param {Object} config The config object
26001  */
26002
26003
26004 Roo.bootstrap.LocationPicker = function(config){
26005     
26006     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26007     
26008     this.addEvents({
26009         /**
26010          * @event initial
26011          * Fires when the picker initialized.
26012          * @param {Roo.bootstrap.LocationPicker} this
26013          * @param {Google Location} location
26014          */
26015         initial : true,
26016         /**
26017          * @event positionchanged
26018          * Fires when the picker position changed.
26019          * @param {Roo.bootstrap.LocationPicker} this
26020          * @param {Google Location} location
26021          */
26022         positionchanged : true,
26023         /**
26024          * @event resize
26025          * Fires when the map resize.
26026          * @param {Roo.bootstrap.LocationPicker} this
26027          */
26028         resize : true,
26029         /**
26030          * @event show
26031          * Fires when the map show.
26032          * @param {Roo.bootstrap.LocationPicker} this
26033          */
26034         show : true,
26035         /**
26036          * @event hide
26037          * Fires when the map hide.
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          */
26040         hide : true,
26041         /**
26042          * @event mapClick
26043          * Fires when click the map.
26044          * @param {Roo.bootstrap.LocationPicker} this
26045          * @param {Map event} e
26046          */
26047         mapClick : true,
26048         /**
26049          * @event mapRightClick
26050          * Fires when right click the map.
26051          * @param {Roo.bootstrap.LocationPicker} this
26052          * @param {Map event} e
26053          */
26054         mapRightClick : true,
26055         /**
26056          * @event markerClick
26057          * Fires when click the marker.
26058          * @param {Roo.bootstrap.LocationPicker} this
26059          * @param {Map event} e
26060          */
26061         markerClick : true,
26062         /**
26063          * @event markerRightClick
26064          * Fires when right click the marker.
26065          * @param {Roo.bootstrap.LocationPicker} this
26066          * @param {Map event} e
26067          */
26068         markerRightClick : true,
26069         /**
26070          * @event OverlayViewDraw
26071          * Fires when OverlayView Draw
26072          * @param {Roo.bootstrap.LocationPicker} this
26073          */
26074         OverlayViewDraw : true,
26075         /**
26076          * @event OverlayViewOnAdd
26077          * Fires when OverlayView Draw
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          */
26080         OverlayViewOnAdd : true,
26081         /**
26082          * @event OverlayViewOnRemove
26083          * Fires when OverlayView Draw
26084          * @param {Roo.bootstrap.LocationPicker} this
26085          */
26086         OverlayViewOnRemove : true,
26087         /**
26088          * @event OverlayViewShow
26089          * Fires when OverlayView Draw
26090          * @param {Roo.bootstrap.LocationPicker} this
26091          * @param {Pixel} cpx
26092          */
26093         OverlayViewShow : true,
26094         /**
26095          * @event OverlayViewHide
26096          * Fires when OverlayView Draw
26097          * @param {Roo.bootstrap.LocationPicker} this
26098          */
26099         OverlayViewHide : true,
26100         /**
26101          * @event loadexception
26102          * Fires when load google lib failed.
26103          * @param {Roo.bootstrap.LocationPicker} this
26104          */
26105         loadexception : true
26106     });
26107         
26108 };
26109
26110 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26111     
26112     gMapContext: false,
26113     
26114     latitude: 0,
26115     longitude: 0,
26116     zoom: 15,
26117     mapTypeId: false,
26118     mapTypeControl: false,
26119     disableDoubleClickZoom: false,
26120     scrollwheel: true,
26121     streetViewControl: false,
26122     radius: 0,
26123     locationName: '',
26124     draggable: true,
26125     enableAutocomplete: false,
26126     enableReverseGeocode: true,
26127     markerTitle: '',
26128     
26129     getAutoCreate: function()
26130     {
26131
26132         var cfg = {
26133             tag: 'div',
26134             cls: 'roo-location-picker'
26135         };
26136         
26137         return cfg
26138     },
26139     
26140     initEvents: function(ct, position)
26141     {       
26142         if(!this.el.getWidth() || this.isApplied()){
26143             return;
26144         }
26145         
26146         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26147         
26148         this.initial();
26149     },
26150     
26151     initial: function()
26152     {
26153         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26154             this.fireEvent('loadexception', this);
26155             return;
26156         }
26157         
26158         if(!this.mapTypeId){
26159             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26160         }
26161         
26162         this.gMapContext = this.GMapContext();
26163         
26164         this.initOverlayView();
26165         
26166         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26167         
26168         var _this = this;
26169                 
26170         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26171             _this.setPosition(_this.gMapContext.marker.position);
26172         });
26173         
26174         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26175             _this.fireEvent('mapClick', this, event);
26176             
26177         });
26178
26179         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26180             _this.fireEvent('mapRightClick', this, event);
26181             
26182         });
26183         
26184         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26185             _this.fireEvent('markerClick', this, event);
26186             
26187         });
26188
26189         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26190             _this.fireEvent('markerRightClick', this, event);
26191             
26192         });
26193         
26194         this.setPosition(this.gMapContext.location);
26195         
26196         this.fireEvent('initial', this, this.gMapContext.location);
26197     },
26198     
26199     initOverlayView: function()
26200     {
26201         var _this = this;
26202         
26203         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26204             
26205             draw: function()
26206             {
26207                 _this.fireEvent('OverlayViewDraw', _this);
26208             },
26209             
26210             onAdd: function()
26211             {
26212                 _this.fireEvent('OverlayViewOnAdd', _this);
26213             },
26214             
26215             onRemove: function()
26216             {
26217                 _this.fireEvent('OverlayViewOnRemove', _this);
26218             },
26219             
26220             show: function(cpx)
26221             {
26222                 _this.fireEvent('OverlayViewShow', _this, cpx);
26223             },
26224             
26225             hide: function()
26226             {
26227                 _this.fireEvent('OverlayViewHide', _this);
26228             }
26229             
26230         });
26231     },
26232     
26233     fromLatLngToContainerPixel: function(event)
26234     {
26235         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26236     },
26237     
26238     isApplied: function() 
26239     {
26240         return this.getGmapContext() == false ? false : true;
26241     },
26242     
26243     getGmapContext: function() 
26244     {
26245         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26246     },
26247     
26248     GMapContext: function() 
26249     {
26250         var position = new google.maps.LatLng(this.latitude, this.longitude);
26251         
26252         var _map = new google.maps.Map(this.el.dom, {
26253             center: position,
26254             zoom: this.zoom,
26255             mapTypeId: this.mapTypeId,
26256             mapTypeControl: this.mapTypeControl,
26257             disableDoubleClickZoom: this.disableDoubleClickZoom,
26258             scrollwheel: this.scrollwheel,
26259             streetViewControl: this.streetViewControl,
26260             locationName: this.locationName,
26261             draggable: this.draggable,
26262             enableAutocomplete: this.enableAutocomplete,
26263             enableReverseGeocode: this.enableReverseGeocode
26264         });
26265         
26266         var _marker = new google.maps.Marker({
26267             position: position,
26268             map: _map,
26269             title: this.markerTitle,
26270             draggable: this.draggable
26271         });
26272         
26273         return {
26274             map: _map,
26275             marker: _marker,
26276             circle: null,
26277             location: position,
26278             radius: this.radius,
26279             locationName: this.locationName,
26280             addressComponents: {
26281                 formatted_address: null,
26282                 addressLine1: null,
26283                 addressLine2: null,
26284                 streetName: null,
26285                 streetNumber: null,
26286                 city: null,
26287                 district: null,
26288                 state: null,
26289                 stateOrProvince: null
26290             },
26291             settings: this,
26292             domContainer: this.el.dom,
26293             geodecoder: new google.maps.Geocoder()
26294         };
26295     },
26296     
26297     drawCircle: function(center, radius, options) 
26298     {
26299         if (this.gMapContext.circle != null) {
26300             this.gMapContext.circle.setMap(null);
26301         }
26302         if (radius > 0) {
26303             radius *= 1;
26304             options = Roo.apply({}, options, {
26305                 strokeColor: "#0000FF",
26306                 strokeOpacity: .35,
26307                 strokeWeight: 2,
26308                 fillColor: "#0000FF",
26309                 fillOpacity: .2
26310             });
26311             
26312             options.map = this.gMapContext.map;
26313             options.radius = radius;
26314             options.center = center;
26315             this.gMapContext.circle = new google.maps.Circle(options);
26316             return this.gMapContext.circle;
26317         }
26318         
26319         return null;
26320     },
26321     
26322     setPosition: function(location) 
26323     {
26324         this.gMapContext.location = location;
26325         this.gMapContext.marker.setPosition(location);
26326         this.gMapContext.map.panTo(location);
26327         this.drawCircle(location, this.gMapContext.radius, {});
26328         
26329         var _this = this;
26330         
26331         if (this.gMapContext.settings.enableReverseGeocode) {
26332             this.gMapContext.geodecoder.geocode({
26333                 latLng: this.gMapContext.location
26334             }, function(results, status) {
26335                 
26336                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26337                     _this.gMapContext.locationName = results[0].formatted_address;
26338                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26339                     
26340                     _this.fireEvent('positionchanged', this, location);
26341                 }
26342             });
26343             
26344             return;
26345         }
26346         
26347         this.fireEvent('positionchanged', this, location);
26348     },
26349     
26350     resize: function()
26351     {
26352         google.maps.event.trigger(this.gMapContext.map, "resize");
26353         
26354         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26355         
26356         this.fireEvent('resize', this);
26357     },
26358     
26359     setPositionByLatLng: function(latitude, longitude)
26360     {
26361         this.setPosition(new google.maps.LatLng(latitude, longitude));
26362     },
26363     
26364     getCurrentPosition: function() 
26365     {
26366         return {
26367             latitude: this.gMapContext.location.lat(),
26368             longitude: this.gMapContext.location.lng()
26369         };
26370     },
26371     
26372     getAddressName: function() 
26373     {
26374         return this.gMapContext.locationName;
26375     },
26376     
26377     getAddressComponents: function() 
26378     {
26379         return this.gMapContext.addressComponents;
26380     },
26381     
26382     address_component_from_google_geocode: function(address_components) 
26383     {
26384         var result = {};
26385         
26386         for (var i = 0; i < address_components.length; i++) {
26387             var component = address_components[i];
26388             if (component.types.indexOf("postal_code") >= 0) {
26389                 result.postalCode = component.short_name;
26390             } else if (component.types.indexOf("street_number") >= 0) {
26391                 result.streetNumber = component.short_name;
26392             } else if (component.types.indexOf("route") >= 0) {
26393                 result.streetName = component.short_name;
26394             } else if (component.types.indexOf("neighborhood") >= 0) {
26395                 result.city = component.short_name;
26396             } else if (component.types.indexOf("locality") >= 0) {
26397                 result.city = component.short_name;
26398             } else if (component.types.indexOf("sublocality") >= 0) {
26399                 result.district = component.short_name;
26400             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26401                 result.stateOrProvince = component.short_name;
26402             } else if (component.types.indexOf("country") >= 0) {
26403                 result.country = component.short_name;
26404             }
26405         }
26406         
26407         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26408         result.addressLine2 = "";
26409         return result;
26410     },
26411     
26412     setZoomLevel: function(zoom)
26413     {
26414         this.gMapContext.map.setZoom(zoom);
26415     },
26416     
26417     show: function()
26418     {
26419         if(!this.el){
26420             return;
26421         }
26422         
26423         this.el.show();
26424         
26425         this.resize();
26426         
26427         this.fireEvent('show', this);
26428     },
26429     
26430     hide: function()
26431     {
26432         if(!this.el){
26433             return;
26434         }
26435         
26436         this.el.hide();
26437         
26438         this.fireEvent('hide', this);
26439     }
26440     
26441 });
26442
26443 Roo.apply(Roo.bootstrap.LocationPicker, {
26444     
26445     OverlayView : function(map, options)
26446     {
26447         options = options || {};
26448         
26449         this.setMap(map);
26450     }
26451     
26452     
26453 });/*
26454  * - LGPL
26455  *
26456  * Alert
26457  * 
26458  */
26459
26460 /**
26461  * @class Roo.bootstrap.Alert
26462  * @extends Roo.bootstrap.Component
26463  * Bootstrap Alert class
26464  * @cfg {String} title The title of alert
26465  * @cfg {String} html The content of alert
26466  * @cfg {String} weight (  success | info | warning | danger )
26467  * @cfg {String} faicon font-awesomeicon
26468  * 
26469  * @constructor
26470  * Create a new alert
26471  * @param {Object} config The config object
26472  */
26473
26474
26475 Roo.bootstrap.Alert = function(config){
26476     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26477     
26478 };
26479
26480 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26481     
26482     title: '',
26483     html: '',
26484     weight: false,
26485     faicon: false,
26486     
26487     getAutoCreate : function()
26488     {
26489         
26490         var cfg = {
26491             tag : 'div',
26492             cls : 'alert',
26493             cn : [
26494                 {
26495                     tag : 'i',
26496                     cls : 'roo-alert-icon'
26497                     
26498                 },
26499                 {
26500                     tag : 'b',
26501                     cls : 'roo-alert-title',
26502                     html : this.title
26503                 },
26504                 {
26505                     tag : 'span',
26506                     cls : 'roo-alert-text',
26507                     html : this.html
26508                 }
26509             ]
26510         };
26511         
26512         if(this.faicon){
26513             cfg.cn[0].cls += ' fa ' + this.faicon;
26514         }
26515         
26516         if(this.weight){
26517             cfg.cls += ' alert-' + this.weight;
26518         }
26519         
26520         return cfg;
26521     },
26522     
26523     initEvents: function() 
26524     {
26525         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26526     },
26527     
26528     setTitle : function(str)
26529     {
26530         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26531     },
26532     
26533     setText : function(str)
26534     {
26535         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26536     },
26537     
26538     setWeight : function(weight)
26539     {
26540         if(this.weight){
26541             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26542         }
26543         
26544         this.weight = weight;
26545         
26546         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26547     },
26548     
26549     setIcon : function(icon)
26550     {
26551         if(this.faicon){
26552             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26553         }
26554         
26555         this.faicon = icon;
26556         
26557         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26558     },
26559     
26560     hide: function() 
26561     {
26562         this.el.hide();   
26563     },
26564     
26565     show: function() 
26566     {  
26567         this.el.show();   
26568     }
26569     
26570 });
26571
26572  
26573 /*
26574 * Licence: LGPL
26575 */
26576
26577 /**
26578  * @class Roo.bootstrap.UploadCropbox
26579  * @extends Roo.bootstrap.Component
26580  * Bootstrap UploadCropbox class
26581  * @cfg {String} emptyText show when image has been loaded
26582  * @cfg {String} rotateNotify show when image too small to rotate
26583  * @cfg {Number} errorTimeout default 3000
26584  * @cfg {Number} minWidth default 300
26585  * @cfg {Number} minHeight default 300
26586  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26587  * @cfg {Boolean} isDocument (true|false) default false
26588  * @cfg {String} url action url
26589  * @cfg {String} paramName default 'imageUpload'
26590  * @cfg {String} method default POST
26591  * @cfg {Boolean} loadMask (true|false) default true
26592  * @cfg {Boolean} loadingText default 'Loading...'
26593  * 
26594  * @constructor
26595  * Create a new UploadCropbox
26596  * @param {Object} config The config object
26597  */
26598
26599 Roo.bootstrap.UploadCropbox = function(config){
26600     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26601     
26602     this.addEvents({
26603         /**
26604          * @event beforeselectfile
26605          * Fire before select file
26606          * @param {Roo.bootstrap.UploadCropbox} this
26607          */
26608         "beforeselectfile" : true,
26609         /**
26610          * @event initial
26611          * Fire after initEvent
26612          * @param {Roo.bootstrap.UploadCropbox} this
26613          */
26614         "initial" : true,
26615         /**
26616          * @event crop
26617          * Fire after initEvent
26618          * @param {Roo.bootstrap.UploadCropbox} this
26619          * @param {String} data
26620          */
26621         "crop" : true,
26622         /**
26623          * @event prepare
26624          * Fire when preparing the file data
26625          * @param {Roo.bootstrap.UploadCropbox} this
26626          * @param {Object} file
26627          */
26628         "prepare" : true,
26629         /**
26630          * @event exception
26631          * Fire when get exception
26632          * @param {Roo.bootstrap.UploadCropbox} this
26633          * @param {XMLHttpRequest} xhr
26634          */
26635         "exception" : true,
26636         /**
26637          * @event beforeloadcanvas
26638          * Fire before load the canvas
26639          * @param {Roo.bootstrap.UploadCropbox} this
26640          * @param {String} src
26641          */
26642         "beforeloadcanvas" : true,
26643         /**
26644          * @event trash
26645          * Fire when trash image
26646          * @param {Roo.bootstrap.UploadCropbox} this
26647          */
26648         "trash" : true,
26649         /**
26650          * @event download
26651          * Fire when download the image
26652          * @param {Roo.bootstrap.UploadCropbox} this
26653          */
26654         "download" : true,
26655         /**
26656          * @event footerbuttonclick
26657          * Fire when footerbuttonclick
26658          * @param {Roo.bootstrap.UploadCropbox} this
26659          * @param {String} type
26660          */
26661         "footerbuttonclick" : true,
26662         /**
26663          * @event resize
26664          * Fire when resize
26665          * @param {Roo.bootstrap.UploadCropbox} this
26666          */
26667         "resize" : true,
26668         /**
26669          * @event rotate
26670          * Fire when rotate the image
26671          * @param {Roo.bootstrap.UploadCropbox} this
26672          * @param {String} pos
26673          */
26674         "rotate" : true,
26675         /**
26676          * @event inspect
26677          * Fire when inspect the file
26678          * @param {Roo.bootstrap.UploadCropbox} this
26679          * @param {Object} file
26680          */
26681         "inspect" : true,
26682         /**
26683          * @event upload
26684          * Fire when xhr upload the file
26685          * @param {Roo.bootstrap.UploadCropbox} this
26686          * @param {Object} data
26687          */
26688         "upload" : true,
26689         /**
26690          * @event arrange
26691          * Fire when arrange the file data
26692          * @param {Roo.bootstrap.UploadCropbox} this
26693          * @param {Object} formData
26694          */
26695         "arrange" : true
26696     });
26697     
26698     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26699 };
26700
26701 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26702     
26703     emptyText : 'Click to upload image',
26704     rotateNotify : 'Image is too small to rotate',
26705     errorTimeout : 3000,
26706     scale : 0,
26707     baseScale : 1,
26708     rotate : 0,
26709     dragable : false,
26710     pinching : false,
26711     mouseX : 0,
26712     mouseY : 0,
26713     cropData : false,
26714     minWidth : 300,
26715     minHeight : 300,
26716     file : false,
26717     exif : {},
26718     baseRotate : 1,
26719     cropType : 'image/jpeg',
26720     buttons : false,
26721     canvasLoaded : false,
26722     isDocument : false,
26723     method : 'POST',
26724     paramName : 'imageUpload',
26725     loadMask : true,
26726     loadingText : 'Loading...',
26727     maskEl : false,
26728     
26729     getAutoCreate : function()
26730     {
26731         var cfg = {
26732             tag : 'div',
26733             cls : 'roo-upload-cropbox',
26734             cn : [
26735                 {
26736                     tag : 'input',
26737                     cls : 'roo-upload-cropbox-selector',
26738                     type : 'file'
26739                 },
26740                 {
26741                     tag : 'div',
26742                     cls : 'roo-upload-cropbox-body',
26743                     style : 'cursor:pointer',
26744                     cn : [
26745                         {
26746                             tag : 'div',
26747                             cls : 'roo-upload-cropbox-preview'
26748                         },
26749                         {
26750                             tag : 'div',
26751                             cls : 'roo-upload-cropbox-thumb'
26752                         },
26753                         {
26754                             tag : 'div',
26755                             cls : 'roo-upload-cropbox-empty-notify',
26756                             html : this.emptyText
26757                         },
26758                         {
26759                             tag : 'div',
26760                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26761                             html : this.rotateNotify
26762                         }
26763                     ]
26764                 },
26765                 {
26766                     tag : 'div',
26767                     cls : 'roo-upload-cropbox-footer',
26768                     cn : {
26769                         tag : 'div',
26770                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26771                         cn : []
26772                     }
26773                 }
26774             ]
26775         };
26776         
26777         return cfg;
26778     },
26779     
26780     onRender : function(ct, position)
26781     {
26782         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26783         
26784         if (this.buttons.length) {
26785             
26786             Roo.each(this.buttons, function(bb) {
26787                 
26788                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26789                 
26790                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26791                 
26792             }, this);
26793         }
26794         
26795         if(this.loadMask){
26796             this.maskEl = this.el;
26797         }
26798     },
26799     
26800     initEvents : function()
26801     {
26802         this.urlAPI = (window.createObjectURL && window) || 
26803                                 (window.URL && URL.revokeObjectURL && URL) || 
26804                                 (window.webkitURL && webkitURL);
26805                         
26806         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26807         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26808         
26809         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26810         this.selectorEl.hide();
26811         
26812         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26813         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26814         
26815         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26816         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26817         this.thumbEl.hide();
26818         
26819         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26820         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26821         
26822         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26823         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26824         this.errorEl.hide();
26825         
26826         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26827         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26828         this.footerEl.hide();
26829         
26830         this.setThumbBoxSize();
26831         
26832         this.bind();
26833         
26834         this.resize();
26835         
26836         this.fireEvent('initial', this);
26837     },
26838
26839     bind : function()
26840     {
26841         var _this = this;
26842         
26843         window.addEventListener("resize", function() { _this.resize(); } );
26844         
26845         this.bodyEl.on('click', this.beforeSelectFile, this);
26846         
26847         if(Roo.isTouch){
26848             this.bodyEl.on('touchstart', this.onTouchStart, this);
26849             this.bodyEl.on('touchmove', this.onTouchMove, this);
26850             this.bodyEl.on('touchend', this.onTouchEnd, this);
26851         }
26852         
26853         if(!Roo.isTouch){
26854             this.bodyEl.on('mousedown', this.onMouseDown, this);
26855             this.bodyEl.on('mousemove', this.onMouseMove, this);
26856             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26857             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26858             Roo.get(document).on('mouseup', this.onMouseUp, this);
26859         }
26860         
26861         this.selectorEl.on('change', this.onFileSelected, this);
26862     },
26863     
26864     reset : function()
26865     {    
26866         this.scale = 0;
26867         this.baseScale = 1;
26868         this.rotate = 0;
26869         this.baseRotate = 1;
26870         this.dragable = false;
26871         this.pinching = false;
26872         this.mouseX = 0;
26873         this.mouseY = 0;
26874         this.cropData = false;
26875         this.notifyEl.dom.innerHTML = this.emptyText;
26876         
26877         this.selectorEl.dom.value = '';
26878         
26879     },
26880     
26881     resize : function()
26882     {
26883         if(this.fireEvent('resize', this) != false){
26884             this.setThumbBoxPosition();
26885             this.setCanvasPosition();
26886         }
26887     },
26888     
26889     onFooterButtonClick : function(e, el, o, type)
26890     {
26891         switch (type) {
26892             case 'rotate-left' :
26893                 this.onRotateLeft(e);
26894                 break;
26895             case 'rotate-right' :
26896                 this.onRotateRight(e);
26897                 break;
26898             case 'picture' :
26899                 this.beforeSelectFile(e);
26900                 break;
26901             case 'trash' :
26902                 this.trash(e);
26903                 break;
26904             case 'crop' :
26905                 this.crop(e);
26906                 break;
26907             case 'download' :
26908                 this.download(e);
26909                 break;
26910             default :
26911                 break;
26912         }
26913         
26914         this.fireEvent('footerbuttonclick', this, type);
26915     },
26916     
26917     beforeSelectFile : function(e)
26918     {
26919         e.preventDefault();
26920         
26921         if(this.fireEvent('beforeselectfile', this) != false){
26922             this.selectorEl.dom.click();
26923         }
26924     },
26925     
26926     onFileSelected : function(e)
26927     {
26928         e.preventDefault();
26929         
26930         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26931             return;
26932         }
26933         
26934         var file = this.selectorEl.dom.files[0];
26935         
26936         if(this.fireEvent('inspect', this, file) != false){
26937             this.prepare(file);
26938         }
26939         
26940     },
26941     
26942     trash : function(e)
26943     {
26944         this.fireEvent('trash', this);
26945     },
26946     
26947     download : function(e)
26948     {
26949         this.fireEvent('download', this);
26950     },
26951     
26952     loadCanvas : function(src)
26953     {   
26954         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26955             
26956             this.reset();
26957             
26958             this.imageEl = document.createElement('img');
26959             
26960             var _this = this;
26961             
26962             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26963             
26964             this.imageEl.src = src;
26965         }
26966     },
26967     
26968     onLoadCanvas : function()
26969     {   
26970         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26971         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26972         
26973         this.bodyEl.un('click', this.beforeSelectFile, this);
26974         
26975         this.notifyEl.hide();
26976         this.thumbEl.show();
26977         this.footerEl.show();
26978         
26979         this.baseRotateLevel();
26980         
26981         if(this.isDocument){
26982             this.setThumbBoxSize();
26983         }
26984         
26985         this.setThumbBoxPosition();
26986         
26987         this.baseScaleLevel();
26988         
26989         this.draw();
26990         
26991         this.resize();
26992         
26993         this.canvasLoaded = true;
26994         
26995         if(this.loadMask){
26996             this.maskEl.unmask();
26997         }
26998         
26999     },
27000     
27001     setCanvasPosition : function()
27002     {   
27003         if(!this.canvasEl){
27004             return;
27005         }
27006         
27007         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27008         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27009         
27010         this.previewEl.setLeft(pw);
27011         this.previewEl.setTop(ph);
27012         
27013     },
27014     
27015     onMouseDown : function(e)
27016     {   
27017         e.stopEvent();
27018         
27019         this.dragable = true;
27020         this.pinching = false;
27021         
27022         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27023             this.dragable = false;
27024             return;
27025         }
27026         
27027         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27028         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27029         
27030     },
27031     
27032     onMouseMove : function(e)
27033     {   
27034         e.stopEvent();
27035         
27036         if(!this.canvasLoaded){
27037             return;
27038         }
27039         
27040         if (!this.dragable){
27041             return;
27042         }
27043         
27044         var minX = Math.ceil(this.thumbEl.getLeft(true));
27045         var minY = Math.ceil(this.thumbEl.getTop(true));
27046         
27047         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27048         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27049         
27050         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27051         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27052         
27053         x = x - this.mouseX;
27054         y = y - this.mouseY;
27055         
27056         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27057         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27058         
27059         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27060         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27061         
27062         this.previewEl.setLeft(bgX);
27063         this.previewEl.setTop(bgY);
27064         
27065         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27066         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27067     },
27068     
27069     onMouseUp : function(e)
27070     {   
27071         e.stopEvent();
27072         
27073         this.dragable = false;
27074     },
27075     
27076     onMouseWheel : function(e)
27077     {   
27078         e.stopEvent();
27079         
27080         this.startScale = this.scale;
27081         
27082         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27083         
27084         if(!this.zoomable()){
27085             this.scale = this.startScale;
27086             return;
27087         }
27088         
27089         this.draw();
27090         
27091         return;
27092     },
27093     
27094     zoomable : function()
27095     {
27096         var minScale = this.thumbEl.getWidth() / this.minWidth;
27097         
27098         if(this.minWidth < this.minHeight){
27099             minScale = this.thumbEl.getHeight() / this.minHeight;
27100         }
27101         
27102         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27103         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27104         
27105         if(
27106                 this.isDocument &&
27107                 (this.rotate == 0 || this.rotate == 180) && 
27108                 (
27109                     width > this.imageEl.OriginWidth || 
27110                     height > this.imageEl.OriginHeight ||
27111                     (width < this.minWidth && height < this.minHeight)
27112                 )
27113         ){
27114             return false;
27115         }
27116         
27117         if(
27118                 this.isDocument &&
27119                 (this.rotate == 90 || this.rotate == 270) && 
27120                 (
27121                     width > this.imageEl.OriginWidth || 
27122                     height > this.imageEl.OriginHeight ||
27123                     (width < this.minHeight && height < this.minWidth)
27124                 )
27125         ){
27126             return false;
27127         }
27128         
27129         if(
27130                 !this.isDocument &&
27131                 (this.rotate == 0 || this.rotate == 180) && 
27132                 (
27133                     width < this.minWidth || 
27134                     width > this.imageEl.OriginWidth || 
27135                     height < this.minHeight || 
27136                     height > this.imageEl.OriginHeight
27137                 )
27138         ){
27139             return false;
27140         }
27141         
27142         if(
27143                 !this.isDocument &&
27144                 (this.rotate == 90 || this.rotate == 270) && 
27145                 (
27146                     width < this.minHeight || 
27147                     width > this.imageEl.OriginWidth || 
27148                     height < this.minWidth || 
27149                     height > this.imageEl.OriginHeight
27150                 )
27151         ){
27152             return false;
27153         }
27154         
27155         return true;
27156         
27157     },
27158     
27159     onRotateLeft : function(e)
27160     {   
27161         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27162             
27163             var minScale = this.thumbEl.getWidth() / this.minWidth;
27164             
27165             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27166             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27167             
27168             this.startScale = this.scale;
27169             
27170             while (this.getScaleLevel() < minScale){
27171             
27172                 this.scale = this.scale + 1;
27173                 
27174                 if(!this.zoomable()){
27175                     break;
27176                 }
27177                 
27178                 if(
27179                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27180                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27181                 ){
27182                     continue;
27183                 }
27184                 
27185                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27186
27187                 this.draw();
27188                 
27189                 return;
27190             }
27191             
27192             this.scale = this.startScale;
27193             
27194             this.onRotateFail();
27195             
27196             return false;
27197         }
27198         
27199         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27200
27201         if(this.isDocument){
27202             this.setThumbBoxSize();
27203             this.setThumbBoxPosition();
27204             this.setCanvasPosition();
27205         }
27206         
27207         this.draw();
27208         
27209         this.fireEvent('rotate', this, 'left');
27210         
27211     },
27212     
27213     onRotateRight : function(e)
27214     {
27215         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27216             
27217             var minScale = this.thumbEl.getWidth() / this.minWidth;
27218         
27219             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27220             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27221             
27222             this.startScale = this.scale;
27223             
27224             while (this.getScaleLevel() < minScale){
27225             
27226                 this.scale = this.scale + 1;
27227                 
27228                 if(!this.zoomable()){
27229                     break;
27230                 }
27231                 
27232                 if(
27233                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27234                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27235                 ){
27236                     continue;
27237                 }
27238                 
27239                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27240
27241                 this.draw();
27242                 
27243                 return;
27244             }
27245             
27246             this.scale = this.startScale;
27247             
27248             this.onRotateFail();
27249             
27250             return false;
27251         }
27252         
27253         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27254
27255         if(this.isDocument){
27256             this.setThumbBoxSize();
27257             this.setThumbBoxPosition();
27258             this.setCanvasPosition();
27259         }
27260         
27261         this.draw();
27262         
27263         this.fireEvent('rotate', this, 'right');
27264     },
27265     
27266     onRotateFail : function()
27267     {
27268         this.errorEl.show(true);
27269         
27270         var _this = this;
27271         
27272         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27273     },
27274     
27275     draw : function()
27276     {
27277         this.previewEl.dom.innerHTML = '';
27278         
27279         var canvasEl = document.createElement("canvas");
27280         
27281         var contextEl = canvasEl.getContext("2d");
27282         
27283         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27284         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27285         var center = this.imageEl.OriginWidth / 2;
27286         
27287         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27288             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27289             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27290             center = this.imageEl.OriginHeight / 2;
27291         }
27292         
27293         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27294         
27295         contextEl.translate(center, center);
27296         contextEl.rotate(this.rotate * Math.PI / 180);
27297
27298         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27299         
27300         this.canvasEl = document.createElement("canvas");
27301         
27302         this.contextEl = this.canvasEl.getContext("2d");
27303         
27304         switch (this.rotate) {
27305             case 0 :
27306                 
27307                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27308                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27309                 
27310                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27311                 
27312                 break;
27313             case 90 : 
27314                 
27315                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27316                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27317                 
27318                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27319                     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);
27320                     break;
27321                 }
27322                 
27323                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27324                 
27325                 break;
27326             case 180 :
27327                 
27328                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27329                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27330                 
27331                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27332                     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);
27333                     break;
27334                 }
27335                 
27336                 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);
27337                 
27338                 break;
27339             case 270 :
27340                 
27341                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27342                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27343         
27344                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27345                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27346                     break;
27347                 }
27348                 
27349                 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);
27350                 
27351                 break;
27352             default : 
27353                 break;
27354         }
27355         
27356         this.previewEl.appendChild(this.canvasEl);
27357         
27358         this.setCanvasPosition();
27359     },
27360     
27361     crop : function()
27362     {
27363         if(!this.canvasLoaded){
27364             return;
27365         }
27366         
27367         var imageCanvas = document.createElement("canvas");
27368         
27369         var imageContext = imageCanvas.getContext("2d");
27370         
27371         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27372         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27373         
27374         var center = imageCanvas.width / 2;
27375         
27376         imageContext.translate(center, center);
27377         
27378         imageContext.rotate(this.rotate * Math.PI / 180);
27379         
27380         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27381         
27382         var canvas = document.createElement("canvas");
27383         
27384         var context = canvas.getContext("2d");
27385                 
27386         canvas.width = this.minWidth;
27387         canvas.height = this.minHeight;
27388
27389         switch (this.rotate) {
27390             case 0 :
27391                 
27392                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27393                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27394                 
27395                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27396                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27397                 
27398                 var targetWidth = this.minWidth - 2 * x;
27399                 var targetHeight = this.minHeight - 2 * y;
27400                 
27401                 var scale = 1;
27402                 
27403                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27404                     scale = targetWidth / width;
27405                 }
27406                 
27407                 if(x > 0 && y == 0){
27408                     scale = targetHeight / height;
27409                 }
27410                 
27411                 if(x > 0 && y > 0){
27412                     scale = targetWidth / width;
27413                     
27414                     if(width < height){
27415                         scale = targetHeight / height;
27416                     }
27417                 }
27418                 
27419                 context.scale(scale, scale);
27420                 
27421                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27422                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27423
27424                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27425                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27426
27427                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27428                 
27429                 break;
27430             case 90 : 
27431                 
27432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27434                 
27435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27437                 
27438                 var targetWidth = this.minWidth - 2 * x;
27439                 var targetHeight = this.minHeight - 2 * y;
27440                 
27441                 var scale = 1;
27442                 
27443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27444                     scale = targetWidth / width;
27445                 }
27446                 
27447                 if(x > 0 && y == 0){
27448                     scale = targetHeight / height;
27449                 }
27450                 
27451                 if(x > 0 && y > 0){
27452                     scale = targetWidth / width;
27453                     
27454                     if(width < height){
27455                         scale = targetHeight / height;
27456                     }
27457                 }
27458                 
27459                 context.scale(scale, scale);
27460                 
27461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27463
27464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27466                 
27467                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27468                 
27469                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27470                 
27471                 break;
27472             case 180 :
27473                 
27474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27476                 
27477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27479                 
27480                 var targetWidth = this.minWidth - 2 * x;
27481                 var targetHeight = this.minHeight - 2 * y;
27482                 
27483                 var scale = 1;
27484                 
27485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27486                     scale = targetWidth / width;
27487                 }
27488                 
27489                 if(x > 0 && y == 0){
27490                     scale = targetHeight / height;
27491                 }
27492                 
27493                 if(x > 0 && y > 0){
27494                     scale = targetWidth / width;
27495                     
27496                     if(width < height){
27497                         scale = targetHeight / height;
27498                     }
27499                 }
27500                 
27501                 context.scale(scale, scale);
27502                 
27503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27505
27506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27508
27509                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27510                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27511                 
27512                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27513                 
27514                 break;
27515             case 270 :
27516                 
27517                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27518                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27519                 
27520                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27521                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27522                 
27523                 var targetWidth = this.minWidth - 2 * x;
27524                 var targetHeight = this.minHeight - 2 * y;
27525                 
27526                 var scale = 1;
27527                 
27528                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27529                     scale = targetWidth / width;
27530                 }
27531                 
27532                 if(x > 0 && y == 0){
27533                     scale = targetHeight / height;
27534                 }
27535                 
27536                 if(x > 0 && y > 0){
27537                     scale = targetWidth / width;
27538                     
27539                     if(width < height){
27540                         scale = targetHeight / height;
27541                     }
27542                 }
27543                 
27544                 context.scale(scale, scale);
27545                 
27546                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27547                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27548
27549                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27550                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27551                 
27552                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27553                 
27554                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27555                 
27556                 break;
27557             default : 
27558                 break;
27559         }
27560         
27561         this.cropData = canvas.toDataURL(this.cropType);
27562         
27563         if(this.fireEvent('crop', this, this.cropData) !== false){
27564             this.process(this.file, this.cropData);
27565         }
27566         
27567         return;
27568         
27569     },
27570     
27571     setThumbBoxSize : function()
27572     {
27573         var width, height;
27574         
27575         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27576             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27577             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27578             
27579             this.minWidth = width;
27580             this.minHeight = height;
27581             
27582             if(this.rotate == 90 || this.rotate == 270){
27583                 this.minWidth = height;
27584                 this.minHeight = width;
27585             }
27586         }
27587         
27588         height = 300;
27589         width = Math.ceil(this.minWidth * height / this.minHeight);
27590         
27591         if(this.minWidth > this.minHeight){
27592             width = 300;
27593             height = Math.ceil(this.minHeight * width / this.minWidth);
27594         }
27595         
27596         this.thumbEl.setStyle({
27597             width : width + 'px',
27598             height : height + 'px'
27599         });
27600
27601         return;
27602             
27603     },
27604     
27605     setThumbBoxPosition : function()
27606     {
27607         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27608         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27609         
27610         this.thumbEl.setLeft(x);
27611         this.thumbEl.setTop(y);
27612         
27613     },
27614     
27615     baseRotateLevel : function()
27616     {
27617         this.baseRotate = 1;
27618         
27619         if(
27620                 typeof(this.exif) != 'undefined' &&
27621                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27622                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27623         ){
27624             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27625         }
27626         
27627         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27628         
27629     },
27630     
27631     baseScaleLevel : function()
27632     {
27633         var width, height;
27634         
27635         if(this.isDocument){
27636             
27637             if(this.baseRotate == 6 || this.baseRotate == 8){
27638             
27639                 height = this.thumbEl.getHeight();
27640                 this.baseScale = height / this.imageEl.OriginWidth;
27641
27642                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27643                     width = this.thumbEl.getWidth();
27644                     this.baseScale = width / this.imageEl.OriginHeight;
27645                 }
27646
27647                 return;
27648             }
27649
27650             height = this.thumbEl.getHeight();
27651             this.baseScale = height / this.imageEl.OriginHeight;
27652
27653             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27654                 width = this.thumbEl.getWidth();
27655                 this.baseScale = width / this.imageEl.OriginWidth;
27656             }
27657
27658             return;
27659         }
27660         
27661         if(this.baseRotate == 6 || this.baseRotate == 8){
27662             
27663             width = this.thumbEl.getHeight();
27664             this.baseScale = width / this.imageEl.OriginHeight;
27665             
27666             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27667                 height = this.thumbEl.getWidth();
27668                 this.baseScale = height / this.imageEl.OriginHeight;
27669             }
27670             
27671             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27672                 height = this.thumbEl.getWidth();
27673                 this.baseScale = height / this.imageEl.OriginHeight;
27674                 
27675                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27676                     width = this.thumbEl.getHeight();
27677                     this.baseScale = width / this.imageEl.OriginWidth;
27678                 }
27679             }
27680             
27681             return;
27682         }
27683         
27684         width = this.thumbEl.getWidth();
27685         this.baseScale = width / this.imageEl.OriginWidth;
27686         
27687         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27688             height = this.thumbEl.getHeight();
27689             this.baseScale = height / this.imageEl.OriginHeight;
27690         }
27691         
27692         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27693             
27694             height = this.thumbEl.getHeight();
27695             this.baseScale = height / this.imageEl.OriginHeight;
27696             
27697             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27698                 width = this.thumbEl.getWidth();
27699                 this.baseScale = width / this.imageEl.OriginWidth;
27700             }
27701             
27702         }
27703         
27704         return;
27705     },
27706     
27707     getScaleLevel : function()
27708     {
27709         return this.baseScale * Math.pow(1.1, this.scale);
27710     },
27711     
27712     onTouchStart : function(e)
27713     {
27714         if(!this.canvasLoaded){
27715             this.beforeSelectFile(e);
27716             return;
27717         }
27718         
27719         var touches = e.browserEvent.touches;
27720         
27721         if(!touches){
27722             return;
27723         }
27724         
27725         if(touches.length == 1){
27726             this.onMouseDown(e);
27727             return;
27728         }
27729         
27730         if(touches.length != 2){
27731             return;
27732         }
27733         
27734         var coords = [];
27735         
27736         for(var i = 0, finger; finger = touches[i]; i++){
27737             coords.push(finger.pageX, finger.pageY);
27738         }
27739         
27740         var x = Math.pow(coords[0] - coords[2], 2);
27741         var y = Math.pow(coords[1] - coords[3], 2);
27742         
27743         this.startDistance = Math.sqrt(x + y);
27744         
27745         this.startScale = this.scale;
27746         
27747         this.pinching = true;
27748         this.dragable = false;
27749         
27750     },
27751     
27752     onTouchMove : function(e)
27753     {
27754         if(!this.pinching && !this.dragable){
27755             return;
27756         }
27757         
27758         var touches = e.browserEvent.touches;
27759         
27760         if(!touches){
27761             return;
27762         }
27763         
27764         if(this.dragable){
27765             this.onMouseMove(e);
27766             return;
27767         }
27768         
27769         var coords = [];
27770         
27771         for(var i = 0, finger; finger = touches[i]; i++){
27772             coords.push(finger.pageX, finger.pageY);
27773         }
27774         
27775         var x = Math.pow(coords[0] - coords[2], 2);
27776         var y = Math.pow(coords[1] - coords[3], 2);
27777         
27778         this.endDistance = Math.sqrt(x + y);
27779         
27780         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27781         
27782         if(!this.zoomable()){
27783             this.scale = this.startScale;
27784             return;
27785         }
27786         
27787         this.draw();
27788         
27789     },
27790     
27791     onTouchEnd : function(e)
27792     {
27793         this.pinching = false;
27794         this.dragable = false;
27795         
27796     },
27797     
27798     process : function(file, crop)
27799     {
27800         if(this.loadMask){
27801             this.maskEl.mask(this.loadingText);
27802         }
27803         
27804         this.xhr = new XMLHttpRequest();
27805         
27806         file.xhr = this.xhr;
27807
27808         this.xhr.open(this.method, this.url, true);
27809         
27810         var headers = {
27811             "Accept": "application/json",
27812             "Cache-Control": "no-cache",
27813             "X-Requested-With": "XMLHttpRequest"
27814         };
27815         
27816         for (var headerName in headers) {
27817             var headerValue = headers[headerName];
27818             if (headerValue) {
27819                 this.xhr.setRequestHeader(headerName, headerValue);
27820             }
27821         }
27822         
27823         var _this = this;
27824         
27825         this.xhr.onload = function()
27826         {
27827             _this.xhrOnLoad(_this.xhr);
27828         }
27829         
27830         this.xhr.onerror = function()
27831         {
27832             _this.xhrOnError(_this.xhr);
27833         }
27834         
27835         var formData = new FormData();
27836
27837         formData.append('returnHTML', 'NO');
27838         
27839         if(crop){
27840             formData.append('crop', crop);
27841         }
27842         
27843         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27844             formData.append(this.paramName, file, file.name);
27845         }
27846         
27847         if(typeof(file.filename) != 'undefined'){
27848             formData.append('filename', file.filename);
27849         }
27850         
27851         if(typeof(file.mimetype) != 'undefined'){
27852             formData.append('mimetype', file.mimetype);
27853         }
27854         
27855         if(this.fireEvent('arrange', this, formData) != false){
27856             this.xhr.send(formData);
27857         };
27858     },
27859     
27860     xhrOnLoad : function(xhr)
27861     {
27862         if(this.loadMask){
27863             this.maskEl.unmask();
27864         }
27865         
27866         if (xhr.readyState !== 4) {
27867             this.fireEvent('exception', this, xhr);
27868             return;
27869         }
27870
27871         var response = Roo.decode(xhr.responseText);
27872         
27873         if(!response.success){
27874             this.fireEvent('exception', this, xhr);
27875             return;
27876         }
27877         
27878         var response = Roo.decode(xhr.responseText);
27879         
27880         this.fireEvent('upload', this, response);
27881         
27882     },
27883     
27884     xhrOnError : function()
27885     {
27886         if(this.loadMask){
27887             this.maskEl.unmask();
27888         }
27889         
27890         Roo.log('xhr on error');
27891         
27892         var response = Roo.decode(xhr.responseText);
27893           
27894         Roo.log(response);
27895         
27896     },
27897     
27898     prepare : function(file)
27899     {   
27900         if(this.loadMask){
27901             this.maskEl.mask(this.loadingText);
27902         }
27903         
27904         this.file = false;
27905         this.exif = {};
27906         
27907         if(typeof(file) === 'string'){
27908             this.loadCanvas(file);
27909             return;
27910         }
27911         
27912         if(!file || !this.urlAPI){
27913             return;
27914         }
27915         
27916         this.file = file;
27917         this.cropType = file.type;
27918         
27919         var _this = this;
27920         
27921         if(this.fireEvent('prepare', this, this.file) != false){
27922             
27923             var reader = new FileReader();
27924             
27925             reader.onload = function (e) {
27926                 if (e.target.error) {
27927                     Roo.log(e.target.error);
27928                     return;
27929                 }
27930                 
27931                 var buffer = e.target.result,
27932                     dataView = new DataView(buffer),
27933                     offset = 2,
27934                     maxOffset = dataView.byteLength - 4,
27935                     markerBytes,
27936                     markerLength;
27937                 
27938                 if (dataView.getUint16(0) === 0xffd8) {
27939                     while (offset < maxOffset) {
27940                         markerBytes = dataView.getUint16(offset);
27941                         
27942                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27943                             markerLength = dataView.getUint16(offset + 2) + 2;
27944                             if (offset + markerLength > dataView.byteLength) {
27945                                 Roo.log('Invalid meta data: Invalid segment size.');
27946                                 break;
27947                             }
27948                             
27949                             if(markerBytes == 0xffe1){
27950                                 _this.parseExifData(
27951                                     dataView,
27952                                     offset,
27953                                     markerLength
27954                                 );
27955                             }
27956                             
27957                             offset += markerLength;
27958                             
27959                             continue;
27960                         }
27961                         
27962                         break;
27963                     }
27964                     
27965                 }
27966                 
27967                 var url = _this.urlAPI.createObjectURL(_this.file);
27968                 
27969                 _this.loadCanvas(url);
27970                 
27971                 return;
27972             }
27973             
27974             reader.readAsArrayBuffer(this.file);
27975             
27976         }
27977         
27978     },
27979     
27980     parseExifData : function(dataView, offset, length)
27981     {
27982         var tiffOffset = offset + 10,
27983             littleEndian,
27984             dirOffset;
27985     
27986         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27987             // No Exif data, might be XMP data instead
27988             return;
27989         }
27990         
27991         // Check for the ASCII code for "Exif" (0x45786966):
27992         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27993             // No Exif data, might be XMP data instead
27994             return;
27995         }
27996         if (tiffOffset + 8 > dataView.byteLength) {
27997             Roo.log('Invalid Exif data: Invalid segment size.');
27998             return;
27999         }
28000         // Check for the two null bytes:
28001         if (dataView.getUint16(offset + 8) !== 0x0000) {
28002             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28003             return;
28004         }
28005         // Check the byte alignment:
28006         switch (dataView.getUint16(tiffOffset)) {
28007         case 0x4949:
28008             littleEndian = true;
28009             break;
28010         case 0x4D4D:
28011             littleEndian = false;
28012             break;
28013         default:
28014             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28015             return;
28016         }
28017         // Check for the TIFF tag marker (0x002A):
28018         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28019             Roo.log('Invalid Exif data: Missing TIFF marker.');
28020             return;
28021         }
28022         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28023         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28024         
28025         this.parseExifTags(
28026             dataView,
28027             tiffOffset,
28028             tiffOffset + dirOffset,
28029             littleEndian
28030         );
28031     },
28032     
28033     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28034     {
28035         var tagsNumber,
28036             dirEndOffset,
28037             i;
28038         if (dirOffset + 6 > dataView.byteLength) {
28039             Roo.log('Invalid Exif data: Invalid directory offset.');
28040             return;
28041         }
28042         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28043         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28044         if (dirEndOffset + 4 > dataView.byteLength) {
28045             Roo.log('Invalid Exif data: Invalid directory size.');
28046             return;
28047         }
28048         for (i = 0; i < tagsNumber; i += 1) {
28049             this.parseExifTag(
28050                 dataView,
28051                 tiffOffset,
28052                 dirOffset + 2 + 12 * i, // tag offset
28053                 littleEndian
28054             );
28055         }
28056         // Return the offset to the next directory:
28057         return dataView.getUint32(dirEndOffset, littleEndian);
28058     },
28059     
28060     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28061     {
28062         var tag = dataView.getUint16(offset, littleEndian);
28063         
28064         this.exif[tag] = this.getExifValue(
28065             dataView,
28066             tiffOffset,
28067             offset,
28068             dataView.getUint16(offset + 2, littleEndian), // tag type
28069             dataView.getUint32(offset + 4, littleEndian), // tag length
28070             littleEndian
28071         );
28072     },
28073     
28074     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28075     {
28076         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28077             tagSize,
28078             dataOffset,
28079             values,
28080             i,
28081             str,
28082             c;
28083     
28084         if (!tagType) {
28085             Roo.log('Invalid Exif data: Invalid tag type.');
28086             return;
28087         }
28088         
28089         tagSize = tagType.size * length;
28090         // Determine if the value is contained in the dataOffset bytes,
28091         // or if the value at the dataOffset is a pointer to the actual data:
28092         dataOffset = tagSize > 4 ?
28093                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28094         if (dataOffset + tagSize > dataView.byteLength) {
28095             Roo.log('Invalid Exif data: Invalid data offset.');
28096             return;
28097         }
28098         if (length === 1) {
28099             return tagType.getValue(dataView, dataOffset, littleEndian);
28100         }
28101         values = [];
28102         for (i = 0; i < length; i += 1) {
28103             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28104         }
28105         
28106         if (tagType.ascii) {
28107             str = '';
28108             // Concatenate the chars:
28109             for (i = 0; i < values.length; i += 1) {
28110                 c = values[i];
28111                 // Ignore the terminating NULL byte(s):
28112                 if (c === '\u0000') {
28113                     break;
28114                 }
28115                 str += c;
28116             }
28117             return str;
28118         }
28119         return values;
28120     }
28121     
28122 });
28123
28124 Roo.apply(Roo.bootstrap.UploadCropbox, {
28125     tags : {
28126         'Orientation': 0x0112
28127     },
28128     
28129     Orientation: {
28130             1: 0, //'top-left',
28131 //            2: 'top-right',
28132             3: 180, //'bottom-right',
28133 //            4: 'bottom-left',
28134 //            5: 'left-top',
28135             6: 90, //'right-top',
28136 //            7: 'right-bottom',
28137             8: 270 //'left-bottom'
28138     },
28139     
28140     exifTagTypes : {
28141         // byte, 8-bit unsigned int:
28142         1: {
28143             getValue: function (dataView, dataOffset) {
28144                 return dataView.getUint8(dataOffset);
28145             },
28146             size: 1
28147         },
28148         // ascii, 8-bit byte:
28149         2: {
28150             getValue: function (dataView, dataOffset) {
28151                 return String.fromCharCode(dataView.getUint8(dataOffset));
28152             },
28153             size: 1,
28154             ascii: true
28155         },
28156         // short, 16 bit int:
28157         3: {
28158             getValue: function (dataView, dataOffset, littleEndian) {
28159                 return dataView.getUint16(dataOffset, littleEndian);
28160             },
28161             size: 2
28162         },
28163         // long, 32 bit int:
28164         4: {
28165             getValue: function (dataView, dataOffset, littleEndian) {
28166                 return dataView.getUint32(dataOffset, littleEndian);
28167             },
28168             size: 4
28169         },
28170         // rational = two long values, first is numerator, second is denominator:
28171         5: {
28172             getValue: function (dataView, dataOffset, littleEndian) {
28173                 return dataView.getUint32(dataOffset, littleEndian) /
28174                     dataView.getUint32(dataOffset + 4, littleEndian);
28175             },
28176             size: 8
28177         },
28178         // slong, 32 bit signed int:
28179         9: {
28180             getValue: function (dataView, dataOffset, littleEndian) {
28181                 return dataView.getInt32(dataOffset, littleEndian);
28182             },
28183             size: 4
28184         },
28185         // srational, two slongs, first is numerator, second is denominator:
28186         10: {
28187             getValue: function (dataView, dataOffset, littleEndian) {
28188                 return dataView.getInt32(dataOffset, littleEndian) /
28189                     dataView.getInt32(dataOffset + 4, littleEndian);
28190             },
28191             size: 8
28192         }
28193     },
28194     
28195     footer : {
28196         STANDARD : [
28197             {
28198                 tag : 'div',
28199                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28200                 action : 'rotate-left',
28201                 cn : [
28202                     {
28203                         tag : 'button',
28204                         cls : 'btn btn-default',
28205                         html : '<i class="fa fa-undo"></i>'
28206                     }
28207                 ]
28208             },
28209             {
28210                 tag : 'div',
28211                 cls : 'btn-group roo-upload-cropbox-picture',
28212                 action : 'picture',
28213                 cn : [
28214                     {
28215                         tag : 'button',
28216                         cls : 'btn btn-default',
28217                         html : '<i class="fa fa-picture-o"></i>'
28218                     }
28219                 ]
28220             },
28221             {
28222                 tag : 'div',
28223                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28224                 action : 'rotate-right',
28225                 cn : [
28226                     {
28227                         tag : 'button',
28228                         cls : 'btn btn-default',
28229                         html : '<i class="fa fa-repeat"></i>'
28230                     }
28231                 ]
28232             }
28233         ],
28234         DOCUMENT : [
28235             {
28236                 tag : 'div',
28237                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28238                 action : 'rotate-left',
28239                 cn : [
28240                     {
28241                         tag : 'button',
28242                         cls : 'btn btn-default',
28243                         html : '<i class="fa fa-undo"></i>'
28244                     }
28245                 ]
28246             },
28247             {
28248                 tag : 'div',
28249                 cls : 'btn-group roo-upload-cropbox-download',
28250                 action : 'download',
28251                 cn : [
28252                     {
28253                         tag : 'button',
28254                         cls : 'btn btn-default',
28255                         html : '<i class="fa fa-download"></i>'
28256                     }
28257                 ]
28258             },
28259             {
28260                 tag : 'div',
28261                 cls : 'btn-group roo-upload-cropbox-crop',
28262                 action : 'crop',
28263                 cn : [
28264                     {
28265                         tag : 'button',
28266                         cls : 'btn btn-default',
28267                         html : '<i class="fa fa-crop"></i>'
28268                     }
28269                 ]
28270             },
28271             {
28272                 tag : 'div',
28273                 cls : 'btn-group roo-upload-cropbox-trash',
28274                 action : 'trash',
28275                 cn : [
28276                     {
28277                         tag : 'button',
28278                         cls : 'btn btn-default',
28279                         html : '<i class="fa fa-trash"></i>'
28280                     }
28281                 ]
28282             },
28283             {
28284                 tag : 'div',
28285                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28286                 action : 'rotate-right',
28287                 cn : [
28288                     {
28289                         tag : 'button',
28290                         cls : 'btn btn-default',
28291                         html : '<i class="fa fa-repeat"></i>'
28292                     }
28293                 ]
28294             }
28295         ],
28296         ROTATOR : [
28297             {
28298                 tag : 'div',
28299                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28300                 action : 'rotate-left',
28301                 cn : [
28302                     {
28303                         tag : 'button',
28304                         cls : 'btn btn-default',
28305                         html : '<i class="fa fa-undo"></i>'
28306                     }
28307                 ]
28308             },
28309             {
28310                 tag : 'div',
28311                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28312                 action : 'rotate-right',
28313                 cn : [
28314                     {
28315                         tag : 'button',
28316                         cls : 'btn btn-default',
28317                         html : '<i class="fa fa-repeat"></i>'
28318                     }
28319                 ]
28320             }
28321         ]
28322     }
28323 });
28324
28325 /*
28326 * Licence: LGPL
28327 */
28328
28329 /**
28330  * @class Roo.bootstrap.DocumentManager
28331  * @extends Roo.bootstrap.Component
28332  * Bootstrap DocumentManager class
28333  * @cfg {String} paramName default 'imageUpload'
28334  * @cfg {String} toolTipName default 'filename'
28335  * @cfg {String} method default POST
28336  * @cfg {String} url action url
28337  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28338  * @cfg {Boolean} multiple multiple upload default true
28339  * @cfg {Number} thumbSize default 300
28340  * @cfg {String} fieldLabel
28341  * @cfg {Number} labelWidth default 4
28342  * @cfg {String} labelAlign (left|top) default left
28343  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28344 * @cfg {Number} labellg set the width of label (1-12)
28345  * @cfg {Number} labelmd set the width of label (1-12)
28346  * @cfg {Number} labelsm set the width of label (1-12)
28347  * @cfg {Number} labelxs set the width of label (1-12)
28348  * 
28349  * @constructor
28350  * Create a new DocumentManager
28351  * @param {Object} config The config object
28352  */
28353
28354 Roo.bootstrap.DocumentManager = function(config){
28355     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28356     
28357     this.files = [];
28358     this.delegates = [];
28359     
28360     this.addEvents({
28361         /**
28362          * @event initial
28363          * Fire when initial the DocumentManager
28364          * @param {Roo.bootstrap.DocumentManager} this
28365          */
28366         "initial" : true,
28367         /**
28368          * @event inspect
28369          * inspect selected file
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          * @param {File} file
28372          */
28373         "inspect" : true,
28374         /**
28375          * @event exception
28376          * Fire when xhr load exception
28377          * @param {Roo.bootstrap.DocumentManager} this
28378          * @param {XMLHttpRequest} xhr
28379          */
28380         "exception" : true,
28381         /**
28382          * @event afterupload
28383          * Fire when xhr load exception
28384          * @param {Roo.bootstrap.DocumentManager} this
28385          * @param {XMLHttpRequest} xhr
28386          */
28387         "afterupload" : true,
28388         /**
28389          * @event prepare
28390          * prepare the form data
28391          * @param {Roo.bootstrap.DocumentManager} this
28392          * @param {Object} formData
28393          */
28394         "prepare" : true,
28395         /**
28396          * @event remove
28397          * Fire when remove the file
28398          * @param {Roo.bootstrap.DocumentManager} this
28399          * @param {Object} file
28400          */
28401         "remove" : true,
28402         /**
28403          * @event refresh
28404          * Fire after refresh the file
28405          * @param {Roo.bootstrap.DocumentManager} this
28406          */
28407         "refresh" : true,
28408         /**
28409          * @event click
28410          * Fire after click the image
28411          * @param {Roo.bootstrap.DocumentManager} this
28412          * @param {Object} file
28413          */
28414         "click" : true,
28415         /**
28416          * @event edit
28417          * Fire when upload a image and editable set to true
28418          * @param {Roo.bootstrap.DocumentManager} this
28419          * @param {Object} file
28420          */
28421         "edit" : true,
28422         /**
28423          * @event beforeselectfile
28424          * Fire before select file
28425          * @param {Roo.bootstrap.DocumentManager} this
28426          */
28427         "beforeselectfile" : true,
28428         /**
28429          * @event process
28430          * Fire before process file
28431          * @param {Roo.bootstrap.DocumentManager} this
28432          * @param {Object} file
28433          */
28434         "process" : true
28435         
28436     });
28437 };
28438
28439 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28440     
28441     boxes : 0,
28442     inputName : '',
28443     thumbSize : 300,
28444     multiple : true,
28445     files : false,
28446     method : 'POST',
28447     url : '',
28448     paramName : 'imageUpload',
28449     toolTipName : 'filename',
28450     fieldLabel : '',
28451     labelWidth : 4,
28452     labelAlign : 'left',
28453     editable : true,
28454     delegates : false,
28455     xhr : false, 
28456     
28457     labellg : 0,
28458     labelmd : 0,
28459     labelsm : 0,
28460     labelxs : 0,
28461     
28462     getAutoCreate : function()
28463     {   
28464         var managerWidget = {
28465             tag : 'div',
28466             cls : 'roo-document-manager',
28467             cn : [
28468                 {
28469                     tag : 'input',
28470                     cls : 'roo-document-manager-selector',
28471                     type : 'file'
28472                 },
28473                 {
28474                     tag : 'div',
28475                     cls : 'roo-document-manager-uploader',
28476                     cn : [
28477                         {
28478                             tag : 'div',
28479                             cls : 'roo-document-manager-upload-btn',
28480                             html : '<i class="fa fa-plus"></i>'
28481                         }
28482                     ]
28483                     
28484                 }
28485             ]
28486         };
28487         
28488         var content = [
28489             {
28490                 tag : 'div',
28491                 cls : 'column col-md-12',
28492                 cn : managerWidget
28493             }
28494         ];
28495         
28496         if(this.fieldLabel.length){
28497             
28498             content = [
28499                 {
28500                     tag : 'div',
28501                     cls : 'column col-md-12',
28502                     html : this.fieldLabel
28503                 },
28504                 {
28505                     tag : 'div',
28506                     cls : 'column col-md-12',
28507                     cn : managerWidget
28508                 }
28509             ];
28510
28511             if(this.labelAlign == 'left'){
28512                 content = [
28513                     {
28514                         tag : 'div',
28515                         cls : 'column',
28516                         html : this.fieldLabel
28517                     },
28518                     {
28519                         tag : 'div',
28520                         cls : 'column',
28521                         cn : managerWidget
28522                     }
28523                 ];
28524                 
28525                 if(this.labelWidth > 12){
28526                     content[0].style = "width: " + this.labelWidth + 'px';
28527                 }
28528
28529                 if(this.labelWidth < 13 && this.labelmd == 0){
28530                     this.labelmd = this.labelWidth;
28531                 }
28532
28533                 if(this.labellg > 0){
28534                     content[0].cls += ' col-lg-' + this.labellg;
28535                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28536                 }
28537
28538                 if(this.labelmd > 0){
28539                     content[0].cls += ' col-md-' + this.labelmd;
28540                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28541                 }
28542
28543                 if(this.labelsm > 0){
28544                     content[0].cls += ' col-sm-' + this.labelsm;
28545                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28546                 }
28547
28548                 if(this.labelxs > 0){
28549                     content[0].cls += ' col-xs-' + this.labelxs;
28550                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28551                 }
28552                 
28553             }
28554         }
28555         
28556         var cfg = {
28557             tag : 'div',
28558             cls : 'row clearfix',
28559             cn : content
28560         };
28561         
28562         return cfg;
28563         
28564     },
28565     
28566     initEvents : function()
28567     {
28568         this.managerEl = this.el.select('.roo-document-manager', true).first();
28569         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28570         
28571         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28572         this.selectorEl.hide();
28573         
28574         if(this.multiple){
28575             this.selectorEl.attr('multiple', 'multiple');
28576         }
28577         
28578         this.selectorEl.on('change', this.onFileSelected, this);
28579         
28580         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28581         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28582         
28583         this.uploader.on('click', this.onUploaderClick, this);
28584         
28585         this.renderProgressDialog();
28586         
28587         var _this = this;
28588         
28589         window.addEventListener("resize", function() { _this.refresh(); } );
28590         
28591         this.fireEvent('initial', this);
28592     },
28593     
28594     renderProgressDialog : function()
28595     {
28596         var _this = this;
28597         
28598         this.progressDialog = new Roo.bootstrap.Modal({
28599             cls : 'roo-document-manager-progress-dialog',
28600             allow_close : false,
28601             title : '',
28602             buttons : [
28603                 {
28604                     name  :'cancel',
28605                     weight : 'danger',
28606                     html : 'Cancel'
28607                 }
28608             ], 
28609             listeners : { 
28610                 btnclick : function() {
28611                     _this.uploadCancel();
28612                     this.hide();
28613                 }
28614             }
28615         });
28616          
28617         this.progressDialog.render(Roo.get(document.body));
28618          
28619         this.progress = new Roo.bootstrap.Progress({
28620             cls : 'roo-document-manager-progress',
28621             active : true,
28622             striped : true
28623         });
28624         
28625         this.progress.render(this.progressDialog.getChildContainer());
28626         
28627         this.progressBar = new Roo.bootstrap.ProgressBar({
28628             cls : 'roo-document-manager-progress-bar',
28629             aria_valuenow : 0,
28630             aria_valuemin : 0,
28631             aria_valuemax : 12,
28632             panel : 'success'
28633         });
28634         
28635         this.progressBar.render(this.progress.getChildContainer());
28636     },
28637     
28638     onUploaderClick : function(e)
28639     {
28640         e.preventDefault();
28641      
28642         if(this.fireEvent('beforeselectfile', this) != false){
28643             this.selectorEl.dom.click();
28644         }
28645         
28646     },
28647     
28648     onFileSelected : function(e)
28649     {
28650         e.preventDefault();
28651         
28652         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28653             return;
28654         }
28655         
28656         Roo.each(this.selectorEl.dom.files, function(file){
28657             if(this.fireEvent('inspect', this, file) != false){
28658                 this.files.push(file);
28659             }
28660         }, this);
28661         
28662         this.queue();
28663         
28664     },
28665     
28666     queue : function()
28667     {
28668         this.selectorEl.dom.value = '';
28669         
28670         if(!this.files || !this.files.length){
28671             return;
28672         }
28673         
28674         if(this.boxes > 0 && this.files.length > this.boxes){
28675             this.files = this.files.slice(0, this.boxes);
28676         }
28677         
28678         this.uploader.show();
28679         
28680         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28681             this.uploader.hide();
28682         }
28683         
28684         var _this = this;
28685         
28686         var files = [];
28687         
28688         var docs = [];
28689         
28690         Roo.each(this.files, function(file){
28691             
28692             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28693                 var f = this.renderPreview(file);
28694                 files.push(f);
28695                 return;
28696             }
28697             
28698             if(file.type.indexOf('image') != -1){
28699                 this.delegates.push(
28700                     (function(){
28701                         _this.process(file);
28702                     }).createDelegate(this)
28703                 );
28704         
28705                 return;
28706             }
28707             
28708             docs.push(
28709                 (function(){
28710                     _this.process(file);
28711                 }).createDelegate(this)
28712             );
28713             
28714         }, this);
28715         
28716         this.files = files;
28717         
28718         this.delegates = this.delegates.concat(docs);
28719         
28720         if(!this.delegates.length){
28721             this.refresh();
28722             return;
28723         }
28724         
28725         this.progressBar.aria_valuemax = this.delegates.length;
28726         
28727         this.arrange();
28728         
28729         return;
28730     },
28731     
28732     arrange : function()
28733     {
28734         if(!this.delegates.length){
28735             this.progressDialog.hide();
28736             this.refresh();
28737             return;
28738         }
28739         
28740         var delegate = this.delegates.shift();
28741         
28742         this.progressDialog.show();
28743         
28744         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28745         
28746         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28747         
28748         delegate();
28749     },
28750     
28751     refresh : function()
28752     {
28753         this.uploader.show();
28754         
28755         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28756             this.uploader.hide();
28757         }
28758         
28759         Roo.isTouch ? this.closable(false) : this.closable(true);
28760         
28761         this.fireEvent('refresh', this);
28762     },
28763     
28764     onRemove : function(e, el, o)
28765     {
28766         e.preventDefault();
28767         
28768         this.fireEvent('remove', this, o);
28769         
28770     },
28771     
28772     remove : function(o)
28773     {
28774         var files = [];
28775         
28776         Roo.each(this.files, function(file){
28777             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28778                 files.push(file);
28779                 return;
28780             }
28781
28782             o.target.remove();
28783
28784         }, this);
28785         
28786         this.files = files;
28787         
28788         this.refresh();
28789     },
28790     
28791     clear : function()
28792     {
28793         Roo.each(this.files, function(file){
28794             if(!file.target){
28795                 return;
28796             }
28797             
28798             file.target.remove();
28799
28800         }, this);
28801         
28802         this.files = [];
28803         
28804         this.refresh();
28805     },
28806     
28807     onClick : function(e, el, o)
28808     {
28809         e.preventDefault();
28810         
28811         this.fireEvent('click', this, o);
28812         
28813     },
28814     
28815     closable : function(closable)
28816     {
28817         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28818             
28819             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28820             
28821             if(closable){
28822                 el.show();
28823                 return;
28824             }
28825             
28826             el.hide();
28827             
28828         }, this);
28829     },
28830     
28831     xhrOnLoad : function(xhr)
28832     {
28833         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28834             el.remove();
28835         }, this);
28836         
28837         if (xhr.readyState !== 4) {
28838             this.arrange();
28839             this.fireEvent('exception', this, xhr);
28840             return;
28841         }
28842
28843         var response = Roo.decode(xhr.responseText);
28844         
28845         if(!response.success){
28846             this.arrange();
28847             this.fireEvent('exception', this, xhr);
28848             return;
28849         }
28850         
28851         var file = this.renderPreview(response.data);
28852         
28853         this.files.push(file);
28854         
28855         this.arrange();
28856         
28857         this.fireEvent('afterupload', this, xhr);
28858         
28859     },
28860     
28861     xhrOnError : function(xhr)
28862     {
28863         Roo.log('xhr on error');
28864         
28865         var response = Roo.decode(xhr.responseText);
28866           
28867         Roo.log(response);
28868         
28869         this.arrange();
28870     },
28871     
28872     process : function(file)
28873     {
28874         if(this.fireEvent('process', this, file) !== false){
28875             if(this.editable && file.type.indexOf('image') != -1){
28876                 this.fireEvent('edit', this, file);
28877                 return;
28878             }
28879
28880             this.uploadStart(file, false);
28881
28882             return;
28883         }
28884         
28885     },
28886     
28887     uploadStart : function(file, crop)
28888     {
28889         this.xhr = new XMLHttpRequest();
28890         
28891         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28892             this.arrange();
28893             return;
28894         }
28895         
28896         file.xhr = this.xhr;
28897             
28898         this.managerEl.createChild({
28899             tag : 'div',
28900             cls : 'roo-document-manager-loading',
28901             cn : [
28902                 {
28903                     tag : 'div',
28904                     tooltip : file.name,
28905                     cls : 'roo-document-manager-thumb',
28906                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28907                 }
28908             ]
28909
28910         });
28911
28912         this.xhr.open(this.method, this.url, true);
28913         
28914         var headers = {
28915             "Accept": "application/json",
28916             "Cache-Control": "no-cache",
28917             "X-Requested-With": "XMLHttpRequest"
28918         };
28919         
28920         for (var headerName in headers) {
28921             var headerValue = headers[headerName];
28922             if (headerValue) {
28923                 this.xhr.setRequestHeader(headerName, headerValue);
28924             }
28925         }
28926         
28927         var _this = this;
28928         
28929         this.xhr.onload = function()
28930         {
28931             _this.xhrOnLoad(_this.xhr);
28932         }
28933         
28934         this.xhr.onerror = function()
28935         {
28936             _this.xhrOnError(_this.xhr);
28937         }
28938         
28939         var formData = new FormData();
28940
28941         formData.append('returnHTML', 'NO');
28942         
28943         if(crop){
28944             formData.append('crop', crop);
28945         }
28946         
28947         formData.append(this.paramName, file, file.name);
28948         
28949         var options = {
28950             file : file, 
28951             manually : false
28952         };
28953         
28954         if(this.fireEvent('prepare', this, formData, options) != false){
28955             
28956             if(options.manually){
28957                 return;
28958             }
28959             
28960             this.xhr.send(formData);
28961             return;
28962         };
28963         
28964         this.uploadCancel();
28965     },
28966     
28967     uploadCancel : function()
28968     {
28969         if (this.xhr) {
28970             this.xhr.abort();
28971         }
28972         
28973         this.delegates = [];
28974         
28975         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28976             el.remove();
28977         }, this);
28978         
28979         this.arrange();
28980     },
28981     
28982     renderPreview : function(file)
28983     {
28984         if(typeof(file.target) != 'undefined' && file.target){
28985             return file;
28986         }
28987         
28988         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28989         
28990         var previewEl = this.managerEl.createChild({
28991             tag : 'div',
28992             cls : 'roo-document-manager-preview',
28993             cn : [
28994                 {
28995                     tag : 'div',
28996                     tooltip : file[this.toolTipName],
28997                     cls : 'roo-document-manager-thumb',
28998                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28999                 },
29000                 {
29001                     tag : 'button',
29002                     cls : 'close',
29003                     html : '<i class="fa fa-times-circle"></i>'
29004                 }
29005             ]
29006         });
29007
29008         var close = previewEl.select('button.close', true).first();
29009
29010         close.on('click', this.onRemove, this, file);
29011
29012         file.target = previewEl;
29013
29014         var image = previewEl.select('img', true).first();
29015         
29016         var _this = this;
29017         
29018         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29019         
29020         image.on('click', this.onClick, this, file);
29021         
29022         return file;
29023         
29024     },
29025     
29026     onPreviewLoad : function(file, image)
29027     {
29028         if(typeof(file.target) == 'undefined' || !file.target){
29029             return;
29030         }
29031         
29032         var width = image.dom.naturalWidth || image.dom.width;
29033         var height = image.dom.naturalHeight || image.dom.height;
29034         
29035         if(width > height){
29036             file.target.addClass('wide');
29037             return;
29038         }
29039         
29040         file.target.addClass('tall');
29041         return;
29042         
29043     },
29044     
29045     uploadFromSource : function(file, crop)
29046     {
29047         this.xhr = new XMLHttpRequest();
29048         
29049         this.managerEl.createChild({
29050             tag : 'div',
29051             cls : 'roo-document-manager-loading',
29052             cn : [
29053                 {
29054                     tag : 'div',
29055                     tooltip : file.name,
29056                     cls : 'roo-document-manager-thumb',
29057                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29058                 }
29059             ]
29060
29061         });
29062
29063         this.xhr.open(this.method, this.url, true);
29064         
29065         var headers = {
29066             "Accept": "application/json",
29067             "Cache-Control": "no-cache",
29068             "X-Requested-With": "XMLHttpRequest"
29069         };
29070         
29071         for (var headerName in headers) {
29072             var headerValue = headers[headerName];
29073             if (headerValue) {
29074                 this.xhr.setRequestHeader(headerName, headerValue);
29075             }
29076         }
29077         
29078         var _this = this;
29079         
29080         this.xhr.onload = function()
29081         {
29082             _this.xhrOnLoad(_this.xhr);
29083         }
29084         
29085         this.xhr.onerror = function()
29086         {
29087             _this.xhrOnError(_this.xhr);
29088         }
29089         
29090         var formData = new FormData();
29091
29092         formData.append('returnHTML', 'NO');
29093         
29094         formData.append('crop', crop);
29095         
29096         if(typeof(file.filename) != 'undefined'){
29097             formData.append('filename', file.filename);
29098         }
29099         
29100         if(typeof(file.mimetype) != 'undefined'){
29101             formData.append('mimetype', file.mimetype);
29102         }
29103         
29104         Roo.log(formData);
29105         
29106         if(this.fireEvent('prepare', this, formData) != false){
29107             this.xhr.send(formData);
29108         };
29109     }
29110 });
29111
29112 /*
29113 * Licence: LGPL
29114 */
29115
29116 /**
29117  * @class Roo.bootstrap.DocumentViewer
29118  * @extends Roo.bootstrap.Component
29119  * Bootstrap DocumentViewer class
29120  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29121  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29122  * 
29123  * @constructor
29124  * Create a new DocumentViewer
29125  * @param {Object} config The config object
29126  */
29127
29128 Roo.bootstrap.DocumentViewer = function(config){
29129     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29130     
29131     this.addEvents({
29132         /**
29133          * @event initial
29134          * Fire after initEvent
29135          * @param {Roo.bootstrap.DocumentViewer} this
29136          */
29137         "initial" : true,
29138         /**
29139          * @event click
29140          * Fire after click
29141          * @param {Roo.bootstrap.DocumentViewer} this
29142          */
29143         "click" : true,
29144         /**
29145          * @event download
29146          * Fire after download button
29147          * @param {Roo.bootstrap.DocumentViewer} this
29148          */
29149         "download" : true,
29150         /**
29151          * @event trash
29152          * Fire after trash button
29153          * @param {Roo.bootstrap.DocumentViewer} this
29154          */
29155         "trash" : true
29156         
29157     });
29158 };
29159
29160 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29161     
29162     showDownload : true,
29163     
29164     showTrash : true,
29165     
29166     getAutoCreate : function()
29167     {
29168         var cfg = {
29169             tag : 'div',
29170             cls : 'roo-document-viewer',
29171             cn : [
29172                 {
29173                     tag : 'div',
29174                     cls : 'roo-document-viewer-body',
29175                     cn : [
29176                         {
29177                             tag : 'div',
29178                             cls : 'roo-document-viewer-thumb',
29179                             cn : [
29180                                 {
29181                                     tag : 'img',
29182                                     cls : 'roo-document-viewer-image'
29183                                 }
29184                             ]
29185                         }
29186                     ]
29187                 },
29188                 {
29189                     tag : 'div',
29190                     cls : 'roo-document-viewer-footer',
29191                     cn : {
29192                         tag : 'div',
29193                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29194                         cn : [
29195                             {
29196                                 tag : 'div',
29197                                 cls : 'btn-group roo-document-viewer-download',
29198                                 cn : [
29199                                     {
29200                                         tag : 'button',
29201                                         cls : 'btn btn-default',
29202                                         html : '<i class="fa fa-download"></i>'
29203                                     }
29204                                 ]
29205                             },
29206                             {
29207                                 tag : 'div',
29208                                 cls : 'btn-group roo-document-viewer-trash',
29209                                 cn : [
29210                                     {
29211                                         tag : 'button',
29212                                         cls : 'btn btn-default',
29213                                         html : '<i class="fa fa-trash"></i>'
29214                                     }
29215                                 ]
29216                             }
29217                         ]
29218                     }
29219                 }
29220             ]
29221         };
29222         
29223         return cfg;
29224     },
29225     
29226     initEvents : function()
29227     {
29228         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29229         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29230         
29231         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29232         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29233         
29234         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29235         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29236         
29237         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29238         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29239         
29240         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29241         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29242         
29243         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29244         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29245         
29246         this.bodyEl.on('click', this.onClick, this);
29247         this.downloadBtn.on('click', this.onDownload, this);
29248         this.trashBtn.on('click', this.onTrash, this);
29249         
29250         this.downloadBtn.hide();
29251         this.trashBtn.hide();
29252         
29253         if(this.showDownload){
29254             this.downloadBtn.show();
29255         }
29256         
29257         if(this.showTrash){
29258             this.trashBtn.show();
29259         }
29260         
29261         if(!this.showDownload && !this.showTrash) {
29262             this.footerEl.hide();
29263         }
29264         
29265     },
29266     
29267     initial : function()
29268     {
29269         this.fireEvent('initial', this);
29270         
29271     },
29272     
29273     onClick : function(e)
29274     {
29275         e.preventDefault();
29276         
29277         this.fireEvent('click', this);
29278     },
29279     
29280     onDownload : function(e)
29281     {
29282         e.preventDefault();
29283         
29284         this.fireEvent('download', this);
29285     },
29286     
29287     onTrash : function(e)
29288     {
29289         e.preventDefault();
29290         
29291         this.fireEvent('trash', this);
29292     }
29293     
29294 });
29295 /*
29296  * - LGPL
29297  *
29298  * nav progress bar
29299  * 
29300  */
29301
29302 /**
29303  * @class Roo.bootstrap.NavProgressBar
29304  * @extends Roo.bootstrap.Component
29305  * Bootstrap NavProgressBar class
29306  * 
29307  * @constructor
29308  * Create a new nav progress bar
29309  * @param {Object} config The config object
29310  */
29311
29312 Roo.bootstrap.NavProgressBar = function(config){
29313     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29314
29315     this.bullets = this.bullets || [];
29316    
29317 //    Roo.bootstrap.NavProgressBar.register(this);
29318      this.addEvents({
29319         /**
29320              * @event changed
29321              * Fires when the active item changes
29322              * @param {Roo.bootstrap.NavProgressBar} this
29323              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29324              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29325          */
29326         'changed': true
29327      });
29328     
29329 };
29330
29331 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29332     
29333     bullets : [],
29334     barItems : [],
29335     
29336     getAutoCreate : function()
29337     {
29338         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29339         
29340         cfg = {
29341             tag : 'div',
29342             cls : 'roo-navigation-bar-group',
29343             cn : [
29344                 {
29345                     tag : 'div',
29346                     cls : 'roo-navigation-top-bar'
29347                 },
29348                 {
29349                     tag : 'div',
29350                     cls : 'roo-navigation-bullets-bar',
29351                     cn : [
29352                         {
29353                             tag : 'ul',
29354                             cls : 'roo-navigation-bar'
29355                         }
29356                     ]
29357                 },
29358                 
29359                 {
29360                     tag : 'div',
29361                     cls : 'roo-navigation-bottom-bar'
29362                 }
29363             ]
29364             
29365         };
29366         
29367         return cfg;
29368         
29369     },
29370     
29371     initEvents: function() 
29372     {
29373         
29374     },
29375     
29376     onRender : function(ct, position) 
29377     {
29378         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29379         
29380         if(this.bullets.length){
29381             Roo.each(this.bullets, function(b){
29382                this.addItem(b);
29383             }, this);
29384         }
29385         
29386         this.format();
29387         
29388     },
29389     
29390     addItem : function(cfg)
29391     {
29392         var item = new Roo.bootstrap.NavProgressItem(cfg);
29393         
29394         item.parentId = this.id;
29395         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29396         
29397         if(cfg.html){
29398             var top = new Roo.bootstrap.Element({
29399                 tag : 'div',
29400                 cls : 'roo-navigation-bar-text'
29401             });
29402             
29403             var bottom = new Roo.bootstrap.Element({
29404                 tag : 'div',
29405                 cls : 'roo-navigation-bar-text'
29406             });
29407             
29408             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29409             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29410             
29411             var topText = new Roo.bootstrap.Element({
29412                 tag : 'span',
29413                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29414             });
29415             
29416             var bottomText = new Roo.bootstrap.Element({
29417                 tag : 'span',
29418                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29419             });
29420             
29421             topText.onRender(top.el, null);
29422             bottomText.onRender(bottom.el, null);
29423             
29424             item.topEl = top;
29425             item.bottomEl = bottom;
29426         }
29427         
29428         this.barItems.push(item);
29429         
29430         return item;
29431     },
29432     
29433     getActive : function()
29434     {
29435         var active = false;
29436         
29437         Roo.each(this.barItems, function(v){
29438             
29439             if (!v.isActive()) {
29440                 return;
29441             }
29442             
29443             active = v;
29444             return false;
29445             
29446         });
29447         
29448         return active;
29449     },
29450     
29451     setActiveItem : function(item)
29452     {
29453         var prev = false;
29454         
29455         Roo.each(this.barItems, function(v){
29456             if (v.rid == item.rid) {
29457                 return ;
29458             }
29459             
29460             if (v.isActive()) {
29461                 v.setActive(false);
29462                 prev = v;
29463             }
29464         });
29465
29466         item.setActive(true);
29467         
29468         this.fireEvent('changed', this, item, prev);
29469     },
29470     
29471     getBarItem: function(rid)
29472     {
29473         var ret = false;
29474         
29475         Roo.each(this.barItems, function(e) {
29476             if (e.rid != rid) {
29477                 return;
29478             }
29479             
29480             ret =  e;
29481             return false;
29482         });
29483         
29484         return ret;
29485     },
29486     
29487     indexOfItem : function(item)
29488     {
29489         var index = false;
29490         
29491         Roo.each(this.barItems, function(v, i){
29492             
29493             if (v.rid != item.rid) {
29494                 return;
29495             }
29496             
29497             index = i;
29498             return false
29499         });
29500         
29501         return index;
29502     },
29503     
29504     setActiveNext : function()
29505     {
29506         var i = this.indexOfItem(this.getActive());
29507         
29508         if (i > this.barItems.length) {
29509             return;
29510         }
29511         
29512         this.setActiveItem(this.barItems[i+1]);
29513     },
29514     
29515     setActivePrev : function()
29516     {
29517         var i = this.indexOfItem(this.getActive());
29518         
29519         if (i  < 1) {
29520             return;
29521         }
29522         
29523         this.setActiveItem(this.barItems[i-1]);
29524     },
29525     
29526     format : function()
29527     {
29528         if(!this.barItems.length){
29529             return;
29530         }
29531      
29532         var width = 100 / this.barItems.length;
29533         
29534         Roo.each(this.barItems, function(i){
29535             i.el.setStyle('width', width + '%');
29536             i.topEl.el.setStyle('width', width + '%');
29537             i.bottomEl.el.setStyle('width', width + '%');
29538         }, this);
29539         
29540     }
29541     
29542 });
29543 /*
29544  * - LGPL
29545  *
29546  * Nav Progress Item
29547  * 
29548  */
29549
29550 /**
29551  * @class Roo.bootstrap.NavProgressItem
29552  * @extends Roo.bootstrap.Component
29553  * Bootstrap NavProgressItem class
29554  * @cfg {String} rid the reference id
29555  * @cfg {Boolean} active (true|false) Is item active default false
29556  * @cfg {Boolean} disabled (true|false) Is item active default false
29557  * @cfg {String} html
29558  * @cfg {String} position (top|bottom) text position default bottom
29559  * @cfg {String} icon show icon instead of number
29560  * 
29561  * @constructor
29562  * Create a new NavProgressItem
29563  * @param {Object} config The config object
29564  */
29565 Roo.bootstrap.NavProgressItem = function(config){
29566     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29567     this.addEvents({
29568         // raw events
29569         /**
29570          * @event click
29571          * The raw click event for the entire grid.
29572          * @param {Roo.bootstrap.NavProgressItem} this
29573          * @param {Roo.EventObject} e
29574          */
29575         "click" : true
29576     });
29577    
29578 };
29579
29580 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29581     
29582     rid : '',
29583     active : false,
29584     disabled : false,
29585     html : '',
29586     position : 'bottom',
29587     icon : false,
29588     
29589     getAutoCreate : function()
29590     {
29591         var iconCls = 'roo-navigation-bar-item-icon';
29592         
29593         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29594         
29595         var cfg = {
29596             tag: 'li',
29597             cls: 'roo-navigation-bar-item',
29598             cn : [
29599                 {
29600                     tag : 'i',
29601                     cls : iconCls
29602                 }
29603             ]
29604         };
29605         
29606         if(this.active){
29607             cfg.cls += ' active';
29608         }
29609         if(this.disabled){
29610             cfg.cls += ' disabled';
29611         }
29612         
29613         return cfg;
29614     },
29615     
29616     disable : function()
29617     {
29618         this.setDisabled(true);
29619     },
29620     
29621     enable : function()
29622     {
29623         this.setDisabled(false);
29624     },
29625     
29626     initEvents: function() 
29627     {
29628         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29629         
29630         this.iconEl.on('click', this.onClick, this);
29631     },
29632     
29633     onClick : function(e)
29634     {
29635         e.preventDefault();
29636         
29637         if(this.disabled){
29638             return;
29639         }
29640         
29641         if(this.fireEvent('click', this, e) === false){
29642             return;
29643         };
29644         
29645         this.parent().setActiveItem(this);
29646     },
29647     
29648     isActive: function () 
29649     {
29650         return this.active;
29651     },
29652     
29653     setActive : function(state)
29654     {
29655         if(this.active == state){
29656             return;
29657         }
29658         
29659         this.active = state;
29660         
29661         if (state) {
29662             this.el.addClass('active');
29663             return;
29664         }
29665         
29666         this.el.removeClass('active');
29667         
29668         return;
29669     },
29670     
29671     setDisabled : function(state)
29672     {
29673         if(this.disabled == state){
29674             return;
29675         }
29676         
29677         this.disabled = state;
29678         
29679         if (state) {
29680             this.el.addClass('disabled');
29681             return;
29682         }
29683         
29684         this.el.removeClass('disabled');
29685     },
29686     
29687     tooltipEl : function()
29688     {
29689         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29690     }
29691 });
29692  
29693
29694  /*
29695  * - LGPL
29696  *
29697  * FieldLabel
29698  * 
29699  */
29700
29701 /**
29702  * @class Roo.bootstrap.FieldLabel
29703  * @extends Roo.bootstrap.Component
29704  * Bootstrap FieldLabel class
29705  * @cfg {String} html contents of the element
29706  * @cfg {String} tag tag of the element default label
29707  * @cfg {String} cls class of the element
29708  * @cfg {String} target label target 
29709  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29710  * @cfg {String} invalidClass default "text-warning"
29711  * @cfg {String} validClass default "text-success"
29712  * @cfg {String} iconTooltip default "This field is required"
29713  * @cfg {String} indicatorpos (left|right) default left
29714  * 
29715  * @constructor
29716  * Create a new FieldLabel
29717  * @param {Object} config The config object
29718  */
29719
29720 Roo.bootstrap.FieldLabel = function(config){
29721     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29722     
29723     this.addEvents({
29724             /**
29725              * @event invalid
29726              * Fires after the field has been marked as invalid.
29727              * @param {Roo.form.FieldLabel} this
29728              * @param {String} msg The validation message
29729              */
29730             invalid : true,
29731             /**
29732              * @event valid
29733              * Fires after the field has been validated with no errors.
29734              * @param {Roo.form.FieldLabel} this
29735              */
29736             valid : true
29737         });
29738 };
29739
29740 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29741     
29742     tag: 'label',
29743     cls: '',
29744     html: '',
29745     target: '',
29746     allowBlank : true,
29747     invalidClass : 'has-warning',
29748     validClass : 'has-success',
29749     iconTooltip : 'This field is required',
29750     indicatorpos : 'left',
29751     
29752     getAutoCreate : function(){
29753         
29754         var cfg = {
29755             tag : this.tag,
29756             cls : 'roo-bootstrap-field-label ' + this.cls,
29757             for : this.target,
29758             cn : [
29759                 {
29760                     tag : 'i',
29761                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29762                     tooltip : this.iconTooltip
29763                 },
29764                 {
29765                     tag : 'span',
29766                     html : this.html
29767                 }
29768             ] 
29769         };
29770         
29771         if(this.indicatorpos == 'right'){
29772             var cfg = {
29773                 tag : this.tag,
29774                 cls : 'roo-bootstrap-field-label ' + this.cls,
29775                 for : this.target,
29776                 cn : [
29777                     {
29778                         tag : 'span',
29779                         html : this.html
29780                     },
29781                     {
29782                         tag : 'i',
29783                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29784                         tooltip : this.iconTooltip
29785                     }
29786                 ] 
29787             };
29788         }
29789         
29790         return cfg;
29791     },
29792     
29793     initEvents: function() 
29794     {
29795         Roo.bootstrap.Element.superclass.initEvents.call(this);
29796         
29797         this.indicator = this.indicatorEl();
29798         
29799         if(this.indicator){
29800             this.indicator.removeClass('visible');
29801             this.indicator.addClass('invisible');
29802         }
29803         
29804         Roo.bootstrap.FieldLabel.register(this);
29805     },
29806     
29807     indicatorEl : function()
29808     {
29809         var indicator = this.el.select('i.roo-required-indicator',true).first();
29810         
29811         if(!indicator){
29812             return false;
29813         }
29814         
29815         return indicator;
29816         
29817     },
29818     
29819     /**
29820      * Mark this field as valid
29821      */
29822     markValid : function()
29823     {
29824         if(this.indicator){
29825             this.indicator.removeClass('visible');
29826             this.indicator.addClass('invisible');
29827         }
29828         
29829         this.el.removeClass(this.invalidClass);
29830         
29831         this.el.addClass(this.validClass);
29832         
29833         this.fireEvent('valid', this);
29834     },
29835     
29836     /**
29837      * Mark this field as invalid
29838      * @param {String} msg The validation message
29839      */
29840     markInvalid : function(msg)
29841     {
29842         if(this.indicator){
29843             this.indicator.removeClass('invisible');
29844             this.indicator.addClass('visible');
29845         }
29846         
29847         this.el.removeClass(this.validClass);
29848         
29849         this.el.addClass(this.invalidClass);
29850         
29851         this.fireEvent('invalid', this, msg);
29852     }
29853     
29854    
29855 });
29856
29857 Roo.apply(Roo.bootstrap.FieldLabel, {
29858     
29859     groups: {},
29860     
29861      /**
29862     * register a FieldLabel Group
29863     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29864     */
29865     register : function(label)
29866     {
29867         if(this.groups.hasOwnProperty(label.target)){
29868             return;
29869         }
29870      
29871         this.groups[label.target] = label;
29872         
29873     },
29874     /**
29875     * fetch a FieldLabel Group based on the target
29876     * @param {string} target
29877     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29878     */
29879     get: function(target) {
29880         if (typeof(this.groups[target]) == 'undefined') {
29881             return false;
29882         }
29883         
29884         return this.groups[target] ;
29885     }
29886 });
29887
29888  
29889
29890  /*
29891  * - LGPL
29892  *
29893  * page DateSplitField.
29894  * 
29895  */
29896
29897
29898 /**
29899  * @class Roo.bootstrap.DateSplitField
29900  * @extends Roo.bootstrap.Component
29901  * Bootstrap DateSplitField class
29902  * @cfg {string} fieldLabel - the label associated
29903  * @cfg {Number} labelWidth set the width of label (0-12)
29904  * @cfg {String} labelAlign (top|left)
29905  * @cfg {Boolean} dayAllowBlank (true|false) default false
29906  * @cfg {Boolean} monthAllowBlank (true|false) default false
29907  * @cfg {Boolean} yearAllowBlank (true|false) default false
29908  * @cfg {string} dayPlaceholder 
29909  * @cfg {string} monthPlaceholder
29910  * @cfg {string} yearPlaceholder
29911  * @cfg {string} dayFormat default 'd'
29912  * @cfg {string} monthFormat default 'm'
29913  * @cfg {string} yearFormat default 'Y'
29914  * @cfg {Number} labellg set the width of label (1-12)
29915  * @cfg {Number} labelmd set the width of label (1-12)
29916  * @cfg {Number} labelsm set the width of label (1-12)
29917  * @cfg {Number} labelxs set the width of label (1-12)
29918
29919  *     
29920  * @constructor
29921  * Create a new DateSplitField
29922  * @param {Object} config The config object
29923  */
29924
29925 Roo.bootstrap.DateSplitField = function(config){
29926     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29927     
29928     this.addEvents({
29929         // raw events
29930          /**
29931          * @event years
29932          * getting the data of years
29933          * @param {Roo.bootstrap.DateSplitField} this
29934          * @param {Object} years
29935          */
29936         "years" : true,
29937         /**
29938          * @event days
29939          * getting the data of days
29940          * @param {Roo.bootstrap.DateSplitField} this
29941          * @param {Object} days
29942          */
29943         "days" : true,
29944         /**
29945          * @event invalid
29946          * Fires after the field has been marked as invalid.
29947          * @param {Roo.form.Field} this
29948          * @param {String} msg The validation message
29949          */
29950         invalid : true,
29951        /**
29952          * @event valid
29953          * Fires after the field has been validated with no errors.
29954          * @param {Roo.form.Field} this
29955          */
29956         valid : true
29957     });
29958 };
29959
29960 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29961     
29962     fieldLabel : '',
29963     labelAlign : 'top',
29964     labelWidth : 3,
29965     dayAllowBlank : false,
29966     monthAllowBlank : false,
29967     yearAllowBlank : false,
29968     dayPlaceholder : '',
29969     monthPlaceholder : '',
29970     yearPlaceholder : '',
29971     dayFormat : 'd',
29972     monthFormat : 'm',
29973     yearFormat : 'Y',
29974     isFormField : true,
29975     labellg : 0,
29976     labelmd : 0,
29977     labelsm : 0,
29978     labelxs : 0,
29979     
29980     getAutoCreate : function()
29981     {
29982         var cfg = {
29983             tag : 'div',
29984             cls : 'row roo-date-split-field-group',
29985             cn : [
29986                 {
29987                     tag : 'input',
29988                     type : 'hidden',
29989                     cls : 'form-hidden-field roo-date-split-field-group-value',
29990                     name : this.name
29991                 }
29992             ]
29993         };
29994         
29995         var labelCls = 'col-md-12';
29996         var contentCls = 'col-md-4';
29997         
29998         if(this.fieldLabel){
29999             
30000             var label = {
30001                 tag : 'div',
30002                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30003                 cn : [
30004                     {
30005                         tag : 'label',
30006                         html : this.fieldLabel
30007                     }
30008                 ]
30009             };
30010             
30011             if(this.labelAlign == 'left'){
30012             
30013                 if(this.labelWidth > 12){
30014                     label.style = "width: " + this.labelWidth + 'px';
30015                 }
30016
30017                 if(this.labelWidth < 13 && this.labelmd == 0){
30018                     this.labelmd = this.labelWidth;
30019                 }
30020
30021                 if(this.labellg > 0){
30022                     labelCls = ' col-lg-' + this.labellg;
30023                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30024                 }
30025
30026                 if(this.labelmd > 0){
30027                     labelCls = ' col-md-' + this.labelmd;
30028                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30029                 }
30030
30031                 if(this.labelsm > 0){
30032                     labelCls = ' col-sm-' + this.labelsm;
30033                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30034                 }
30035
30036                 if(this.labelxs > 0){
30037                     labelCls = ' col-xs-' + this.labelxs;
30038                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30039                 }
30040             }
30041             
30042             label.cls += ' ' + labelCls;
30043             
30044             cfg.cn.push(label);
30045         }
30046         
30047         Roo.each(['day', 'month', 'year'], function(t){
30048             cfg.cn.push({
30049                 tag : 'div',
30050                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30051             });
30052         }, this);
30053         
30054         return cfg;
30055     },
30056     
30057     inputEl: function ()
30058     {
30059         return this.el.select('.roo-date-split-field-group-value', true).first();
30060     },
30061     
30062     onRender : function(ct, position) 
30063     {
30064         var _this = this;
30065         
30066         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30067         
30068         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30069         
30070         this.dayField = new Roo.bootstrap.ComboBox({
30071             allowBlank : this.dayAllowBlank,
30072             alwaysQuery : true,
30073             displayField : 'value',
30074             editable : false,
30075             fieldLabel : '',
30076             forceSelection : true,
30077             mode : 'local',
30078             placeholder : this.dayPlaceholder,
30079             selectOnFocus : true,
30080             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30081             triggerAction : 'all',
30082             typeAhead : true,
30083             valueField : 'value',
30084             store : new Roo.data.SimpleStore({
30085                 data : (function() {    
30086                     var days = [];
30087                     _this.fireEvent('days', _this, days);
30088                     return days;
30089                 })(),
30090                 fields : [ 'value' ]
30091             }),
30092             listeners : {
30093                 select : function (_self, record, index)
30094                 {
30095                     _this.setValue(_this.getValue());
30096                 }
30097             }
30098         });
30099
30100         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30101         
30102         this.monthField = new Roo.bootstrap.MonthField({
30103             after : '<i class=\"fa fa-calendar\"></i>',
30104             allowBlank : this.monthAllowBlank,
30105             placeholder : this.monthPlaceholder,
30106             readOnly : true,
30107             listeners : {
30108                 render : function (_self)
30109                 {
30110                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30111                         e.preventDefault();
30112                         _self.focus();
30113                     });
30114                 },
30115                 select : function (_self, oldvalue, newvalue)
30116                 {
30117                     _this.setValue(_this.getValue());
30118                 }
30119             }
30120         });
30121         
30122         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30123         
30124         this.yearField = new Roo.bootstrap.ComboBox({
30125             allowBlank : this.yearAllowBlank,
30126             alwaysQuery : true,
30127             displayField : 'value',
30128             editable : false,
30129             fieldLabel : '',
30130             forceSelection : true,
30131             mode : 'local',
30132             placeholder : this.yearPlaceholder,
30133             selectOnFocus : true,
30134             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30135             triggerAction : 'all',
30136             typeAhead : true,
30137             valueField : 'value',
30138             store : new Roo.data.SimpleStore({
30139                 data : (function() {
30140                     var years = [];
30141                     _this.fireEvent('years', _this, years);
30142                     return years;
30143                 })(),
30144                 fields : [ 'value' ]
30145             }),
30146             listeners : {
30147                 select : function (_self, record, index)
30148                 {
30149                     _this.setValue(_this.getValue());
30150                 }
30151             }
30152         });
30153
30154         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30155     },
30156     
30157     setValue : function(v, format)
30158     {
30159         this.inputEl.dom.value = v;
30160         
30161         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30162         
30163         var d = Date.parseDate(v, f);
30164         
30165         if(!d){
30166             this.validate();
30167             return;
30168         }
30169         
30170         this.setDay(d.format(this.dayFormat));
30171         this.setMonth(d.format(this.monthFormat));
30172         this.setYear(d.format(this.yearFormat));
30173         
30174         this.validate();
30175         
30176         return;
30177     },
30178     
30179     setDay : function(v)
30180     {
30181         this.dayField.setValue(v);
30182         this.inputEl.dom.value = this.getValue();
30183         this.validate();
30184         return;
30185     },
30186     
30187     setMonth : function(v)
30188     {
30189         this.monthField.setValue(v, true);
30190         this.inputEl.dom.value = this.getValue();
30191         this.validate();
30192         return;
30193     },
30194     
30195     setYear : function(v)
30196     {
30197         this.yearField.setValue(v);
30198         this.inputEl.dom.value = this.getValue();
30199         this.validate();
30200         return;
30201     },
30202     
30203     getDay : function()
30204     {
30205         return this.dayField.getValue();
30206     },
30207     
30208     getMonth : function()
30209     {
30210         return this.monthField.getValue();
30211     },
30212     
30213     getYear : function()
30214     {
30215         return this.yearField.getValue();
30216     },
30217     
30218     getValue : function()
30219     {
30220         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30221         
30222         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30223         
30224         return date;
30225     },
30226     
30227     reset : function()
30228     {
30229         this.setDay('');
30230         this.setMonth('');
30231         this.setYear('');
30232         this.inputEl.dom.value = '';
30233         this.validate();
30234         return;
30235     },
30236     
30237     validate : function()
30238     {
30239         var d = this.dayField.validate();
30240         var m = this.monthField.validate();
30241         var y = this.yearField.validate();
30242         
30243         var valid = true;
30244         
30245         if(
30246                 (!this.dayAllowBlank && !d) ||
30247                 (!this.monthAllowBlank && !m) ||
30248                 (!this.yearAllowBlank && !y)
30249         ){
30250             valid = false;
30251         }
30252         
30253         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30254             return valid;
30255         }
30256         
30257         if(valid){
30258             this.markValid();
30259             return valid;
30260         }
30261         
30262         this.markInvalid();
30263         
30264         return valid;
30265     },
30266     
30267     markValid : function()
30268     {
30269         
30270         var label = this.el.select('label', true).first();
30271         var icon = this.el.select('i.fa-star', true).first();
30272
30273         if(label && icon){
30274             icon.remove();
30275         }
30276         
30277         this.fireEvent('valid', this);
30278     },
30279     
30280      /**
30281      * Mark this field as invalid
30282      * @param {String} msg The validation message
30283      */
30284     markInvalid : function(msg)
30285     {
30286         
30287         var label = this.el.select('label', true).first();
30288         var icon = this.el.select('i.fa-star', true).first();
30289
30290         if(label && !icon){
30291             this.el.select('.roo-date-split-field-label', true).createChild({
30292                 tag : 'i',
30293                 cls : 'text-danger fa fa-lg fa-star',
30294                 tooltip : 'This field is required',
30295                 style : 'margin-right:5px;'
30296             }, label, true);
30297         }
30298         
30299         this.fireEvent('invalid', this, msg);
30300     },
30301     
30302     clearInvalid : function()
30303     {
30304         var label = this.el.select('label', true).first();
30305         var icon = this.el.select('i.fa-star', true).first();
30306
30307         if(label && icon){
30308             icon.remove();
30309         }
30310         
30311         this.fireEvent('valid', this);
30312     },
30313     
30314     getName: function()
30315     {
30316         return this.name;
30317     }
30318     
30319 });
30320
30321  /**
30322  *
30323  * This is based on 
30324  * http://masonry.desandro.com
30325  *
30326  * The idea is to render all the bricks based on vertical width...
30327  *
30328  * The original code extends 'outlayer' - we might need to use that....
30329  * 
30330  */
30331
30332
30333 /**
30334  * @class Roo.bootstrap.LayoutMasonry
30335  * @extends Roo.bootstrap.Component
30336  * Bootstrap Layout Masonry class
30337  * 
30338  * @constructor
30339  * Create a new Element
30340  * @param {Object} config The config object
30341  */
30342
30343 Roo.bootstrap.LayoutMasonry = function(config){
30344     
30345     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30346     
30347     this.bricks = [];
30348     
30349     Roo.bootstrap.LayoutMasonry.register(this);
30350     
30351     this.addEvents({
30352         // raw events
30353         /**
30354          * @event layout
30355          * Fire after layout the items
30356          * @param {Roo.bootstrap.LayoutMasonry} this
30357          * @param {Roo.EventObject} e
30358          */
30359         "layout" : true
30360     });
30361     
30362 };
30363
30364 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30365     
30366     /**
30367      * @cfg {Boolean} isLayoutInstant = no animation?
30368      */   
30369     isLayoutInstant : false, // needed?
30370    
30371     /**
30372      * @cfg {Number} boxWidth  width of the columns
30373      */   
30374     boxWidth : 450,
30375     
30376       /**
30377      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30378      */   
30379     boxHeight : 0,
30380     
30381     /**
30382      * @cfg {Number} padWidth padding below box..
30383      */   
30384     padWidth : 10, 
30385     
30386     /**
30387      * @cfg {Number} gutter gutter width..
30388      */   
30389     gutter : 10,
30390     
30391      /**
30392      * @cfg {Number} maxCols maximum number of columns
30393      */   
30394     
30395     maxCols: 0,
30396     
30397     /**
30398      * @cfg {Boolean} isAutoInitial defalut true
30399      */   
30400     isAutoInitial : true, 
30401     
30402     containerWidth: 0,
30403     
30404     /**
30405      * @cfg {Boolean} isHorizontal defalut false
30406      */   
30407     isHorizontal : false, 
30408
30409     currentSize : null,
30410     
30411     tag: 'div',
30412     
30413     cls: '',
30414     
30415     bricks: null, //CompositeElement
30416     
30417     cols : 1,
30418     
30419     _isLayoutInited : false,
30420     
30421 //    isAlternative : false, // only use for vertical layout...
30422     
30423     /**
30424      * @cfg {Number} alternativePadWidth padding below box..
30425      */   
30426     alternativePadWidth : 50,
30427     
30428     selectedBrick : [],
30429     
30430     getAutoCreate : function(){
30431         
30432         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30433         
30434         var cfg = {
30435             tag: this.tag,
30436             cls: 'blog-masonary-wrapper ' + this.cls,
30437             cn : {
30438                 cls : 'mas-boxes masonary'
30439             }
30440         };
30441         
30442         return cfg;
30443     },
30444     
30445     getChildContainer: function( )
30446     {
30447         if (this.boxesEl) {
30448             return this.boxesEl;
30449         }
30450         
30451         this.boxesEl = this.el.select('.mas-boxes').first();
30452         
30453         return this.boxesEl;
30454     },
30455     
30456     
30457     initEvents : function()
30458     {
30459         var _this = this;
30460         
30461         if(this.isAutoInitial){
30462             Roo.log('hook children rendered');
30463             this.on('childrenrendered', function() {
30464                 Roo.log('children rendered');
30465                 _this.initial();
30466             } ,this);
30467         }
30468     },
30469     
30470     initial : function()
30471     {
30472         this.selectedBrick = [];
30473         
30474         this.currentSize = this.el.getBox(true);
30475         
30476         Roo.EventManager.onWindowResize(this.resize, this); 
30477
30478         if(!this.isAutoInitial){
30479             this.layout();
30480             return;
30481         }
30482         
30483         this.layout();
30484         
30485         return;
30486         //this.layout.defer(500,this);
30487         
30488     },
30489     
30490     resize : function()
30491     {
30492         var cs = this.el.getBox(true);
30493         
30494         if (
30495                 this.currentSize.width == cs.width && 
30496                 this.currentSize.x == cs.x && 
30497                 this.currentSize.height == cs.height && 
30498                 this.currentSize.y == cs.y 
30499         ) {
30500             Roo.log("no change in with or X or Y");
30501             return;
30502         }
30503         
30504         this.currentSize = cs;
30505         
30506         this.layout();
30507         
30508     },
30509     
30510     layout : function()
30511     {   
30512         this._resetLayout();
30513         
30514         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30515         
30516         this.layoutItems( isInstant );
30517       
30518         this._isLayoutInited = true;
30519         
30520         this.fireEvent('layout', this);
30521         
30522     },
30523     
30524     _resetLayout : function()
30525     {
30526         if(this.isHorizontal){
30527             this.horizontalMeasureColumns();
30528             return;
30529         }
30530         
30531         this.verticalMeasureColumns();
30532         
30533     },
30534     
30535     verticalMeasureColumns : function()
30536     {
30537         this.getContainerWidth();
30538         
30539 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30540 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30541 //            return;
30542 //        }
30543         
30544         var boxWidth = this.boxWidth + this.padWidth;
30545         
30546         if(this.containerWidth < this.boxWidth){
30547             boxWidth = this.containerWidth
30548         }
30549         
30550         var containerWidth = this.containerWidth;
30551         
30552         var cols = Math.floor(containerWidth / boxWidth);
30553         
30554         this.cols = Math.max( cols, 1 );
30555         
30556         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30557         
30558         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30559         
30560         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30561         
30562         this.colWidth = boxWidth + avail - this.padWidth;
30563         
30564         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30565         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30566     },
30567     
30568     horizontalMeasureColumns : function()
30569     {
30570         this.getContainerWidth();
30571         
30572         var boxWidth = this.boxWidth;
30573         
30574         if(this.containerWidth < boxWidth){
30575             boxWidth = this.containerWidth;
30576         }
30577         
30578         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30579         
30580         this.el.setHeight(boxWidth);
30581         
30582     },
30583     
30584     getContainerWidth : function()
30585     {
30586         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30587     },
30588     
30589     layoutItems : function( isInstant )
30590     {
30591         Roo.log(this.bricks);
30592         
30593         var items = Roo.apply([], this.bricks);
30594         
30595         if(this.isHorizontal){
30596             this._horizontalLayoutItems( items , isInstant );
30597             return;
30598         }
30599         
30600 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30601 //            this._verticalAlternativeLayoutItems( items , isInstant );
30602 //            return;
30603 //        }
30604         
30605         this._verticalLayoutItems( items , isInstant );
30606         
30607     },
30608     
30609     _verticalLayoutItems : function ( items , isInstant)
30610     {
30611         if ( !items || !items.length ) {
30612             return;
30613         }
30614         
30615         var standard = [
30616             ['xs', 'xs', 'xs', 'tall'],
30617             ['xs', 'xs', 'tall'],
30618             ['xs', 'xs', 'sm'],
30619             ['xs', 'xs', 'xs'],
30620             ['xs', 'tall'],
30621             ['xs', 'sm'],
30622             ['xs', 'xs'],
30623             ['xs'],
30624             
30625             ['sm', 'xs', 'xs'],
30626             ['sm', 'xs'],
30627             ['sm'],
30628             
30629             ['tall', 'xs', 'xs', 'xs'],
30630             ['tall', 'xs', 'xs'],
30631             ['tall', 'xs'],
30632             ['tall']
30633             
30634         ];
30635         
30636         var queue = [];
30637         
30638         var boxes = [];
30639         
30640         var box = [];
30641         
30642         Roo.each(items, function(item, k){
30643             
30644             switch (item.size) {
30645                 // these layouts take up a full box,
30646                 case 'md' :
30647                 case 'md-left' :
30648                 case 'md-right' :
30649                 case 'wide' :
30650                     
30651                     if(box.length){
30652                         boxes.push(box);
30653                         box = [];
30654                     }
30655                     
30656                     boxes.push([item]);
30657                     
30658                     break;
30659                     
30660                 case 'xs' :
30661                 case 'sm' :
30662                 case 'tall' :
30663                     
30664                     box.push(item);
30665                     
30666                     break;
30667                 default :
30668                     break;
30669                     
30670             }
30671             
30672         }, this);
30673         
30674         if(box.length){
30675             boxes.push(box);
30676             box = [];
30677         }
30678         
30679         var filterPattern = function(box, length)
30680         {
30681             if(!box.length){
30682                 return;
30683             }
30684             
30685             var match = false;
30686             
30687             var pattern = box.slice(0, length);
30688             
30689             var format = [];
30690             
30691             Roo.each(pattern, function(i){
30692                 format.push(i.size);
30693             }, this);
30694             
30695             Roo.each(standard, function(s){
30696                 
30697                 if(String(s) != String(format)){
30698                     return;
30699                 }
30700                 
30701                 match = true;
30702                 return false;
30703                 
30704             }, this);
30705             
30706             if(!match && length == 1){
30707                 return;
30708             }
30709             
30710             if(!match){
30711                 filterPattern(box, length - 1);
30712                 return;
30713             }
30714                 
30715             queue.push(pattern);
30716
30717             box = box.slice(length, box.length);
30718
30719             filterPattern(box, 4);
30720
30721             return;
30722             
30723         }
30724         
30725         Roo.each(boxes, function(box, k){
30726             
30727             if(!box.length){
30728                 return;
30729             }
30730             
30731             if(box.length == 1){
30732                 queue.push(box);
30733                 return;
30734             }
30735             
30736             filterPattern(box, 4);
30737             
30738         }, this);
30739         
30740         this._processVerticalLayoutQueue( queue, isInstant );
30741         
30742     },
30743     
30744 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30745 //    {
30746 //        if ( !items || !items.length ) {
30747 //            return;
30748 //        }
30749 //
30750 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30751 //        
30752 //    },
30753     
30754     _horizontalLayoutItems : function ( items , isInstant)
30755     {
30756         if ( !items || !items.length || items.length < 3) {
30757             return;
30758         }
30759         
30760         items.reverse();
30761         
30762         var eItems = items.slice(0, 3);
30763         
30764         items = items.slice(3, items.length);
30765         
30766         var standard = [
30767             ['xs', 'xs', 'xs', 'wide'],
30768             ['xs', 'xs', 'wide'],
30769             ['xs', 'xs', 'sm'],
30770             ['xs', 'xs', 'xs'],
30771             ['xs', 'wide'],
30772             ['xs', 'sm'],
30773             ['xs', 'xs'],
30774             ['xs'],
30775             
30776             ['sm', 'xs', 'xs'],
30777             ['sm', 'xs'],
30778             ['sm'],
30779             
30780             ['wide', 'xs', 'xs', 'xs'],
30781             ['wide', 'xs', 'xs'],
30782             ['wide', 'xs'],
30783             ['wide'],
30784             
30785             ['wide-thin']
30786         ];
30787         
30788         var queue = [];
30789         
30790         var boxes = [];
30791         
30792         var box = [];
30793         
30794         Roo.each(items, function(item, k){
30795             
30796             switch (item.size) {
30797                 case 'md' :
30798                 case 'md-left' :
30799                 case 'md-right' :
30800                 case 'tall' :
30801                     
30802                     if(box.length){
30803                         boxes.push(box);
30804                         box = [];
30805                     }
30806                     
30807                     boxes.push([item]);
30808                     
30809                     break;
30810                     
30811                 case 'xs' :
30812                 case 'sm' :
30813                 case 'wide' :
30814                 case 'wide-thin' :
30815                     
30816                     box.push(item);
30817                     
30818                     break;
30819                 default :
30820                     break;
30821                     
30822             }
30823             
30824         }, this);
30825         
30826         if(box.length){
30827             boxes.push(box);
30828             box = [];
30829         }
30830         
30831         var filterPattern = function(box, length)
30832         {
30833             if(!box.length){
30834                 return;
30835             }
30836             
30837             var match = false;
30838             
30839             var pattern = box.slice(0, length);
30840             
30841             var format = [];
30842             
30843             Roo.each(pattern, function(i){
30844                 format.push(i.size);
30845             }, this);
30846             
30847             Roo.each(standard, function(s){
30848                 
30849                 if(String(s) != String(format)){
30850                     return;
30851                 }
30852                 
30853                 match = true;
30854                 return false;
30855                 
30856             }, this);
30857             
30858             if(!match && length == 1){
30859                 return;
30860             }
30861             
30862             if(!match){
30863                 filterPattern(box, length - 1);
30864                 return;
30865             }
30866                 
30867             queue.push(pattern);
30868
30869             box = box.slice(length, box.length);
30870
30871             filterPattern(box, 4);
30872
30873             return;
30874             
30875         }
30876         
30877         Roo.each(boxes, function(box, k){
30878             
30879             if(!box.length){
30880                 return;
30881             }
30882             
30883             if(box.length == 1){
30884                 queue.push(box);
30885                 return;
30886             }
30887             
30888             filterPattern(box, 4);
30889             
30890         }, this);
30891         
30892         
30893         var prune = [];
30894         
30895         var pos = this.el.getBox(true);
30896         
30897         var minX = pos.x;
30898         
30899         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30900         
30901         var hit_end = false;
30902         
30903         Roo.each(queue, function(box){
30904             
30905             if(hit_end){
30906                 
30907                 Roo.each(box, function(b){
30908                 
30909                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30910                     b.el.hide();
30911
30912                 }, this);
30913
30914                 return;
30915             }
30916             
30917             var mx = 0;
30918             
30919             Roo.each(box, function(b){
30920                 
30921                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30922                 b.el.show();
30923
30924                 mx = Math.max(mx, b.x);
30925                 
30926             }, this);
30927             
30928             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30929             
30930             if(maxX < minX){
30931                 
30932                 Roo.each(box, function(b){
30933                 
30934                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30935                     b.el.hide();
30936                     
30937                 }, this);
30938                 
30939                 hit_end = true;
30940                 
30941                 return;
30942             }
30943             
30944             prune.push(box);
30945             
30946         }, this);
30947         
30948         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30949     },
30950     
30951     /** Sets position of item in DOM
30952     * @param {Element} item
30953     * @param {Number} x - horizontal position
30954     * @param {Number} y - vertical position
30955     * @param {Boolean} isInstant - disables transitions
30956     */
30957     _processVerticalLayoutQueue : function( queue, isInstant )
30958     {
30959         var pos = this.el.getBox(true);
30960         var x = pos.x;
30961         var y = pos.y;
30962         var maxY = [];
30963         
30964         for (var i = 0; i < this.cols; i++){
30965             maxY[i] = pos.y;
30966         }
30967         
30968         Roo.each(queue, function(box, k){
30969             
30970             var col = k % this.cols;
30971             
30972             Roo.each(box, function(b,kk){
30973                 
30974                 b.el.position('absolute');
30975                 
30976                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30977                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30978                 
30979                 if(b.size == 'md-left' || b.size == 'md-right'){
30980                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30981                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30982                 }
30983                 
30984                 b.el.setWidth(width);
30985                 b.el.setHeight(height);
30986                 // iframe?
30987                 b.el.select('iframe',true).setSize(width,height);
30988                 
30989             }, this);
30990             
30991             for (var i = 0; i < this.cols; i++){
30992                 
30993                 if(maxY[i] < maxY[col]){
30994                     col = i;
30995                     continue;
30996                 }
30997                 
30998                 col = Math.min(col, i);
30999                 
31000             }
31001             
31002             x = pos.x + col * (this.colWidth + this.padWidth);
31003             
31004             y = maxY[col];
31005             
31006             var positions = [];
31007             
31008             switch (box.length){
31009                 case 1 :
31010                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31011                     break;
31012                 case 2 :
31013                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31014                     break;
31015                 case 3 :
31016                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31017                     break;
31018                 case 4 :
31019                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31020                     break;
31021                 default :
31022                     break;
31023             }
31024             
31025             Roo.each(box, function(b,kk){
31026                 
31027                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31028                 
31029                 var sz = b.el.getSize();
31030                 
31031                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31032                 
31033             }, this);
31034             
31035         }, this);
31036         
31037         var mY = 0;
31038         
31039         for (var i = 0; i < this.cols; i++){
31040             mY = Math.max(mY, maxY[i]);
31041         }
31042         
31043         this.el.setHeight(mY - pos.y);
31044         
31045     },
31046     
31047 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31048 //    {
31049 //        var pos = this.el.getBox(true);
31050 //        var x = pos.x;
31051 //        var y = pos.y;
31052 //        var maxX = pos.right;
31053 //        
31054 //        var maxHeight = 0;
31055 //        
31056 //        Roo.each(items, function(item, k){
31057 //            
31058 //            var c = k % 2;
31059 //            
31060 //            item.el.position('absolute');
31061 //                
31062 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31063 //
31064 //            item.el.setWidth(width);
31065 //
31066 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31067 //
31068 //            item.el.setHeight(height);
31069 //            
31070 //            if(c == 0){
31071 //                item.el.setXY([x, y], isInstant ? false : true);
31072 //            } else {
31073 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31074 //            }
31075 //            
31076 //            y = y + height + this.alternativePadWidth;
31077 //            
31078 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31079 //            
31080 //        }, this);
31081 //        
31082 //        this.el.setHeight(maxHeight);
31083 //        
31084 //    },
31085     
31086     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31087     {
31088         var pos = this.el.getBox(true);
31089         
31090         var minX = pos.x;
31091         var minY = pos.y;
31092         
31093         var maxX = pos.right;
31094         
31095         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31096         
31097         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31098         
31099         Roo.each(queue, function(box, k){
31100             
31101             Roo.each(box, function(b, kk){
31102                 
31103                 b.el.position('absolute');
31104                 
31105                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31106                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31107                 
31108                 if(b.size == 'md-left' || b.size == 'md-right'){
31109                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31110                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31111                 }
31112                 
31113                 b.el.setWidth(width);
31114                 b.el.setHeight(height);
31115                 
31116             }, this);
31117             
31118             if(!box.length){
31119                 return;
31120             }
31121             
31122             var positions = [];
31123             
31124             switch (box.length){
31125                 case 1 :
31126                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31127                     break;
31128                 case 2 :
31129                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31130                     break;
31131                 case 3 :
31132                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31133                     break;
31134                 case 4 :
31135                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31136                     break;
31137                 default :
31138                     break;
31139             }
31140             
31141             Roo.each(box, function(b,kk){
31142                 
31143                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31144                 
31145                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31146                 
31147             }, this);
31148             
31149         }, this);
31150         
31151     },
31152     
31153     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31154     {
31155         Roo.each(eItems, function(b,k){
31156             
31157             b.size = (k == 0) ? 'sm' : 'xs';
31158             b.x = (k == 0) ? 2 : 1;
31159             b.y = (k == 0) ? 2 : 1;
31160             
31161             b.el.position('absolute');
31162             
31163             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31164                 
31165             b.el.setWidth(width);
31166             
31167             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31168             
31169             b.el.setHeight(height);
31170             
31171         }, this);
31172
31173         var positions = [];
31174         
31175         positions.push({
31176             x : maxX - this.unitWidth * 2 - this.gutter,
31177             y : minY
31178         });
31179         
31180         positions.push({
31181             x : maxX - this.unitWidth,
31182             y : minY + (this.unitWidth + this.gutter) * 2
31183         });
31184         
31185         positions.push({
31186             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31187             y : minY
31188         });
31189         
31190         Roo.each(eItems, function(b,k){
31191             
31192             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31193
31194         }, this);
31195         
31196     },
31197     
31198     getVerticalOneBoxColPositions : function(x, y, box)
31199     {
31200         var pos = [];
31201         
31202         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31203         
31204         if(box[0].size == 'md-left'){
31205             rand = 0;
31206         }
31207         
31208         if(box[0].size == 'md-right'){
31209             rand = 1;
31210         }
31211         
31212         pos.push({
31213             x : x + (this.unitWidth + this.gutter) * rand,
31214             y : y
31215         });
31216         
31217         return pos;
31218     },
31219     
31220     getVerticalTwoBoxColPositions : function(x, y, box)
31221     {
31222         var pos = [];
31223         
31224         if(box[0].size == 'xs'){
31225             
31226             pos.push({
31227                 x : x,
31228                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31229             });
31230
31231             pos.push({
31232                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31233                 y : y
31234             });
31235             
31236             return pos;
31237             
31238         }
31239         
31240         pos.push({
31241             x : x,
31242             y : y
31243         });
31244
31245         pos.push({
31246             x : x + (this.unitWidth + this.gutter) * 2,
31247             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31248         });
31249         
31250         return pos;
31251         
31252     },
31253     
31254     getVerticalThreeBoxColPositions : function(x, y, box)
31255     {
31256         var pos = [];
31257         
31258         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31259             
31260             pos.push({
31261                 x : x,
31262                 y : y
31263             });
31264
31265             pos.push({
31266                 x : x + (this.unitWidth + this.gutter) * 1,
31267                 y : y
31268             });
31269             
31270             pos.push({
31271                 x : x + (this.unitWidth + this.gutter) * 2,
31272                 y : y
31273             });
31274             
31275             return pos;
31276             
31277         }
31278         
31279         if(box[0].size == 'xs' && box[1].size == 'xs'){
31280             
31281             pos.push({
31282                 x : x,
31283                 y : y
31284             });
31285
31286             pos.push({
31287                 x : x,
31288                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31289             });
31290             
31291             pos.push({
31292                 x : x + (this.unitWidth + this.gutter) * 1,
31293                 y : y
31294             });
31295             
31296             return pos;
31297             
31298         }
31299         
31300         pos.push({
31301             x : x,
31302             y : y
31303         });
31304
31305         pos.push({
31306             x : x + (this.unitWidth + this.gutter) * 2,
31307             y : y
31308         });
31309
31310         pos.push({
31311             x : x + (this.unitWidth + this.gutter) * 2,
31312             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31313         });
31314             
31315         return pos;
31316         
31317     },
31318     
31319     getVerticalFourBoxColPositions : function(x, y, box)
31320     {
31321         var pos = [];
31322         
31323         if(box[0].size == 'xs'){
31324             
31325             pos.push({
31326                 x : x,
31327                 y : y
31328             });
31329
31330             pos.push({
31331                 x : x,
31332                 y : y + (this.unitHeight + this.gutter) * 1
31333             });
31334             
31335             pos.push({
31336                 x : x,
31337                 y : y + (this.unitHeight + this.gutter) * 2
31338             });
31339             
31340             pos.push({
31341                 x : x + (this.unitWidth + this.gutter) * 1,
31342                 y : y
31343             });
31344             
31345             return pos;
31346             
31347         }
31348         
31349         pos.push({
31350             x : x,
31351             y : y
31352         });
31353
31354         pos.push({
31355             x : x + (this.unitWidth + this.gutter) * 2,
31356             y : y
31357         });
31358
31359         pos.push({
31360             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31361             y : y + (this.unitHeight + this.gutter) * 1
31362         });
31363
31364         pos.push({
31365             x : x + (this.unitWidth + this.gutter) * 2,
31366             y : y + (this.unitWidth + this.gutter) * 2
31367         });
31368
31369         return pos;
31370         
31371     },
31372     
31373     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31374     {
31375         var pos = [];
31376         
31377         if(box[0].size == 'md-left'){
31378             pos.push({
31379                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31380                 y : minY
31381             });
31382             
31383             return pos;
31384         }
31385         
31386         if(box[0].size == 'md-right'){
31387             pos.push({
31388                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31389                 y : minY + (this.unitWidth + this.gutter) * 1
31390             });
31391             
31392             return pos;
31393         }
31394         
31395         var rand = Math.floor(Math.random() * (4 - box[0].y));
31396         
31397         pos.push({
31398             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31399             y : minY + (this.unitWidth + this.gutter) * rand
31400         });
31401         
31402         return pos;
31403         
31404     },
31405     
31406     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31407     {
31408         var pos = [];
31409         
31410         if(box[0].size == 'xs'){
31411             
31412             pos.push({
31413                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31414                 y : minY
31415             });
31416
31417             pos.push({
31418                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31419                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31420             });
31421             
31422             return pos;
31423             
31424         }
31425         
31426         pos.push({
31427             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31428             y : minY
31429         });
31430
31431         pos.push({
31432             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31433             y : minY + (this.unitWidth + this.gutter) * 2
31434         });
31435         
31436         return pos;
31437         
31438     },
31439     
31440     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31441     {
31442         var pos = [];
31443         
31444         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31445             
31446             pos.push({
31447                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31448                 y : minY
31449             });
31450
31451             pos.push({
31452                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31453                 y : minY + (this.unitWidth + this.gutter) * 1
31454             });
31455             
31456             pos.push({
31457                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31458                 y : minY + (this.unitWidth + this.gutter) * 2
31459             });
31460             
31461             return pos;
31462             
31463         }
31464         
31465         if(box[0].size == 'xs' && box[1].size == 'xs'){
31466             
31467             pos.push({
31468                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31469                 y : minY
31470             });
31471
31472             pos.push({
31473                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31474                 y : minY
31475             });
31476             
31477             pos.push({
31478                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31479                 y : minY + (this.unitWidth + this.gutter) * 1
31480             });
31481             
31482             return pos;
31483             
31484         }
31485         
31486         pos.push({
31487             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31488             y : minY
31489         });
31490
31491         pos.push({
31492             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31493             y : minY + (this.unitWidth + this.gutter) * 2
31494         });
31495
31496         pos.push({
31497             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31498             y : minY + (this.unitWidth + this.gutter) * 2
31499         });
31500             
31501         return pos;
31502         
31503     },
31504     
31505     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31506     {
31507         var pos = [];
31508         
31509         if(box[0].size == 'xs'){
31510             
31511             pos.push({
31512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31513                 y : minY
31514             });
31515
31516             pos.push({
31517                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31518                 y : minY
31519             });
31520             
31521             pos.push({
31522                 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),
31523                 y : minY
31524             });
31525             
31526             pos.push({
31527                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31528                 y : minY + (this.unitWidth + this.gutter) * 1
31529             });
31530             
31531             return pos;
31532             
31533         }
31534         
31535         pos.push({
31536             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31537             y : minY
31538         });
31539         
31540         pos.push({
31541             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31542             y : minY + (this.unitWidth + this.gutter) * 2
31543         });
31544         
31545         pos.push({
31546             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31547             y : minY + (this.unitWidth + this.gutter) * 2
31548         });
31549         
31550         pos.push({
31551             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),
31552             y : minY + (this.unitWidth + this.gutter) * 2
31553         });
31554
31555         return pos;
31556         
31557     },
31558     
31559     /**
31560     * remove a Masonry Brick
31561     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31562     */
31563     removeBrick : function(brick_id)
31564     {
31565         if (!brick_id) {
31566             return;
31567         }
31568         
31569         for (var i = 0; i<this.bricks.length; i++) {
31570             if (this.bricks[i].id == brick_id) {
31571                 this.bricks.splice(i,1);
31572                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31573                 this.initial();
31574             }
31575         }
31576     },
31577     
31578     /**
31579     * adds a Masonry Brick
31580     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31581     */
31582     addBrick : function(cfg)
31583     {
31584         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31585         //this.register(cn);
31586         cn.parentId = this.id;
31587         cn.onRender(this.el, null);
31588         return cn;
31589     },
31590     
31591     /**
31592     * register a Masonry Brick
31593     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31594     */
31595     
31596     register : function(brick)
31597     {
31598         this.bricks.push(brick);
31599         brick.masonryId = this.id;
31600     },
31601     
31602     /**
31603     * clear all the Masonry Brick
31604     */
31605     clearAll : function()
31606     {
31607         this.bricks = [];
31608         //this.getChildContainer().dom.innerHTML = "";
31609         this.el.dom.innerHTML = '';
31610     },
31611     
31612     getSelected : function()
31613     {
31614         if (!this.selectedBrick) {
31615             return false;
31616         }
31617         
31618         return this.selectedBrick;
31619     }
31620 });
31621
31622 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31623     
31624     groups: {},
31625      /**
31626     * register a Masonry Layout
31627     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31628     */
31629     
31630     register : function(layout)
31631     {
31632         this.groups[layout.id] = layout;
31633     },
31634     /**
31635     * fetch a  Masonry Layout based on the masonry layout ID
31636     * @param {string} the masonry layout to add
31637     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31638     */
31639     
31640     get: function(layout_id) {
31641         if (typeof(this.groups[layout_id]) == 'undefined') {
31642             return false;
31643         }
31644         return this.groups[layout_id] ;
31645     }
31646     
31647     
31648     
31649 });
31650
31651  
31652
31653  /**
31654  *
31655  * This is based on 
31656  * http://masonry.desandro.com
31657  *
31658  * The idea is to render all the bricks based on vertical width...
31659  *
31660  * The original code extends 'outlayer' - we might need to use that....
31661  * 
31662  */
31663
31664
31665 /**
31666  * @class Roo.bootstrap.LayoutMasonryAuto
31667  * @extends Roo.bootstrap.Component
31668  * Bootstrap Layout Masonry class
31669  * 
31670  * @constructor
31671  * Create a new Element
31672  * @param {Object} config The config object
31673  */
31674
31675 Roo.bootstrap.LayoutMasonryAuto = function(config){
31676     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31677 };
31678
31679 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31680     
31681       /**
31682      * @cfg {Boolean} isFitWidth  - resize the width..
31683      */   
31684     isFitWidth : false,  // options..
31685     /**
31686      * @cfg {Boolean} isOriginLeft = left align?
31687      */   
31688     isOriginLeft : true,
31689     /**
31690      * @cfg {Boolean} isOriginTop = top align?
31691      */   
31692     isOriginTop : false,
31693     /**
31694      * @cfg {Boolean} isLayoutInstant = no animation?
31695      */   
31696     isLayoutInstant : false, // needed?
31697     /**
31698      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31699      */   
31700     isResizingContainer : true,
31701     /**
31702      * @cfg {Number} columnWidth  width of the columns 
31703      */   
31704     
31705     columnWidth : 0,
31706     
31707     /**
31708      * @cfg {Number} maxCols maximum number of columns
31709      */   
31710     
31711     maxCols: 0,
31712     /**
31713      * @cfg {Number} padHeight padding below box..
31714      */   
31715     
31716     padHeight : 10, 
31717     
31718     /**
31719      * @cfg {Boolean} isAutoInitial defalut true
31720      */   
31721     
31722     isAutoInitial : true, 
31723     
31724     // private?
31725     gutter : 0,
31726     
31727     containerWidth: 0,
31728     initialColumnWidth : 0,
31729     currentSize : null,
31730     
31731     colYs : null, // array.
31732     maxY : 0,
31733     padWidth: 10,
31734     
31735     
31736     tag: 'div',
31737     cls: '',
31738     bricks: null, //CompositeElement
31739     cols : 0, // array?
31740     // element : null, // wrapped now this.el
31741     _isLayoutInited : null, 
31742     
31743     
31744     getAutoCreate : function(){
31745         
31746         var cfg = {
31747             tag: this.tag,
31748             cls: 'blog-masonary-wrapper ' + this.cls,
31749             cn : {
31750                 cls : 'mas-boxes masonary'
31751             }
31752         };
31753         
31754         return cfg;
31755     },
31756     
31757     getChildContainer: function( )
31758     {
31759         if (this.boxesEl) {
31760             return this.boxesEl;
31761         }
31762         
31763         this.boxesEl = this.el.select('.mas-boxes').first();
31764         
31765         return this.boxesEl;
31766     },
31767     
31768     
31769     initEvents : function()
31770     {
31771         var _this = this;
31772         
31773         if(this.isAutoInitial){
31774             Roo.log('hook children rendered');
31775             this.on('childrenrendered', function() {
31776                 Roo.log('children rendered');
31777                 _this.initial();
31778             } ,this);
31779         }
31780         
31781     },
31782     
31783     initial : function()
31784     {
31785         this.reloadItems();
31786
31787         this.currentSize = this.el.getBox(true);
31788
31789         /// was window resize... - let's see if this works..
31790         Roo.EventManager.onWindowResize(this.resize, this); 
31791
31792         if(!this.isAutoInitial){
31793             this.layout();
31794             return;
31795         }
31796         
31797         this.layout.defer(500,this);
31798     },
31799     
31800     reloadItems: function()
31801     {
31802         this.bricks = this.el.select('.masonry-brick', true);
31803         
31804         this.bricks.each(function(b) {
31805             //Roo.log(b.getSize());
31806             if (!b.attr('originalwidth')) {
31807                 b.attr('originalwidth',  b.getSize().width);
31808             }
31809             
31810         });
31811         
31812         Roo.log(this.bricks.elements.length);
31813     },
31814     
31815     resize : function()
31816     {
31817         Roo.log('resize');
31818         var cs = this.el.getBox(true);
31819         
31820         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31821             Roo.log("no change in with or X");
31822             return;
31823         }
31824         this.currentSize = cs;
31825         this.layout();
31826     },
31827     
31828     layout : function()
31829     {
31830          Roo.log('layout');
31831         this._resetLayout();
31832         //this._manageStamps();
31833       
31834         // don't animate first layout
31835         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31836         this.layoutItems( isInstant );
31837       
31838         // flag for initalized
31839         this._isLayoutInited = true;
31840     },
31841     
31842     layoutItems : function( isInstant )
31843     {
31844         //var items = this._getItemsForLayout( this.items );
31845         // original code supports filtering layout items.. we just ignore it..
31846         
31847         this._layoutItems( this.bricks , isInstant );
31848       
31849         this._postLayout();
31850     },
31851     _layoutItems : function ( items , isInstant)
31852     {
31853        //this.fireEvent( 'layout', this, items );
31854     
31855
31856         if ( !items || !items.elements.length ) {
31857           // no items, emit event with empty array
31858             return;
31859         }
31860
31861         var queue = [];
31862         items.each(function(item) {
31863             Roo.log("layout item");
31864             Roo.log(item);
31865             // get x/y object from method
31866             var position = this._getItemLayoutPosition( item );
31867             // enqueue
31868             position.item = item;
31869             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31870             queue.push( position );
31871         }, this);
31872       
31873         this._processLayoutQueue( queue );
31874     },
31875     /** Sets position of item in DOM
31876     * @param {Element} item
31877     * @param {Number} x - horizontal position
31878     * @param {Number} y - vertical position
31879     * @param {Boolean} isInstant - disables transitions
31880     */
31881     _processLayoutQueue : function( queue )
31882     {
31883         for ( var i=0, len = queue.length; i < len; i++ ) {
31884             var obj = queue[i];
31885             obj.item.position('absolute');
31886             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31887         }
31888     },
31889       
31890     
31891     /**
31892     * Any logic you want to do after each layout,
31893     * i.e. size the container
31894     */
31895     _postLayout : function()
31896     {
31897         this.resizeContainer();
31898     },
31899     
31900     resizeContainer : function()
31901     {
31902         if ( !this.isResizingContainer ) {
31903             return;
31904         }
31905         var size = this._getContainerSize();
31906         if ( size ) {
31907             this.el.setSize(size.width,size.height);
31908             this.boxesEl.setSize(size.width,size.height);
31909         }
31910     },
31911     
31912     
31913     
31914     _resetLayout : function()
31915     {
31916         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31917         this.colWidth = this.el.getWidth();
31918         //this.gutter = this.el.getWidth(); 
31919         
31920         this.measureColumns();
31921
31922         // reset column Y
31923         var i = this.cols;
31924         this.colYs = [];
31925         while (i--) {
31926             this.colYs.push( 0 );
31927         }
31928     
31929         this.maxY = 0;
31930     },
31931
31932     measureColumns : function()
31933     {
31934         this.getContainerWidth();
31935       // if columnWidth is 0, default to outerWidth of first item
31936         if ( !this.columnWidth ) {
31937             var firstItem = this.bricks.first();
31938             Roo.log(firstItem);
31939             this.columnWidth  = this.containerWidth;
31940             if (firstItem && firstItem.attr('originalwidth') ) {
31941                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31942             }
31943             // columnWidth fall back to item of first element
31944             Roo.log("set column width?");
31945                         this.initialColumnWidth = this.columnWidth  ;
31946
31947             // if first elem has no width, default to size of container
31948             
31949         }
31950         
31951         
31952         if (this.initialColumnWidth) {
31953             this.columnWidth = this.initialColumnWidth;
31954         }
31955         
31956         
31957             
31958         // column width is fixed at the top - however if container width get's smaller we should
31959         // reduce it...
31960         
31961         // this bit calcs how man columns..
31962             
31963         var columnWidth = this.columnWidth += this.gutter;
31964       
31965         // calculate columns
31966         var containerWidth = this.containerWidth + this.gutter;
31967         
31968         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31969         // fix rounding errors, typically with gutters
31970         var excess = columnWidth - containerWidth % columnWidth;
31971         
31972         
31973         // if overshoot is less than a pixel, round up, otherwise floor it
31974         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31975         cols = Math[ mathMethod ]( cols );
31976         this.cols = Math.max( cols, 1 );
31977         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31978         
31979          // padding positioning..
31980         var totalColWidth = this.cols * this.columnWidth;
31981         var padavail = this.containerWidth - totalColWidth;
31982         // so for 2 columns - we need 3 'pads'
31983         
31984         var padNeeded = (1+this.cols) * this.padWidth;
31985         
31986         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31987         
31988         this.columnWidth += padExtra
31989         //this.padWidth = Math.floor(padavail /  ( this.cols));
31990         
31991         // adjust colum width so that padding is fixed??
31992         
31993         // we have 3 columns ... total = width * 3
31994         // we have X left over... that should be used by 
31995         
31996         //if (this.expandC) {
31997             
31998         //}
31999         
32000         
32001         
32002     },
32003     
32004     getContainerWidth : function()
32005     {
32006        /* // container is parent if fit width
32007         var container = this.isFitWidth ? this.element.parentNode : this.element;
32008         // check that this.size and size are there
32009         // IE8 triggers resize on body size change, so they might not be
32010         
32011         var size = getSize( container );  //FIXME
32012         this.containerWidth = size && size.innerWidth; //FIXME
32013         */
32014          
32015         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32016         
32017     },
32018     
32019     _getItemLayoutPosition : function( item )  // what is item?
32020     {
32021         // we resize the item to our columnWidth..
32022       
32023         item.setWidth(this.columnWidth);
32024         item.autoBoxAdjust  = false;
32025         
32026         var sz = item.getSize();
32027  
32028         // how many columns does this brick span
32029         var remainder = this.containerWidth % this.columnWidth;
32030         
32031         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32032         // round if off by 1 pixel, otherwise use ceil
32033         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32034         colSpan = Math.min( colSpan, this.cols );
32035         
32036         // normally this should be '1' as we dont' currently allow multi width columns..
32037         
32038         var colGroup = this._getColGroup( colSpan );
32039         // get the minimum Y value from the columns
32040         var minimumY = Math.min.apply( Math, colGroup );
32041         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32042         
32043         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32044          
32045         // position the brick
32046         var position = {
32047             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32048             y: this.currentSize.y + minimumY + this.padHeight
32049         };
32050         
32051         Roo.log(position);
32052         // apply setHeight to necessary columns
32053         var setHeight = minimumY + sz.height + this.padHeight;
32054         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32055         
32056         var setSpan = this.cols + 1 - colGroup.length;
32057         for ( var i = 0; i < setSpan; i++ ) {
32058           this.colYs[ shortColIndex + i ] = setHeight ;
32059         }
32060       
32061         return position;
32062     },
32063     
32064     /**
32065      * @param {Number} colSpan - number of columns the element spans
32066      * @returns {Array} colGroup
32067      */
32068     _getColGroup : function( colSpan )
32069     {
32070         if ( colSpan < 2 ) {
32071           // if brick spans only one column, use all the column Ys
32072           return this.colYs;
32073         }
32074       
32075         var colGroup = [];
32076         // how many different places could this brick fit horizontally
32077         var groupCount = this.cols + 1 - colSpan;
32078         // for each group potential horizontal position
32079         for ( var i = 0; i < groupCount; i++ ) {
32080           // make an array of colY values for that one group
32081           var groupColYs = this.colYs.slice( i, i + colSpan );
32082           // and get the max value of the array
32083           colGroup[i] = Math.max.apply( Math, groupColYs );
32084         }
32085         return colGroup;
32086     },
32087     /*
32088     _manageStamp : function( stamp )
32089     {
32090         var stampSize =  stamp.getSize();
32091         var offset = stamp.getBox();
32092         // get the columns that this stamp affects
32093         var firstX = this.isOriginLeft ? offset.x : offset.right;
32094         var lastX = firstX + stampSize.width;
32095         var firstCol = Math.floor( firstX / this.columnWidth );
32096         firstCol = Math.max( 0, firstCol );
32097         
32098         var lastCol = Math.floor( lastX / this.columnWidth );
32099         // lastCol should not go over if multiple of columnWidth #425
32100         lastCol -= lastX % this.columnWidth ? 0 : 1;
32101         lastCol = Math.min( this.cols - 1, lastCol );
32102         
32103         // set colYs to bottom of the stamp
32104         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32105             stampSize.height;
32106             
32107         for ( var i = firstCol; i <= lastCol; i++ ) {
32108           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32109         }
32110     },
32111     */
32112     
32113     _getContainerSize : function()
32114     {
32115         this.maxY = Math.max.apply( Math, this.colYs );
32116         var size = {
32117             height: this.maxY
32118         };
32119       
32120         if ( this.isFitWidth ) {
32121             size.width = this._getContainerFitWidth();
32122         }
32123       
32124         return size;
32125     },
32126     
32127     _getContainerFitWidth : function()
32128     {
32129         var unusedCols = 0;
32130         // count unused columns
32131         var i = this.cols;
32132         while ( --i ) {
32133           if ( this.colYs[i] !== 0 ) {
32134             break;
32135           }
32136           unusedCols++;
32137         }
32138         // fit container to columns that have been used
32139         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32140     },
32141     
32142     needsResizeLayout : function()
32143     {
32144         var previousWidth = this.containerWidth;
32145         this.getContainerWidth();
32146         return previousWidth !== this.containerWidth;
32147     }
32148  
32149 });
32150
32151  
32152
32153  /*
32154  * - LGPL
32155  *
32156  * element
32157  * 
32158  */
32159
32160 /**
32161  * @class Roo.bootstrap.MasonryBrick
32162  * @extends Roo.bootstrap.Component
32163  * Bootstrap MasonryBrick class
32164  * 
32165  * @constructor
32166  * Create a new MasonryBrick
32167  * @param {Object} config The config object
32168  */
32169
32170 Roo.bootstrap.MasonryBrick = function(config){
32171     
32172     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32173     
32174     Roo.bootstrap.MasonryBrick.register(this);
32175     
32176     this.addEvents({
32177         // raw events
32178         /**
32179          * @event click
32180          * When a MasonryBrick is clcik
32181          * @param {Roo.bootstrap.MasonryBrick} this
32182          * @param {Roo.EventObject} e
32183          */
32184         "click" : true
32185     });
32186 };
32187
32188 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32189     
32190     /**
32191      * @cfg {String} title
32192      */   
32193     title : '',
32194     /**
32195      * @cfg {String} html
32196      */   
32197     html : '',
32198     /**
32199      * @cfg {String} bgimage
32200      */   
32201     bgimage : '',
32202     /**
32203      * @cfg {String} videourl
32204      */   
32205     videourl : '',
32206     /**
32207      * @cfg {String} cls
32208      */   
32209     cls : '',
32210     /**
32211      * @cfg {String} href
32212      */   
32213     href : '',
32214     /**
32215      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32216      */   
32217     size : 'xs',
32218     
32219     /**
32220      * @cfg {String} placetitle (center|bottom)
32221      */   
32222     placetitle : '',
32223     
32224     /**
32225      * @cfg {Boolean} isFitContainer defalut true
32226      */   
32227     isFitContainer : true, 
32228     
32229     /**
32230      * @cfg {Boolean} preventDefault defalut false
32231      */   
32232     preventDefault : false, 
32233     
32234     /**
32235      * @cfg {Boolean} inverse defalut false
32236      */   
32237     maskInverse : false, 
32238     
32239     getAutoCreate : function()
32240     {
32241         if(!this.isFitContainer){
32242             return this.getSplitAutoCreate();
32243         }
32244         
32245         var cls = 'masonry-brick masonry-brick-full';
32246         
32247         if(this.href.length){
32248             cls += ' masonry-brick-link';
32249         }
32250         
32251         if(this.bgimage.length){
32252             cls += ' masonry-brick-image';
32253         }
32254         
32255         if(this.maskInverse){
32256             cls += ' mask-inverse';
32257         }
32258         
32259         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32260             cls += ' enable-mask';
32261         }
32262         
32263         if(this.size){
32264             cls += ' masonry-' + this.size + '-brick';
32265         }
32266         
32267         if(this.placetitle.length){
32268             
32269             switch (this.placetitle) {
32270                 case 'center' :
32271                     cls += ' masonry-center-title';
32272                     break;
32273                 case 'bottom' :
32274                     cls += ' masonry-bottom-title';
32275                     break;
32276                 default:
32277                     break;
32278             }
32279             
32280         } else {
32281             if(!this.html.length && !this.bgimage.length){
32282                 cls += ' masonry-center-title';
32283             }
32284
32285             if(!this.html.length && this.bgimage.length){
32286                 cls += ' masonry-bottom-title';
32287             }
32288         }
32289         
32290         if(this.cls){
32291             cls += ' ' + this.cls;
32292         }
32293         
32294         var cfg = {
32295             tag: (this.href.length) ? 'a' : 'div',
32296             cls: cls,
32297             cn: [
32298                 {
32299                     tag: 'div',
32300                     cls: 'masonry-brick-mask'
32301                 },
32302                 {
32303                     tag: 'div',
32304                     cls: 'masonry-brick-paragraph',
32305                     cn: []
32306                 }
32307             ]
32308         };
32309         
32310         if(this.href.length){
32311             cfg.href = this.href;
32312         }
32313         
32314         var cn = cfg.cn[1].cn;
32315         
32316         if(this.title.length){
32317             cn.push({
32318                 tag: 'h4',
32319                 cls: 'masonry-brick-title',
32320                 html: this.title
32321             });
32322         }
32323         
32324         if(this.html.length){
32325             cn.push({
32326                 tag: 'p',
32327                 cls: 'masonry-brick-text',
32328                 html: this.html
32329             });
32330         }
32331         
32332         if (!this.title.length && !this.html.length) {
32333             cfg.cn[1].cls += ' hide';
32334         }
32335         
32336         if(this.bgimage.length){
32337             cfg.cn.push({
32338                 tag: 'img',
32339                 cls: 'masonry-brick-image-view',
32340                 src: this.bgimage
32341             });
32342         }
32343         
32344         if(this.videourl.length){
32345             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32346             // youtube support only?
32347             cfg.cn.push({
32348                 tag: 'iframe',
32349                 cls: 'masonry-brick-image-view',
32350                 src: vurl,
32351                 frameborder : 0,
32352                 allowfullscreen : true
32353             });
32354         }
32355         
32356         return cfg;
32357         
32358     },
32359     
32360     getSplitAutoCreate : function()
32361     {
32362         var cls = 'masonry-brick masonry-brick-split';
32363         
32364         if(this.href.length){
32365             cls += ' masonry-brick-link';
32366         }
32367         
32368         if(this.bgimage.length){
32369             cls += ' masonry-brick-image';
32370         }
32371         
32372         if(this.size){
32373             cls += ' masonry-' + this.size + '-brick';
32374         }
32375         
32376         switch (this.placetitle) {
32377             case 'center' :
32378                 cls += ' masonry-center-title';
32379                 break;
32380             case 'bottom' :
32381                 cls += ' masonry-bottom-title';
32382                 break;
32383             default:
32384                 if(!this.bgimage.length){
32385                     cls += ' masonry-center-title';
32386                 }
32387
32388                 if(this.bgimage.length){
32389                     cls += ' masonry-bottom-title';
32390                 }
32391                 break;
32392         }
32393         
32394         if(this.cls){
32395             cls += ' ' + this.cls;
32396         }
32397         
32398         var cfg = {
32399             tag: (this.href.length) ? 'a' : 'div',
32400             cls: cls,
32401             cn: [
32402                 {
32403                     tag: 'div',
32404                     cls: 'masonry-brick-split-head',
32405                     cn: [
32406                         {
32407                             tag: 'div',
32408                             cls: 'masonry-brick-paragraph',
32409                             cn: []
32410                         }
32411                     ]
32412                 },
32413                 {
32414                     tag: 'div',
32415                     cls: 'masonry-brick-split-body',
32416                     cn: []
32417                 }
32418             ]
32419         };
32420         
32421         if(this.href.length){
32422             cfg.href = this.href;
32423         }
32424         
32425         if(this.title.length){
32426             cfg.cn[0].cn[0].cn.push({
32427                 tag: 'h4',
32428                 cls: 'masonry-brick-title',
32429                 html: this.title
32430             });
32431         }
32432         
32433         if(this.html.length){
32434             cfg.cn[1].cn.push({
32435                 tag: 'p',
32436                 cls: 'masonry-brick-text',
32437                 html: this.html
32438             });
32439         }
32440
32441         if(this.bgimage.length){
32442             cfg.cn[0].cn.push({
32443                 tag: 'img',
32444                 cls: 'masonry-brick-image-view',
32445                 src: this.bgimage
32446             });
32447         }
32448         
32449         if(this.videourl.length){
32450             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32451             // youtube support only?
32452             cfg.cn[0].cn.cn.push({
32453                 tag: 'iframe',
32454                 cls: 'masonry-brick-image-view',
32455                 src: vurl,
32456                 frameborder : 0,
32457                 allowfullscreen : true
32458             });
32459         }
32460         
32461         return cfg;
32462     },
32463     
32464     initEvents: function() 
32465     {
32466         switch (this.size) {
32467             case 'xs' :
32468                 this.x = 1;
32469                 this.y = 1;
32470                 break;
32471             case 'sm' :
32472                 this.x = 2;
32473                 this.y = 2;
32474                 break;
32475             case 'md' :
32476             case 'md-left' :
32477             case 'md-right' :
32478                 this.x = 3;
32479                 this.y = 3;
32480                 break;
32481             case 'tall' :
32482                 this.x = 2;
32483                 this.y = 3;
32484                 break;
32485             case 'wide' :
32486                 this.x = 3;
32487                 this.y = 2;
32488                 break;
32489             case 'wide-thin' :
32490                 this.x = 3;
32491                 this.y = 1;
32492                 break;
32493                         
32494             default :
32495                 break;
32496         }
32497         
32498         if(Roo.isTouch){
32499             this.el.on('touchstart', this.onTouchStart, this);
32500             this.el.on('touchmove', this.onTouchMove, this);
32501             this.el.on('touchend', this.onTouchEnd, this);
32502             this.el.on('contextmenu', this.onContextMenu, this);
32503         } else {
32504             this.el.on('mouseenter'  ,this.enter, this);
32505             this.el.on('mouseleave', this.leave, this);
32506             this.el.on('click', this.onClick, this);
32507         }
32508         
32509         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32510             this.parent().bricks.push(this);   
32511         }
32512         
32513     },
32514     
32515     onClick: function(e, el)
32516     {
32517         var time = this.endTimer - this.startTimer;
32518         // Roo.log(e.preventDefault());
32519         if(Roo.isTouch){
32520             if(time > 1000){
32521                 e.preventDefault();
32522                 return;
32523             }
32524         }
32525         
32526         if(!this.preventDefault){
32527             return;
32528         }
32529         
32530         e.preventDefault();
32531         
32532         if (this.activcClass != '') {
32533             this.selectBrick();
32534         }
32535         
32536         this.fireEvent('click', this);
32537     },
32538     
32539     enter: function(e, el)
32540     {
32541         e.preventDefault();
32542         
32543         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32544             return;
32545         }
32546         
32547         if(this.bgimage.length && this.html.length){
32548             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32549         }
32550     },
32551     
32552     leave: function(e, el)
32553     {
32554         e.preventDefault();
32555         
32556         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32557             return;
32558         }
32559         
32560         if(this.bgimage.length && this.html.length){
32561             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32562         }
32563     },
32564     
32565     onTouchStart: function(e, el)
32566     {
32567 //        e.preventDefault();
32568         
32569         this.touchmoved = false;
32570         
32571         if(!this.isFitContainer){
32572             return;
32573         }
32574         
32575         if(!this.bgimage.length || !this.html.length){
32576             return;
32577         }
32578         
32579         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32580         
32581         this.timer = new Date().getTime();
32582         
32583     },
32584     
32585     onTouchMove: function(e, el)
32586     {
32587         this.touchmoved = true;
32588     },
32589     
32590     onContextMenu : function(e,el)
32591     {
32592         e.preventDefault();
32593         e.stopPropagation();
32594         return false;
32595     },
32596     
32597     onTouchEnd: function(e, el)
32598     {
32599 //        e.preventDefault();
32600         
32601         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32602         
32603             this.leave(e,el);
32604             
32605             return;
32606         }
32607         
32608         if(!this.bgimage.length || !this.html.length){
32609             
32610             if(this.href.length){
32611                 window.location.href = this.href;
32612             }
32613             
32614             return;
32615         }
32616         
32617         if(!this.isFitContainer){
32618             return;
32619         }
32620         
32621         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32622         
32623         window.location.href = this.href;
32624     },
32625     
32626     //selection on single brick only
32627     selectBrick : function() {
32628         
32629         if (!this.parentId) {
32630             return;
32631         }
32632         
32633         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32634         var index = m.selectedBrick.indexOf(this.id);
32635         
32636         if ( index > -1) {
32637             m.selectedBrick.splice(index,1);
32638             this.el.removeClass(this.activeClass);
32639             return;
32640         }
32641         
32642         for(var i = 0; i < m.selectedBrick.length; i++) {
32643             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32644             b.el.removeClass(b.activeClass);
32645         }
32646         
32647         m.selectedBrick = [];
32648         
32649         m.selectedBrick.push(this.id);
32650         this.el.addClass(this.activeClass);
32651         return;
32652     }
32653     
32654 });
32655
32656 Roo.apply(Roo.bootstrap.MasonryBrick, {
32657     
32658     //groups: {},
32659     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32660      /**
32661     * register a Masonry Brick
32662     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32663     */
32664     
32665     register : function(brick)
32666     {
32667         //this.groups[brick.id] = brick;
32668         this.groups.add(brick.id, brick);
32669     },
32670     /**
32671     * fetch a  masonry brick based on the masonry brick ID
32672     * @param {string} the masonry brick to add
32673     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32674     */
32675     
32676     get: function(brick_id) 
32677     {
32678         // if (typeof(this.groups[brick_id]) == 'undefined') {
32679         //     return false;
32680         // }
32681         // return this.groups[brick_id] ;
32682         
32683         if(this.groups.key(brick_id)) {
32684             return this.groups.key(brick_id);
32685         }
32686         
32687         return false;
32688     }
32689     
32690     
32691     
32692 });
32693
32694  /*
32695  * - LGPL
32696  *
32697  * element
32698  * 
32699  */
32700
32701 /**
32702  * @class Roo.bootstrap.Brick
32703  * @extends Roo.bootstrap.Component
32704  * Bootstrap Brick class
32705  * 
32706  * @constructor
32707  * Create a new Brick
32708  * @param {Object} config The config object
32709  */
32710
32711 Roo.bootstrap.Brick = function(config){
32712     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32713     
32714     this.addEvents({
32715         // raw events
32716         /**
32717          * @event click
32718          * When a Brick is click
32719          * @param {Roo.bootstrap.Brick} this
32720          * @param {Roo.EventObject} e
32721          */
32722         "click" : true
32723     });
32724 };
32725
32726 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32727     
32728     /**
32729      * @cfg {String} title
32730      */   
32731     title : '',
32732     /**
32733      * @cfg {String} html
32734      */   
32735     html : '',
32736     /**
32737      * @cfg {String} bgimage
32738      */   
32739     bgimage : '',
32740     /**
32741      * @cfg {String} cls
32742      */   
32743     cls : '',
32744     /**
32745      * @cfg {String} href
32746      */   
32747     href : '',
32748     /**
32749      * @cfg {String} video
32750      */   
32751     video : '',
32752     /**
32753      * @cfg {Boolean} square
32754      */   
32755     square : true,
32756     
32757     getAutoCreate : function()
32758     {
32759         var cls = 'roo-brick';
32760         
32761         if(this.href.length){
32762             cls += ' roo-brick-link';
32763         }
32764         
32765         if(this.bgimage.length){
32766             cls += ' roo-brick-image';
32767         }
32768         
32769         if(!this.html.length && !this.bgimage.length){
32770             cls += ' roo-brick-center-title';
32771         }
32772         
32773         if(!this.html.length && this.bgimage.length){
32774             cls += ' roo-brick-bottom-title';
32775         }
32776         
32777         if(this.cls){
32778             cls += ' ' + this.cls;
32779         }
32780         
32781         var cfg = {
32782             tag: (this.href.length) ? 'a' : 'div',
32783             cls: cls,
32784             cn: [
32785                 {
32786                     tag: 'div',
32787                     cls: 'roo-brick-paragraph',
32788                     cn: []
32789                 }
32790             ]
32791         };
32792         
32793         if(this.href.length){
32794             cfg.href = this.href;
32795         }
32796         
32797         var cn = cfg.cn[0].cn;
32798         
32799         if(this.title.length){
32800             cn.push({
32801                 tag: 'h4',
32802                 cls: 'roo-brick-title',
32803                 html: this.title
32804             });
32805         }
32806         
32807         if(this.html.length){
32808             cn.push({
32809                 tag: 'p',
32810                 cls: 'roo-brick-text',
32811                 html: this.html
32812             });
32813         } else {
32814             cn.cls += ' hide';
32815         }
32816         
32817         if(this.bgimage.length){
32818             cfg.cn.push({
32819                 tag: 'img',
32820                 cls: 'roo-brick-image-view',
32821                 src: this.bgimage
32822             });
32823         }
32824         
32825         return cfg;
32826     },
32827     
32828     initEvents: function() 
32829     {
32830         if(this.title.length || this.html.length){
32831             this.el.on('mouseenter'  ,this.enter, this);
32832             this.el.on('mouseleave', this.leave, this);
32833         }
32834         
32835         Roo.EventManager.onWindowResize(this.resize, this); 
32836         
32837         if(this.bgimage.length){
32838             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32839             this.imageEl.on('load', this.onImageLoad, this);
32840             return;
32841         }
32842         
32843         this.resize();
32844     },
32845     
32846     onImageLoad : function()
32847     {
32848         this.resize();
32849     },
32850     
32851     resize : function()
32852     {
32853         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32854         
32855         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32856         
32857         if(this.bgimage.length){
32858             var image = this.el.select('.roo-brick-image-view', true).first();
32859             
32860             image.setWidth(paragraph.getWidth());
32861             
32862             if(this.square){
32863                 image.setHeight(paragraph.getWidth());
32864             }
32865             
32866             this.el.setHeight(image.getHeight());
32867             paragraph.setHeight(image.getHeight());
32868             
32869         }
32870         
32871     },
32872     
32873     enter: function(e, el)
32874     {
32875         e.preventDefault();
32876         
32877         if(this.bgimage.length){
32878             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32879             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32880         }
32881     },
32882     
32883     leave: function(e, el)
32884     {
32885         e.preventDefault();
32886         
32887         if(this.bgimage.length){
32888             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32889             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32890         }
32891     }
32892     
32893 });
32894
32895  
32896
32897  /*
32898  * - LGPL
32899  *
32900  * Input
32901  * 
32902  */
32903
32904 /**
32905  * @class Roo.bootstrap.NumberField
32906  * @extends Roo.bootstrap.Input
32907  * Bootstrap NumberField class
32908  * 
32909  * 
32910  * 
32911  * 
32912  * @constructor
32913  * Create a new NumberField
32914  * @param {Object} config The config object
32915  */
32916
32917 Roo.bootstrap.NumberField = function(config){
32918     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32919 };
32920
32921 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32922     
32923     /**
32924      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32925      */
32926     allowDecimals : true,
32927     /**
32928      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32929      */
32930     decimalSeparator : ".",
32931     /**
32932      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32933      */
32934     decimalPrecision : 2,
32935     /**
32936      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32937      */
32938     allowNegative : true,
32939     /**
32940      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32941      */
32942     minValue : Number.NEGATIVE_INFINITY,
32943     /**
32944      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32945      */
32946     maxValue : Number.MAX_VALUE,
32947     /**
32948      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32949      */
32950     minText : "The minimum value for this field is {0}",
32951     /**
32952      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32953      */
32954     maxText : "The maximum value for this field is {0}",
32955     /**
32956      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32957      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32958      */
32959     nanText : "{0} is not a valid number",
32960     /**
32961      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32962      */
32963     castInt : true,
32964
32965     // private
32966     initEvents : function()
32967     {   
32968         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32969         
32970         var allowed = "0123456789";
32971         
32972         if(this.allowDecimals){
32973             allowed += this.decimalSeparator;
32974         }
32975         
32976         if(this.allowNegative){
32977             allowed += "-";
32978         }
32979         
32980         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32981         
32982         var keyPress = function(e){
32983             
32984             var k = e.getKey();
32985             
32986             var c = e.getCharCode();
32987             
32988             if(
32989                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32990                     allowed.indexOf(String.fromCharCode(c)) === -1
32991             ){
32992                 e.stopEvent();
32993                 return;
32994             }
32995             
32996             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32997                 return;
32998             }
32999             
33000             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33001                 e.stopEvent();
33002             }
33003         };
33004         
33005         this.el.on("keypress", keyPress, this);
33006     },
33007     
33008     validateValue : function(value)
33009     {
33010         
33011         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33012             return false;
33013         }
33014         
33015         var num = this.parseValue(value);
33016         
33017         if(isNaN(num)){
33018             this.markInvalid(String.format(this.nanText, value));
33019             return false;
33020         }
33021         
33022         if(num < this.minValue){
33023             this.markInvalid(String.format(this.minText, this.minValue));
33024             return false;
33025         }
33026         
33027         if(num > this.maxValue){
33028             this.markInvalid(String.format(this.maxText, this.maxValue));
33029             return false;
33030         }
33031         
33032         return true;
33033     },
33034
33035     getValue : function()
33036     {
33037         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33038     },
33039
33040     parseValue : function(value)
33041     {
33042         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33043         return isNaN(value) ? '' : value;
33044     },
33045
33046     fixPrecision : function(value)
33047     {
33048         var nan = isNaN(value);
33049         
33050         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33051             return nan ? '' : value;
33052         }
33053         return parseFloat(value).toFixed(this.decimalPrecision);
33054     },
33055
33056     setValue : function(v)
33057     {
33058         v = this.fixPrecision(v);
33059         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33060     },
33061
33062     decimalPrecisionFcn : function(v)
33063     {
33064         return Math.floor(v);
33065     },
33066
33067     beforeBlur : function()
33068     {
33069         if(!this.castInt){
33070             return;
33071         }
33072         
33073         var v = this.parseValue(this.getRawValue());
33074         if(v){
33075             this.setValue(v);
33076         }
33077     }
33078     
33079 });
33080
33081  
33082
33083 /*
33084 * Licence: LGPL
33085 */
33086
33087 /**
33088  * @class Roo.bootstrap.DocumentSlider
33089  * @extends Roo.bootstrap.Component
33090  * Bootstrap DocumentSlider class
33091  * 
33092  * @constructor
33093  * Create a new DocumentViewer
33094  * @param {Object} config The config object
33095  */
33096
33097 Roo.bootstrap.DocumentSlider = function(config){
33098     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33099     
33100     this.files = [];
33101     
33102     this.addEvents({
33103         /**
33104          * @event initial
33105          * Fire after initEvent
33106          * @param {Roo.bootstrap.DocumentSlider} this
33107          */
33108         "initial" : true,
33109         /**
33110          * @event update
33111          * Fire after update
33112          * @param {Roo.bootstrap.DocumentSlider} this
33113          */
33114         "update" : true,
33115         /**
33116          * @event click
33117          * Fire after click
33118          * @param {Roo.bootstrap.DocumentSlider} this
33119          */
33120         "click" : true
33121     });
33122 };
33123
33124 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33125     
33126     files : false,
33127     
33128     indicator : 0,
33129     
33130     getAutoCreate : function()
33131     {
33132         var cfg = {
33133             tag : 'div',
33134             cls : 'roo-document-slider',
33135             cn : [
33136                 {
33137                     tag : 'div',
33138                     cls : 'roo-document-slider-header',
33139                     cn : [
33140                         {
33141                             tag : 'div',
33142                             cls : 'roo-document-slider-header-title'
33143                         }
33144                     ]
33145                 },
33146                 {
33147                     tag : 'div',
33148                     cls : 'roo-document-slider-body',
33149                     cn : [
33150                         {
33151                             tag : 'div',
33152                             cls : 'roo-document-slider-prev',
33153                             cn : [
33154                                 {
33155                                     tag : 'i',
33156                                     cls : 'fa fa-chevron-left'
33157                                 }
33158                             ]
33159                         },
33160                         {
33161                             tag : 'div',
33162                             cls : 'roo-document-slider-thumb',
33163                             cn : [
33164                                 {
33165                                     tag : 'img',
33166                                     cls : 'roo-document-slider-image'
33167                                 }
33168                             ]
33169                         },
33170                         {
33171                             tag : 'div',
33172                             cls : 'roo-document-slider-next',
33173                             cn : [
33174                                 {
33175                                     tag : 'i',
33176                                     cls : 'fa fa-chevron-right'
33177                                 }
33178                             ]
33179                         }
33180                     ]
33181                 }
33182             ]
33183         };
33184         
33185         return cfg;
33186     },
33187     
33188     initEvents : function()
33189     {
33190         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33191         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33192         
33193         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33194         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33195         
33196         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33197         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33198         
33199         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33200         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33201         
33202         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33203         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33204         
33205         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33206         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33207         
33208         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33209         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33210         
33211         this.thumbEl.on('click', this.onClick, this);
33212         
33213         this.prevIndicator.on('click', this.prev, this);
33214         
33215         this.nextIndicator.on('click', this.next, this);
33216         
33217     },
33218     
33219     initial : function()
33220     {
33221         if(this.files.length){
33222             this.indicator = 1;
33223             this.update()
33224         }
33225         
33226         this.fireEvent('initial', this);
33227     },
33228     
33229     update : function()
33230     {
33231         this.imageEl.attr('src', this.files[this.indicator - 1]);
33232         
33233         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33234         
33235         this.prevIndicator.show();
33236         
33237         if(this.indicator == 1){
33238             this.prevIndicator.hide();
33239         }
33240         
33241         this.nextIndicator.show();
33242         
33243         if(this.indicator == this.files.length){
33244             this.nextIndicator.hide();
33245         }
33246         
33247         this.thumbEl.scrollTo('top');
33248         
33249         this.fireEvent('update', this);
33250     },
33251     
33252     onClick : function(e)
33253     {
33254         e.preventDefault();
33255         
33256         this.fireEvent('click', this);
33257     },
33258     
33259     prev : function(e)
33260     {
33261         e.preventDefault();
33262         
33263         this.indicator = Math.max(1, this.indicator - 1);
33264         
33265         this.update();
33266     },
33267     
33268     next : function(e)
33269     {
33270         e.preventDefault();
33271         
33272         this.indicator = Math.min(this.files.length, this.indicator + 1);
33273         
33274         this.update();
33275     }
33276 });
33277 /*
33278  * - LGPL
33279  *
33280  * RadioSet
33281  *
33282  *
33283  */
33284
33285 /**
33286  * @class Roo.bootstrap.RadioSet
33287  * @extends Roo.bootstrap.Input
33288  * Bootstrap RadioSet class
33289  * @cfg {String} indicatorpos (left|right) default left
33290  * @cfg {Boolean} inline (true|false) inline the element (default true)
33291  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33292  * @constructor
33293  * Create a new RadioSet
33294  * @param {Object} config The config object
33295  */
33296
33297 Roo.bootstrap.RadioSet = function(config){
33298     
33299     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33300     
33301     this.radioes = [];
33302     
33303     Roo.bootstrap.RadioSet.register(this);
33304     
33305     this.addEvents({
33306         /**
33307         * @event check
33308         * Fires when the element is checked or unchecked.
33309         * @param {Roo.bootstrap.RadioSet} this This radio
33310         * @param {Roo.bootstrap.Radio} item The checked item
33311         */
33312        check : true
33313     });
33314     
33315 };
33316
33317 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33318
33319     radioes : false,
33320     
33321     inline : true,
33322     
33323     weight : '',
33324     
33325     indicatorpos : 'left',
33326     
33327     getAutoCreate : function()
33328     {
33329         var label = {
33330             tag : 'label',
33331             cls : 'roo-radio-set-label',
33332             cn : [
33333                 {
33334                     tag : 'span',
33335                     html : this.fieldLabel
33336                 }
33337             ]
33338         };
33339         
33340         if(this.indicatorpos == 'left'){
33341             label.cn.unshift({
33342                 tag : 'i',
33343                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33344                 tooltip : 'This field is required'
33345             });
33346         } else {
33347             label.cn.push({
33348                 tag : 'i',
33349                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33350                 tooltip : 'This field is required'
33351             });
33352         }
33353         
33354         var items = {
33355             tag : 'div',
33356             cls : 'roo-radio-set-items'
33357         };
33358         
33359         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33360         
33361         if (align === 'left' && this.fieldLabel.length) {
33362             
33363             items = {
33364                 cls : "roo-radio-set-right", 
33365                 cn: [
33366                     items
33367                 ]
33368             };
33369             
33370             if(this.labelWidth > 12){
33371                 label.style = "width: " + this.labelWidth + 'px';
33372             }
33373             
33374             if(this.labelWidth < 13 && this.labelmd == 0){
33375                 this.labelmd = this.labelWidth;
33376             }
33377             
33378             if(this.labellg > 0){
33379                 label.cls += ' col-lg-' + this.labellg;
33380                 items.cls += ' col-lg-' + (12 - this.labellg);
33381             }
33382             
33383             if(this.labelmd > 0){
33384                 label.cls += ' col-md-' + this.labelmd;
33385                 items.cls += ' col-md-' + (12 - this.labelmd);
33386             }
33387             
33388             if(this.labelsm > 0){
33389                 label.cls += ' col-sm-' + this.labelsm;
33390                 items.cls += ' col-sm-' + (12 - this.labelsm);
33391             }
33392             
33393             if(this.labelxs > 0){
33394                 label.cls += ' col-xs-' + this.labelxs;
33395                 items.cls += ' col-xs-' + (12 - this.labelxs);
33396             }
33397         }
33398         
33399         var cfg = {
33400             tag : 'div',
33401             cls : 'roo-radio-set',
33402             cn : [
33403                 {
33404                     tag : 'input',
33405                     cls : 'roo-radio-set-input',
33406                     type : 'hidden',
33407                     name : this.name,
33408                     value : this.value ? this.value :  ''
33409                 },
33410                 label,
33411                 items
33412             ]
33413         };
33414         
33415         if(this.weight.length){
33416             cfg.cls += ' roo-radio-' + this.weight;
33417         }
33418         
33419         if(this.inline) {
33420             cfg.cls += ' roo-radio-set-inline';
33421         }
33422         
33423         var settings=this;
33424         ['xs','sm','md','lg'].map(function(size){
33425             if (settings[size]) {
33426                 cfg.cls += ' col-' + size + '-' + settings[size];
33427             }
33428         });
33429         
33430         return cfg;
33431         
33432     },
33433
33434     initEvents : function()
33435     {
33436         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33437         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33438         
33439         if(!this.fieldLabel.length){
33440             this.labelEl.hide();
33441         }
33442         
33443         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33444         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33445         
33446         this.indicatorEl().addClass('invisible');
33447         
33448         this.originalValue = this.getValue();
33449         
33450     },
33451     
33452     inputEl: function ()
33453     {
33454         return this.el.select('.roo-radio-set-input', true).first();
33455     },
33456     
33457     getChildContainer : function()
33458     {
33459         return this.itemsEl;
33460     },
33461     
33462     register : function(item)
33463     {
33464         this.radioes.push(item);
33465         
33466     },
33467     
33468     validate : function()
33469     {   
33470         var valid = false;
33471         
33472         Roo.each(this.radioes, function(i){
33473             if(!i.checked){
33474                 return;
33475             }
33476             
33477             valid = true;
33478             return false;
33479         });
33480         
33481         if(this.allowBlank) {
33482             return true;
33483         }
33484         
33485         if(this.disabled || valid){
33486             this.markValid();
33487             return true;
33488         }
33489         
33490         this.markInvalid();
33491         return false;
33492         
33493     },
33494     
33495     markValid : function()
33496     {
33497         if(this.labelEl.isVisible(true)){
33498             this.indicatorEl().removeClass('visible');
33499             this.indicatorEl().addClass('invisible');
33500         }
33501         
33502         this.el.removeClass([this.invalidClass, this.validClass]);
33503         this.el.addClass(this.validClass);
33504         
33505         this.fireEvent('valid', this);
33506     },
33507     
33508     markInvalid : function(msg)
33509     {
33510         if(this.allowBlank || this.disabled){
33511             return;
33512         }
33513         
33514         if(this.labelEl.isVisible(true)){
33515             this.indicatorEl().removeClass('invisible');
33516             this.indicatorEl().addClass('visible');
33517         }
33518         
33519         this.el.removeClass([this.invalidClass, this.validClass]);
33520         this.el.addClass(this.invalidClass);
33521         
33522         this.fireEvent('invalid', this, msg);
33523         
33524     },
33525     
33526     setValue : function(v, suppressEvent)
33527     {   
33528         if(this.value === v){
33529             return;
33530         }
33531         
33532         this.value = v;
33533         
33534         if(this.rendered){
33535             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33536         }
33537         
33538         Roo.each(this.radioes, function(i){
33539             
33540             i.checked = false;
33541             i.el.removeClass('checked');
33542             
33543             if(i.value === v || i.value.toString() === v.toString()){
33544                 i.checked = true;
33545                 i.el.addClass('checked');
33546                 
33547                 if(suppressEvent !== true){
33548                     this.fireEvent('check', this, i);
33549                 }
33550             }
33551             
33552         }, this);
33553         
33554         this.validate();
33555     },
33556     
33557     clearInvalid : function(){
33558         
33559         if(!this.el || this.preventMark){
33560             return;
33561         }
33562         
33563         this.el.removeClass([this.invalidClass]);
33564         
33565         this.fireEvent('valid', this);
33566     }
33567     
33568 });
33569
33570 Roo.apply(Roo.bootstrap.RadioSet, {
33571     
33572     groups: {},
33573     
33574     register : function(set)
33575     {
33576         this.groups[set.name] = set;
33577     },
33578     
33579     get: function(name) 
33580     {
33581         if (typeof(this.groups[name]) == 'undefined') {
33582             return false;
33583         }
33584         
33585         return this.groups[name] ;
33586     }
33587     
33588 });
33589 /*
33590  * Based on:
33591  * Ext JS Library 1.1.1
33592  * Copyright(c) 2006-2007, Ext JS, LLC.
33593  *
33594  * Originally Released Under LGPL - original licence link has changed is not relivant.
33595  *
33596  * Fork - LGPL
33597  * <script type="text/javascript">
33598  */
33599
33600
33601 /**
33602  * @class Roo.bootstrap.SplitBar
33603  * @extends Roo.util.Observable
33604  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33605  * <br><br>
33606  * Usage:
33607  * <pre><code>
33608 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33609                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33610 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33611 split.minSize = 100;
33612 split.maxSize = 600;
33613 split.animate = true;
33614 split.on('moved', splitterMoved);
33615 </code></pre>
33616  * @constructor
33617  * Create a new SplitBar
33618  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33619  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33620  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33621  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33622                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33623                         position of the SplitBar).
33624  */
33625 Roo.bootstrap.SplitBar = function(cfg){
33626     
33627     /** @private */
33628     
33629     //{
33630     //  dragElement : elm
33631     //  resizingElement: el,
33632         // optional..
33633     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33634     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33635         // existingProxy ???
33636     //}
33637     
33638     this.el = Roo.get(cfg.dragElement, true);
33639     this.el.dom.unselectable = "on";
33640     /** @private */
33641     this.resizingEl = Roo.get(cfg.resizingElement, true);
33642
33643     /**
33644      * @private
33645      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33647      * @type Number
33648      */
33649     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33650     
33651     /**
33652      * The minimum size of the resizing element. (Defaults to 0)
33653      * @type Number
33654      */
33655     this.minSize = 0;
33656     
33657     /**
33658      * The maximum size of the resizing element. (Defaults to 2000)
33659      * @type Number
33660      */
33661     this.maxSize = 2000;
33662     
33663     /**
33664      * Whether to animate the transition to the new size
33665      * @type Boolean
33666      */
33667     this.animate = false;
33668     
33669     /**
33670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33671      * @type Boolean
33672      */
33673     this.useShim = false;
33674     
33675     /** @private */
33676     this.shim = null;
33677     
33678     if(!cfg.existingProxy){
33679         /** @private */
33680         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33681     }else{
33682         this.proxy = Roo.get(cfg.existingProxy).dom;
33683     }
33684     /** @private */
33685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33686     
33687     /** @private */
33688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33689     
33690     /** @private */
33691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33692     
33693     /** @private */
33694     this.dragSpecs = {};
33695     
33696     /**
33697      * @private The adapter to use to positon and resize elements
33698      */
33699     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33700     this.adapter.init(this);
33701     
33702     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33703         /** @private */
33704         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33705         this.el.addClass("roo-splitbar-h");
33706     }else{
33707         /** @private */
33708         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33709         this.el.addClass("roo-splitbar-v");
33710     }
33711     
33712     this.addEvents({
33713         /**
33714          * @event resize
33715          * Fires when the splitter is moved (alias for {@link #event-moved})
33716          * @param {Roo.bootstrap.SplitBar} this
33717          * @param {Number} newSize the new width or height
33718          */
33719         "resize" : true,
33720         /**
33721          * @event moved
33722          * Fires when the splitter is moved
33723          * @param {Roo.bootstrap.SplitBar} this
33724          * @param {Number} newSize the new width or height
33725          */
33726         "moved" : true,
33727         /**
33728          * @event beforeresize
33729          * Fires before the splitter is dragged
33730          * @param {Roo.bootstrap.SplitBar} this
33731          */
33732         "beforeresize" : true,
33733
33734         "beforeapply" : true
33735     });
33736
33737     Roo.util.Observable.call(this);
33738 };
33739
33740 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33741     onStartProxyDrag : function(x, y){
33742         this.fireEvent("beforeresize", this);
33743         if(!this.overlay){
33744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33745             o.unselectable();
33746             o.enableDisplayMode("block");
33747             // all splitbars share the same overlay
33748             Roo.bootstrap.SplitBar.prototype.overlay = o;
33749         }
33750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33751         this.overlay.show();
33752         Roo.get(this.proxy).setDisplayed("block");
33753         var size = this.adapter.getElementSize(this);
33754         this.activeMinSize = this.getMinimumSize();;
33755         this.activeMaxSize = this.getMaximumSize();;
33756         var c1 = size - this.activeMinSize;
33757         var c2 = Math.max(this.activeMaxSize - size, 0);
33758         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33759             this.dd.resetConstraints();
33760             this.dd.setXConstraint(
33761                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33762                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33763             );
33764             this.dd.setYConstraint(0, 0);
33765         }else{
33766             this.dd.resetConstraints();
33767             this.dd.setXConstraint(0, 0);
33768             this.dd.setYConstraint(
33769                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33770                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33771             );
33772          }
33773         this.dragSpecs.startSize = size;
33774         this.dragSpecs.startPoint = [x, y];
33775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33776     },
33777     
33778     /** 
33779      * @private Called after the drag operation by the DDProxy
33780      */
33781     onEndProxyDrag : function(e){
33782         Roo.get(this.proxy).setDisplayed(false);
33783         var endPoint = Roo.lib.Event.getXY(e);
33784         if(this.overlay){
33785             this.overlay.hide();
33786         }
33787         var newSize;
33788         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33789             newSize = this.dragSpecs.startSize + 
33790                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33791                     endPoint[0] - this.dragSpecs.startPoint[0] :
33792                     this.dragSpecs.startPoint[0] - endPoint[0]
33793                 );
33794         }else{
33795             newSize = this.dragSpecs.startSize + 
33796                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33797                     endPoint[1] - this.dragSpecs.startPoint[1] :
33798                     this.dragSpecs.startPoint[1] - endPoint[1]
33799                 );
33800         }
33801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33802         if(newSize != this.dragSpecs.startSize){
33803             if(this.fireEvent('beforeapply', this, newSize) !== false){
33804                 this.adapter.setElementSize(this, newSize);
33805                 this.fireEvent("moved", this, newSize);
33806                 this.fireEvent("resize", this, newSize);
33807             }
33808         }
33809     },
33810     
33811     /**
33812      * Get the adapter this SplitBar uses
33813      * @return The adapter object
33814      */
33815     getAdapter : function(){
33816         return this.adapter;
33817     },
33818     
33819     /**
33820      * Set the adapter this SplitBar uses
33821      * @param {Object} adapter A SplitBar adapter object
33822      */
33823     setAdapter : function(adapter){
33824         this.adapter = adapter;
33825         this.adapter.init(this);
33826     },
33827     
33828     /**
33829      * Gets the minimum size for the resizing element
33830      * @return {Number} The minimum size
33831      */
33832     getMinimumSize : function(){
33833         return this.minSize;
33834     },
33835     
33836     /**
33837      * Sets the minimum size for the resizing element
33838      * @param {Number} minSize The minimum size
33839      */
33840     setMinimumSize : function(minSize){
33841         this.minSize = minSize;
33842     },
33843     
33844     /**
33845      * Gets the maximum size for the resizing element
33846      * @return {Number} The maximum size
33847      */
33848     getMaximumSize : function(){
33849         return this.maxSize;
33850     },
33851     
33852     /**
33853      * Sets the maximum size for the resizing element
33854      * @param {Number} maxSize The maximum size
33855      */
33856     setMaximumSize : function(maxSize){
33857         this.maxSize = maxSize;
33858     },
33859     
33860     /**
33861      * Sets the initialize size for the resizing element
33862      * @param {Number} size The initial size
33863      */
33864     setCurrentSize : function(size){
33865         var oldAnimate = this.animate;
33866         this.animate = false;
33867         this.adapter.setElementSize(this, size);
33868         this.animate = oldAnimate;
33869     },
33870     
33871     /**
33872      * Destroy this splitbar. 
33873      * @param {Boolean} removeEl True to remove the element
33874      */
33875     destroy : function(removeEl){
33876         if(this.shim){
33877             this.shim.remove();
33878         }
33879         this.dd.unreg();
33880         this.proxy.parentNode.removeChild(this.proxy);
33881         if(removeEl){
33882             this.el.remove();
33883         }
33884     }
33885 });
33886
33887 /**
33888  * @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.
33889  */
33890 Roo.bootstrap.SplitBar.createProxy = function(dir){
33891     var proxy = new Roo.Element(document.createElement("div"));
33892     proxy.unselectable();
33893     var cls = 'roo-splitbar-proxy';
33894     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33895     document.body.appendChild(proxy.dom);
33896     return proxy.dom;
33897 };
33898
33899 /** 
33900  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33901  * Default Adapter. It assumes the splitter and resizing element are not positioned
33902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33903  */
33904 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33905 };
33906
33907 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33908     // do nothing for now
33909     init : function(s){
33910     
33911     },
33912     /**
33913      * Called before drag operations to get the current size of the resizing element. 
33914      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33915      */
33916      getElementSize : function(s){
33917         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33918             return s.resizingEl.getWidth();
33919         }else{
33920             return s.resizingEl.getHeight();
33921         }
33922     },
33923     
33924     /**
33925      * Called after drag operations to set the size of the resizing element.
33926      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33927      * @param {Number} newSize The new size to set
33928      * @param {Function} onComplete A function to be invoked when resizing is complete
33929      */
33930     setElementSize : function(s, newSize, onComplete){
33931         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33932             if(!s.animate){
33933                 s.resizingEl.setWidth(newSize);
33934                 if(onComplete){
33935                     onComplete(s, newSize);
33936                 }
33937             }else{
33938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33939             }
33940         }else{
33941             
33942             if(!s.animate){
33943                 s.resizingEl.setHeight(newSize);
33944                 if(onComplete){
33945                     onComplete(s, newSize);
33946                 }
33947             }else{
33948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33949             }
33950         }
33951     }
33952 };
33953
33954 /** 
33955  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33956  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33957  * Adapter that  moves the splitter element to align with the resized sizing element. 
33958  * Used with an absolute positioned SplitBar.
33959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33960  * document.body, make sure you assign an id to the body element.
33961  */
33962 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33963     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33964     this.container = Roo.get(container);
33965 };
33966
33967 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33968     init : function(s){
33969         this.basic.init(s);
33970     },
33971     
33972     getElementSize : function(s){
33973         return this.basic.getElementSize(s);
33974     },
33975     
33976     setElementSize : function(s, newSize, onComplete){
33977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33978     },
33979     
33980     moveSplitter : function(s){
33981         var yes = Roo.bootstrap.SplitBar;
33982         switch(s.placement){
33983             case yes.LEFT:
33984                 s.el.setX(s.resizingEl.getRight());
33985                 break;
33986             case yes.RIGHT:
33987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33988                 break;
33989             case yes.TOP:
33990                 s.el.setY(s.resizingEl.getBottom());
33991                 break;
33992             case yes.BOTTOM:
33993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33994                 break;
33995         }
33996     }
33997 };
33998
33999 /**
34000  * Orientation constant - Create a vertical SplitBar
34001  * @static
34002  * @type Number
34003  */
34004 Roo.bootstrap.SplitBar.VERTICAL = 1;
34005
34006 /**
34007  * Orientation constant - Create a horizontal SplitBar
34008  * @static
34009  * @type Number
34010  */
34011 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34012
34013 /**
34014  * Placement constant - The resizing element is to the left of the splitter element
34015  * @static
34016  * @type Number
34017  */
34018 Roo.bootstrap.SplitBar.LEFT = 1;
34019
34020 /**
34021  * Placement constant - The resizing element is to the right of the splitter element
34022  * @static
34023  * @type Number
34024  */
34025 Roo.bootstrap.SplitBar.RIGHT = 2;
34026
34027 /**
34028  * Placement constant - The resizing element is positioned above the splitter element
34029  * @static
34030  * @type Number
34031  */
34032 Roo.bootstrap.SplitBar.TOP = 3;
34033
34034 /**
34035  * Placement constant - The resizing element is positioned under splitter element
34036  * @static
34037  * @type Number
34038  */
34039 Roo.bootstrap.SplitBar.BOTTOM = 4;
34040 Roo.namespace("Roo.bootstrap.layout");/*
34041  * Based on:
34042  * Ext JS Library 1.1.1
34043  * Copyright(c) 2006-2007, Ext JS, LLC.
34044  *
34045  * Originally Released Under LGPL - original licence link has changed is not relivant.
34046  *
34047  * Fork - LGPL
34048  * <script type="text/javascript">
34049  */
34050
34051 /**
34052  * @class Roo.bootstrap.layout.Manager
34053  * @extends Roo.bootstrap.Component
34054  * Base class for layout managers.
34055  */
34056 Roo.bootstrap.layout.Manager = function(config)
34057 {
34058     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34059
34060
34061
34062
34063
34064     /** false to disable window resize monitoring @type Boolean */
34065     this.monitorWindowResize = true;
34066     this.regions = {};
34067     this.addEvents({
34068         /**
34069          * @event layout
34070          * Fires when a layout is performed.
34071          * @param {Roo.LayoutManager} this
34072          */
34073         "layout" : true,
34074         /**
34075          * @event regionresized
34076          * Fires when the user resizes a region.
34077          * @param {Roo.LayoutRegion} region The resized region
34078          * @param {Number} newSize The new size (width for east/west, height for north/south)
34079          */
34080         "regionresized" : true,
34081         /**
34082          * @event regioncollapsed
34083          * Fires when a region is collapsed.
34084          * @param {Roo.LayoutRegion} region The collapsed region
34085          */
34086         "regioncollapsed" : true,
34087         /**
34088          * @event regionexpanded
34089          * Fires when a region is expanded.
34090          * @param {Roo.LayoutRegion} region The expanded region
34091          */
34092         "regionexpanded" : true
34093     });
34094     this.updating = false;
34095
34096     if (config.el) {
34097         this.el = Roo.get(config.el);
34098         this.initEvents();
34099     }
34100
34101 };
34102
34103 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34104
34105
34106     regions : null,
34107
34108     monitorWindowResize : true,
34109
34110
34111     updating : false,
34112
34113
34114     onRender : function(ct, position)
34115     {
34116         if(!this.el){
34117             this.el = Roo.get(ct);
34118             this.initEvents();
34119         }
34120         //this.fireEvent('render',this);
34121     },
34122
34123
34124     initEvents: function()
34125     {
34126
34127
34128         // ie scrollbar fix
34129         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34130             document.body.scroll = "no";
34131         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34132             this.el.position('relative');
34133         }
34134         this.id = this.el.id;
34135         this.el.addClass("roo-layout-container");
34136         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34137         if(this.el.dom != document.body ) {
34138             this.el.on('resize', this.layout,this);
34139             this.el.on('show', this.layout,this);
34140         }
34141
34142     },
34143
34144     /**
34145      * Returns true if this layout is currently being updated
34146      * @return {Boolean}
34147      */
34148     isUpdating : function(){
34149         return this.updating;
34150     },
34151
34152     /**
34153      * Suspend the LayoutManager from doing auto-layouts while
34154      * making multiple add or remove calls
34155      */
34156     beginUpdate : function(){
34157         this.updating = true;
34158     },
34159
34160     /**
34161      * Restore auto-layouts and optionally disable the manager from performing a layout
34162      * @param {Boolean} noLayout true to disable a layout update
34163      */
34164     endUpdate : function(noLayout){
34165         this.updating = false;
34166         if(!noLayout){
34167             this.layout();
34168         }
34169     },
34170
34171     layout: function(){
34172         // abstract...
34173     },
34174
34175     onRegionResized : function(region, newSize){
34176         this.fireEvent("regionresized", region, newSize);
34177         this.layout();
34178     },
34179
34180     onRegionCollapsed : function(region){
34181         this.fireEvent("regioncollapsed", region);
34182     },
34183
34184     onRegionExpanded : function(region){
34185         this.fireEvent("regionexpanded", region);
34186     },
34187
34188     /**
34189      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34190      * performs box-model adjustments.
34191      * @return {Object} The size as an object {width: (the width), height: (the height)}
34192      */
34193     getViewSize : function()
34194     {
34195         var size;
34196         if(this.el.dom != document.body){
34197             size = this.el.getSize();
34198         }else{
34199             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34200         }
34201         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34202         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34203         return size;
34204     },
34205
34206     /**
34207      * Returns the Element this layout is bound to.
34208      * @return {Roo.Element}
34209      */
34210     getEl : function(){
34211         return this.el;
34212     },
34213
34214     /**
34215      * Returns the specified region.
34216      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34217      * @return {Roo.LayoutRegion}
34218      */
34219     getRegion : function(target){
34220         return this.regions[target.toLowerCase()];
34221     },
34222
34223     onWindowResize : function(){
34224         if(this.monitorWindowResize){
34225             this.layout();
34226         }
34227     }
34228 });
34229 /*
34230  * Based on:
34231  * Ext JS Library 1.1.1
34232  * Copyright(c) 2006-2007, Ext JS, LLC.
34233  *
34234  * Originally Released Under LGPL - original licence link has changed is not relivant.
34235  *
34236  * Fork - LGPL
34237  * <script type="text/javascript">
34238  */
34239 /**
34240  * @class Roo.bootstrap.layout.Border
34241  * @extends Roo.bootstrap.layout.Manager
34242  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34243  * please see: examples/bootstrap/nested.html<br><br>
34244  
34245 <b>The container the layout is rendered into can be either the body element or any other element.
34246 If it is not the body element, the container needs to either be an absolute positioned element,
34247 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34248 the container size if it is not the body element.</b>
34249
34250 * @constructor
34251 * Create a new Border
34252 * @param {Object} config Configuration options
34253  */
34254 Roo.bootstrap.layout.Border = function(config){
34255     config = config || {};
34256     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34257     
34258     
34259     
34260     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34261         if(config[region]){
34262             config[region].region = region;
34263             this.addRegion(config[region]);
34264         }
34265     },this);
34266     
34267 };
34268
34269 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34270
34271 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34272     /**
34273      * Creates and adds a new region if it doesn't already exist.
34274      * @param {String} target The target region key (north, south, east, west or center).
34275      * @param {Object} config The regions config object
34276      * @return {BorderLayoutRegion} The new region
34277      */
34278     addRegion : function(config)
34279     {
34280         if(!this.regions[config.region]){
34281             var r = this.factory(config);
34282             this.bindRegion(r);
34283         }
34284         return this.regions[config.region];
34285     },
34286
34287     // private (kinda)
34288     bindRegion : function(r){
34289         this.regions[r.config.region] = r;
34290         
34291         r.on("visibilitychange",    this.layout, this);
34292         r.on("paneladded",          this.layout, this);
34293         r.on("panelremoved",        this.layout, this);
34294         r.on("invalidated",         this.layout, this);
34295         r.on("resized",             this.onRegionResized, this);
34296         r.on("collapsed",           this.onRegionCollapsed, this);
34297         r.on("expanded",            this.onRegionExpanded, this);
34298     },
34299
34300     /**
34301      * Performs a layout update.
34302      */
34303     layout : function()
34304     {
34305         if(this.updating) {
34306             return;
34307         }
34308         
34309         // render all the rebions if they have not been done alreayd?
34310         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34311             if(this.regions[region] && !this.regions[region].bodyEl){
34312                 this.regions[region].onRender(this.el)
34313             }
34314         },this);
34315         
34316         var size = this.getViewSize();
34317         var w = size.width;
34318         var h = size.height;
34319         var centerW = w;
34320         var centerH = h;
34321         var centerY = 0;
34322         var centerX = 0;
34323         //var x = 0, y = 0;
34324
34325         var rs = this.regions;
34326         var north = rs["north"];
34327         var south = rs["south"]; 
34328         var west = rs["west"];
34329         var east = rs["east"];
34330         var center = rs["center"];
34331         //if(this.hideOnLayout){ // not supported anymore
34332             //c.el.setStyle("display", "none");
34333         //}
34334         if(north && north.isVisible()){
34335             var b = north.getBox();
34336             var m = north.getMargins();
34337             b.width = w - (m.left+m.right);
34338             b.x = m.left;
34339             b.y = m.top;
34340             centerY = b.height + b.y + m.bottom;
34341             centerH -= centerY;
34342             north.updateBox(this.safeBox(b));
34343         }
34344         if(south && south.isVisible()){
34345             var b = south.getBox();
34346             var m = south.getMargins();
34347             b.width = w - (m.left+m.right);
34348             b.x = m.left;
34349             var totalHeight = (b.height + m.top + m.bottom);
34350             b.y = h - totalHeight + m.top;
34351             centerH -= totalHeight;
34352             south.updateBox(this.safeBox(b));
34353         }
34354         if(west && west.isVisible()){
34355             var b = west.getBox();
34356             var m = west.getMargins();
34357             b.height = centerH - (m.top+m.bottom);
34358             b.x = m.left;
34359             b.y = centerY + m.top;
34360             var totalWidth = (b.width + m.left + m.right);
34361             centerX += totalWidth;
34362             centerW -= totalWidth;
34363             west.updateBox(this.safeBox(b));
34364         }
34365         if(east && east.isVisible()){
34366             var b = east.getBox();
34367             var m = east.getMargins();
34368             b.height = centerH - (m.top+m.bottom);
34369             var totalWidth = (b.width + m.left + m.right);
34370             b.x = w - totalWidth + m.left;
34371             b.y = centerY + m.top;
34372             centerW -= totalWidth;
34373             east.updateBox(this.safeBox(b));
34374         }
34375         if(center){
34376             var m = center.getMargins();
34377             var centerBox = {
34378                 x: centerX + m.left,
34379                 y: centerY + m.top,
34380                 width: centerW - (m.left+m.right),
34381                 height: centerH - (m.top+m.bottom)
34382             };
34383             //if(this.hideOnLayout){
34384                 //center.el.setStyle("display", "block");
34385             //}
34386             center.updateBox(this.safeBox(centerBox));
34387         }
34388         this.el.repaint();
34389         this.fireEvent("layout", this);
34390     },
34391
34392     // private
34393     safeBox : function(box){
34394         box.width = Math.max(0, box.width);
34395         box.height = Math.max(0, box.height);
34396         return box;
34397     },
34398
34399     /**
34400      * Adds a ContentPanel (or subclass) to this layout.
34401      * @param {String} target The target region key (north, south, east, west or center).
34402      * @param {Roo.ContentPanel} panel The panel to add
34403      * @return {Roo.ContentPanel} The added panel
34404      */
34405     add : function(target, panel){
34406          
34407         target = target.toLowerCase();
34408         return this.regions[target].add(panel);
34409     },
34410
34411     /**
34412      * Remove a ContentPanel (or subclass) to this layout.
34413      * @param {String} target The target region key (north, south, east, west or center).
34414      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34415      * @return {Roo.ContentPanel} The removed panel
34416      */
34417     remove : function(target, panel){
34418         target = target.toLowerCase();
34419         return this.regions[target].remove(panel);
34420     },
34421
34422     /**
34423      * Searches all regions for a panel with the specified id
34424      * @param {String} panelId
34425      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34426      */
34427     findPanel : function(panelId){
34428         var rs = this.regions;
34429         for(var target in rs){
34430             if(typeof rs[target] != "function"){
34431                 var p = rs[target].getPanel(panelId);
34432                 if(p){
34433                     return p;
34434                 }
34435             }
34436         }
34437         return null;
34438     },
34439
34440     /**
34441      * Searches all regions for a panel with the specified id and activates (shows) it.
34442      * @param {String/ContentPanel} panelId The panels id or the panel itself
34443      * @return {Roo.ContentPanel} The shown panel or null
34444      */
34445     showPanel : function(panelId) {
34446       var rs = this.regions;
34447       for(var target in rs){
34448          var r = rs[target];
34449          if(typeof r != "function"){
34450             if(r.hasPanel(panelId)){
34451                return r.showPanel(panelId);
34452             }
34453          }
34454       }
34455       return null;
34456    },
34457
34458    /**
34459      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34460      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34461      */
34462    /*
34463     restoreState : function(provider){
34464         if(!provider){
34465             provider = Roo.state.Manager;
34466         }
34467         var sm = new Roo.LayoutStateManager();
34468         sm.init(this, provider);
34469     },
34470 */
34471  
34472  
34473     /**
34474      * Adds a xtype elements to the layout.
34475      * <pre><code>
34476
34477 layout.addxtype({
34478        xtype : 'ContentPanel',
34479        region: 'west',
34480        items: [ .... ]
34481    }
34482 );
34483
34484 layout.addxtype({
34485         xtype : 'NestedLayoutPanel',
34486         region: 'west',
34487         layout: {
34488            center: { },
34489            west: { }   
34490         },
34491         items : [ ... list of content panels or nested layout panels.. ]
34492    }
34493 );
34494 </code></pre>
34495      * @param {Object} cfg Xtype definition of item to add.
34496      */
34497     addxtype : function(cfg)
34498     {
34499         // basically accepts a pannel...
34500         // can accept a layout region..!?!?
34501         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34502         
34503         
34504         // theory?  children can only be panels??
34505         
34506         //if (!cfg.xtype.match(/Panel$/)) {
34507         //    return false;
34508         //}
34509         var ret = false;
34510         
34511         if (typeof(cfg.region) == 'undefined') {
34512             Roo.log("Failed to add Panel, region was not set");
34513             Roo.log(cfg);
34514             return false;
34515         }
34516         var region = cfg.region;
34517         delete cfg.region;
34518         
34519           
34520         var xitems = [];
34521         if (cfg.items) {
34522             xitems = cfg.items;
34523             delete cfg.items;
34524         }
34525         var nb = false;
34526         
34527         switch(cfg.xtype) 
34528         {
34529             case 'Content':  // ContentPanel (el, cfg)
34530             case 'Scroll':  // ContentPanel (el, cfg)
34531             case 'View': 
34532                 cfg.autoCreate = true;
34533                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34534                 //} else {
34535                 //    var el = this.el.createChild();
34536                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34537                 //}
34538                 
34539                 this.add(region, ret);
34540                 break;
34541             
34542             /*
34543             case 'TreePanel': // our new panel!
34544                 cfg.el = this.el.createChild();
34545                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34546                 this.add(region, ret);
34547                 break;
34548             */
34549             
34550             case 'Nest': 
34551                 // create a new Layout (which is  a Border Layout...
34552                 
34553                 var clayout = cfg.layout;
34554                 clayout.el  = this.el.createChild();
34555                 clayout.items   = clayout.items  || [];
34556                 
34557                 delete cfg.layout;
34558                 
34559                 // replace this exitems with the clayout ones..
34560                 xitems = clayout.items;
34561                  
34562                 // force background off if it's in center...
34563                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34564                     cfg.background = false;
34565                 }
34566                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34567                 
34568                 
34569                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34570                 //console.log('adding nested layout panel '  + cfg.toSource());
34571                 this.add(region, ret);
34572                 nb = {}; /// find first...
34573                 break;
34574             
34575             case 'Grid':
34576                 
34577                 // needs grid and region
34578                 
34579                 //var el = this.getRegion(region).el.createChild();
34580                 /*
34581                  *var el = this.el.createChild();
34582                 // create the grid first...
34583                 cfg.grid.container = el;
34584                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34585                 */
34586                 
34587                 if (region == 'center' && this.active ) {
34588                     cfg.background = false;
34589                 }
34590                 
34591                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34592                 
34593                 this.add(region, ret);
34594                 /*
34595                 if (cfg.background) {
34596                     // render grid on panel activation (if panel background)
34597                     ret.on('activate', function(gp) {
34598                         if (!gp.grid.rendered) {
34599                     //        gp.grid.render(el);
34600                         }
34601                     });
34602                 } else {
34603                   //  cfg.grid.render(el);
34604                 }
34605                 */
34606                 break;
34607            
34608            
34609             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34610                 // it was the old xcomponent building that caused this before.
34611                 // espeically if border is the top element in the tree.
34612                 ret = this;
34613                 break; 
34614                 
34615                     
34616                 
34617                 
34618                 
34619             default:
34620                 /*
34621                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34622                     
34623                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34624                     this.add(region, ret);
34625                 } else {
34626                 */
34627                     Roo.log(cfg);
34628                     throw "Can not add '" + cfg.xtype + "' to Border";
34629                     return null;
34630              
34631                                 
34632              
34633         }
34634         this.beginUpdate();
34635         // add children..
34636         var region = '';
34637         var abn = {};
34638         Roo.each(xitems, function(i)  {
34639             region = nb && i.region ? i.region : false;
34640             
34641             var add = ret.addxtype(i);
34642            
34643             if (region) {
34644                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34645                 if (!i.background) {
34646                     abn[region] = nb[region] ;
34647                 }
34648             }
34649             
34650         });
34651         this.endUpdate();
34652
34653         // make the last non-background panel active..
34654         //if (nb) { Roo.log(abn); }
34655         if (nb) {
34656             
34657             for(var r in abn) {
34658                 region = this.getRegion(r);
34659                 if (region) {
34660                     // tried using nb[r], but it does not work..
34661                      
34662                     region.showPanel(abn[r]);
34663                    
34664                 }
34665             }
34666         }
34667         return ret;
34668         
34669     },
34670     
34671     
34672 // private
34673     factory : function(cfg)
34674     {
34675         
34676         var validRegions = Roo.bootstrap.layout.Border.regions;
34677
34678         var target = cfg.region;
34679         cfg.mgr = this;
34680         
34681         var r = Roo.bootstrap.layout;
34682         Roo.log(target);
34683         switch(target){
34684             case "north":
34685                 return new r.North(cfg);
34686             case "south":
34687                 return new r.South(cfg);
34688             case "east":
34689                 return new r.East(cfg);
34690             case "west":
34691                 return new r.West(cfg);
34692             case "center":
34693                 return new r.Center(cfg);
34694         }
34695         throw 'Layout region "'+target+'" not supported.';
34696     }
34697     
34698     
34699 });
34700  /*
34701  * Based on:
34702  * Ext JS Library 1.1.1
34703  * Copyright(c) 2006-2007, Ext JS, LLC.
34704  *
34705  * Originally Released Under LGPL - original licence link has changed is not relivant.
34706  *
34707  * Fork - LGPL
34708  * <script type="text/javascript">
34709  */
34710  
34711 /**
34712  * @class Roo.bootstrap.layout.Basic
34713  * @extends Roo.util.Observable
34714  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34715  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34716  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34717  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34718  * @cfg {string}   region  the region that it inhabits..
34719  * @cfg {bool}   skipConfig skip config?
34720  * 
34721
34722  */
34723 Roo.bootstrap.layout.Basic = function(config){
34724     
34725     this.mgr = config.mgr;
34726     
34727     this.position = config.region;
34728     
34729     var skipConfig = config.skipConfig;
34730     
34731     this.events = {
34732         /**
34733          * @scope Roo.BasicLayoutRegion
34734          */
34735         
34736         /**
34737          * @event beforeremove
34738          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34739          * @param {Roo.LayoutRegion} this
34740          * @param {Roo.ContentPanel} panel The panel
34741          * @param {Object} e The cancel event object
34742          */
34743         "beforeremove" : true,
34744         /**
34745          * @event invalidated
34746          * Fires when the layout for this region is changed.
34747          * @param {Roo.LayoutRegion} this
34748          */
34749         "invalidated" : true,
34750         /**
34751          * @event visibilitychange
34752          * Fires when this region is shown or hidden 
34753          * @param {Roo.LayoutRegion} this
34754          * @param {Boolean} visibility true or false
34755          */
34756         "visibilitychange" : true,
34757         /**
34758          * @event paneladded
34759          * Fires when a panel is added. 
34760          * @param {Roo.LayoutRegion} this
34761          * @param {Roo.ContentPanel} panel The panel
34762          */
34763         "paneladded" : true,
34764         /**
34765          * @event panelremoved
34766          * Fires when a panel is removed. 
34767          * @param {Roo.LayoutRegion} this
34768          * @param {Roo.ContentPanel} panel The panel
34769          */
34770         "panelremoved" : true,
34771         /**
34772          * @event beforecollapse
34773          * Fires when this region before collapse.
34774          * @param {Roo.LayoutRegion} this
34775          */
34776         "beforecollapse" : true,
34777         /**
34778          * @event collapsed
34779          * Fires when this region is collapsed.
34780          * @param {Roo.LayoutRegion} this
34781          */
34782         "collapsed" : true,
34783         /**
34784          * @event expanded
34785          * Fires when this region is expanded.
34786          * @param {Roo.LayoutRegion} this
34787          */
34788         "expanded" : true,
34789         /**
34790          * @event slideshow
34791          * Fires when this region is slid into view.
34792          * @param {Roo.LayoutRegion} this
34793          */
34794         "slideshow" : true,
34795         /**
34796          * @event slidehide
34797          * Fires when this region slides out of view. 
34798          * @param {Roo.LayoutRegion} this
34799          */
34800         "slidehide" : true,
34801         /**
34802          * @event panelactivated
34803          * Fires when a panel is activated. 
34804          * @param {Roo.LayoutRegion} this
34805          * @param {Roo.ContentPanel} panel The activated panel
34806          */
34807         "panelactivated" : true,
34808         /**
34809          * @event resized
34810          * Fires when the user resizes this region. 
34811          * @param {Roo.LayoutRegion} this
34812          * @param {Number} newSize The new size (width for east/west, height for north/south)
34813          */
34814         "resized" : true
34815     };
34816     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34817     this.panels = new Roo.util.MixedCollection();
34818     this.panels.getKey = this.getPanelId.createDelegate(this);
34819     this.box = null;
34820     this.activePanel = null;
34821     // ensure listeners are added...
34822     
34823     if (config.listeners || config.events) {
34824         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34825             listeners : config.listeners || {},
34826             events : config.events || {}
34827         });
34828     }
34829     
34830     if(skipConfig !== true){
34831         this.applyConfig(config);
34832     }
34833 };
34834
34835 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34836 {
34837     getPanelId : function(p){
34838         return p.getId();
34839     },
34840     
34841     applyConfig : function(config){
34842         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34843         this.config = config;
34844         
34845     },
34846     
34847     /**
34848      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34849      * the width, for horizontal (north, south) the height.
34850      * @param {Number} newSize The new width or height
34851      */
34852     resizeTo : function(newSize){
34853         var el = this.el ? this.el :
34854                  (this.activePanel ? this.activePanel.getEl() : null);
34855         if(el){
34856             switch(this.position){
34857                 case "east":
34858                 case "west":
34859                     el.setWidth(newSize);
34860                     this.fireEvent("resized", this, newSize);
34861                 break;
34862                 case "north":
34863                 case "south":
34864                     el.setHeight(newSize);
34865                     this.fireEvent("resized", this, newSize);
34866                 break;                
34867             }
34868         }
34869     },
34870     
34871     getBox : function(){
34872         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34873     },
34874     
34875     getMargins : function(){
34876         return this.margins;
34877     },
34878     
34879     updateBox : function(box){
34880         this.box = box;
34881         var el = this.activePanel.getEl();
34882         el.dom.style.left = box.x + "px";
34883         el.dom.style.top = box.y + "px";
34884         this.activePanel.setSize(box.width, box.height);
34885     },
34886     
34887     /**
34888      * Returns the container element for this region.
34889      * @return {Roo.Element}
34890      */
34891     getEl : function(){
34892         return this.activePanel;
34893     },
34894     
34895     /**
34896      * Returns true if this region is currently visible.
34897      * @return {Boolean}
34898      */
34899     isVisible : function(){
34900         return this.activePanel ? true : false;
34901     },
34902     
34903     setActivePanel : function(panel){
34904         panel = this.getPanel(panel);
34905         if(this.activePanel && this.activePanel != panel){
34906             this.activePanel.setActiveState(false);
34907             this.activePanel.getEl().setLeftTop(-10000,-10000);
34908         }
34909         this.activePanel = panel;
34910         panel.setActiveState(true);
34911         if(this.box){
34912             panel.setSize(this.box.width, this.box.height);
34913         }
34914         this.fireEvent("panelactivated", this, panel);
34915         this.fireEvent("invalidated");
34916     },
34917     
34918     /**
34919      * Show the specified panel.
34920      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34921      * @return {Roo.ContentPanel} The shown panel or null
34922      */
34923     showPanel : function(panel){
34924         panel = this.getPanel(panel);
34925         if(panel){
34926             this.setActivePanel(panel);
34927         }
34928         return panel;
34929     },
34930     
34931     /**
34932      * Get the active panel for this region.
34933      * @return {Roo.ContentPanel} The active panel or null
34934      */
34935     getActivePanel : function(){
34936         return this.activePanel;
34937     },
34938     
34939     /**
34940      * Add the passed ContentPanel(s)
34941      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34942      * @return {Roo.ContentPanel} The panel added (if only one was added)
34943      */
34944     add : function(panel){
34945         if(arguments.length > 1){
34946             for(var i = 0, len = arguments.length; i < len; i++) {
34947                 this.add(arguments[i]);
34948             }
34949             return null;
34950         }
34951         if(this.hasPanel(panel)){
34952             this.showPanel(panel);
34953             return panel;
34954         }
34955         var el = panel.getEl();
34956         if(el.dom.parentNode != this.mgr.el.dom){
34957             this.mgr.el.dom.appendChild(el.dom);
34958         }
34959         if(panel.setRegion){
34960             panel.setRegion(this);
34961         }
34962         this.panels.add(panel);
34963         el.setStyle("position", "absolute");
34964         if(!panel.background){
34965             this.setActivePanel(panel);
34966             if(this.config.initialSize && this.panels.getCount()==1){
34967                 this.resizeTo(this.config.initialSize);
34968             }
34969         }
34970         this.fireEvent("paneladded", this, panel);
34971         return panel;
34972     },
34973     
34974     /**
34975      * Returns true if the panel is in this region.
34976      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34977      * @return {Boolean}
34978      */
34979     hasPanel : function(panel){
34980         if(typeof panel == "object"){ // must be panel obj
34981             panel = panel.getId();
34982         }
34983         return this.getPanel(panel) ? true : false;
34984     },
34985     
34986     /**
34987      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34988      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34989      * @param {Boolean} preservePanel Overrides the config preservePanel option
34990      * @return {Roo.ContentPanel} The panel that was removed
34991      */
34992     remove : function(panel, preservePanel){
34993         panel = this.getPanel(panel);
34994         if(!panel){
34995             return null;
34996         }
34997         var e = {};
34998         this.fireEvent("beforeremove", this, panel, e);
34999         if(e.cancel === true){
35000             return null;
35001         }
35002         var panelId = panel.getId();
35003         this.panels.removeKey(panelId);
35004         return panel;
35005     },
35006     
35007     /**
35008      * Returns the panel specified or null if it's not in this region.
35009      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35010      * @return {Roo.ContentPanel}
35011      */
35012     getPanel : function(id){
35013         if(typeof id == "object"){ // must be panel obj
35014             return id;
35015         }
35016         return this.panels.get(id);
35017     },
35018     
35019     /**
35020      * Returns this regions position (north/south/east/west/center).
35021      * @return {String} 
35022      */
35023     getPosition: function(){
35024         return this.position;    
35025     }
35026 });/*
35027  * Based on:
35028  * Ext JS Library 1.1.1
35029  * Copyright(c) 2006-2007, Ext JS, LLC.
35030  *
35031  * Originally Released Under LGPL - original licence link has changed is not relivant.
35032  *
35033  * Fork - LGPL
35034  * <script type="text/javascript">
35035  */
35036  
35037 /**
35038  * @class Roo.bootstrap.layout.Region
35039  * @extends Roo.bootstrap.layout.Basic
35040  * This class represents a region in a layout manager.
35041  
35042  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35043  * @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})
35044  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35045  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35046  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35047  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35048  * @cfg {String}    title           The title for the region (overrides panel titles)
35049  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35050  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35051  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35052  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35053  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35054  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35055  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35056  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35057  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35058  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35059
35060  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35061  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35062  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35063  * @cfg {Number}    width           For East/West panels
35064  * @cfg {Number}    height          For North/South panels
35065  * @cfg {Boolean}   split           To show the splitter
35066  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35067  * 
35068  * @cfg {string}   cls             Extra CSS classes to add to region
35069  * 
35070  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35071  * @cfg {string}   region  the region that it inhabits..
35072  *
35073
35074  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35075  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35076
35077  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35078  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35079  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35080  */
35081 Roo.bootstrap.layout.Region = function(config)
35082 {
35083     this.applyConfig(config);
35084
35085     var mgr = config.mgr;
35086     var pos = config.region;
35087     config.skipConfig = true;
35088     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35089     
35090     if (mgr.el) {
35091         this.onRender(mgr.el);   
35092     }
35093      
35094     this.visible = true;
35095     this.collapsed = false;
35096     this.unrendered_panels = [];
35097 };
35098
35099 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35100
35101     position: '', // set by wrapper (eg. north/south etc..)
35102     unrendered_panels : null,  // unrendered panels.
35103     createBody : function(){
35104         /** This region's body element 
35105         * @type Roo.Element */
35106         this.bodyEl = this.el.createChild({
35107                 tag: "div",
35108                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35109         });
35110     },
35111
35112     onRender: function(ctr, pos)
35113     {
35114         var dh = Roo.DomHelper;
35115         /** This region's container element 
35116         * @type Roo.Element */
35117         this.el = dh.append(ctr.dom, {
35118                 tag: "div",
35119                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35120             }, true);
35121         /** This region's title element 
35122         * @type Roo.Element */
35123     
35124         this.titleEl = dh.append(this.el.dom,
35125             {
35126                     tag: "div",
35127                     unselectable: "on",
35128                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35129                     children:[
35130                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35131                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35132                     ]}, true);
35133         
35134         this.titleEl.enableDisplayMode();
35135         /** This region's title text element 
35136         * @type HTMLElement */
35137         this.titleTextEl = this.titleEl.dom.firstChild;
35138         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35139         /*
35140         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35141         this.closeBtn.enableDisplayMode();
35142         this.closeBtn.on("click", this.closeClicked, this);
35143         this.closeBtn.hide();
35144     */
35145         this.createBody(this.config);
35146         if(this.config.hideWhenEmpty){
35147             this.hide();
35148             this.on("paneladded", this.validateVisibility, this);
35149             this.on("panelremoved", this.validateVisibility, this);
35150         }
35151         if(this.autoScroll){
35152             this.bodyEl.setStyle("overflow", "auto");
35153         }else{
35154             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35155         }
35156         //if(c.titlebar !== false){
35157             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35158                 this.titleEl.hide();
35159             }else{
35160                 this.titleEl.show();
35161                 if(this.config.title){
35162                     this.titleTextEl.innerHTML = this.config.title;
35163                 }
35164             }
35165         //}
35166         if(this.config.collapsed){
35167             this.collapse(true);
35168         }
35169         if(this.config.hidden){
35170             this.hide();
35171         }
35172         
35173         if (this.unrendered_panels && this.unrendered_panels.length) {
35174             for (var i =0;i< this.unrendered_panels.length; i++) {
35175                 this.add(this.unrendered_panels[i]);
35176             }
35177             this.unrendered_panels = null;
35178             
35179         }
35180         
35181     },
35182     
35183     applyConfig : function(c)
35184     {
35185         /*
35186          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35187             var dh = Roo.DomHelper;
35188             if(c.titlebar !== false){
35189                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35190                 this.collapseBtn.on("click", this.collapse, this);
35191                 this.collapseBtn.enableDisplayMode();
35192                 /*
35193                 if(c.showPin === true || this.showPin){
35194                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35195                     this.stickBtn.enableDisplayMode();
35196                     this.stickBtn.on("click", this.expand, this);
35197                     this.stickBtn.hide();
35198                 }
35199                 
35200             }
35201             */
35202             /** This region's collapsed element
35203             * @type Roo.Element */
35204             /*
35205              *
35206             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35207                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35208             ]}, true);
35209             
35210             if(c.floatable !== false){
35211                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35212                this.collapsedEl.on("click", this.collapseClick, this);
35213             }
35214
35215             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35216                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35217                    id: "message", unselectable: "on", style:{"float":"left"}});
35218                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35219              }
35220             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35221             this.expandBtn.on("click", this.expand, this);
35222             
35223         }
35224         
35225         if(this.collapseBtn){
35226             this.collapseBtn.setVisible(c.collapsible == true);
35227         }
35228         
35229         this.cmargins = c.cmargins || this.cmargins ||
35230                          (this.position == "west" || this.position == "east" ?
35231                              {top: 0, left: 2, right:2, bottom: 0} :
35232                              {top: 2, left: 0, right:0, bottom: 2});
35233         */
35234         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35235         
35236         
35237         this.bottomTabs = c.tabPosition != "top";
35238         
35239         this.autoScroll = c.autoScroll || false;
35240         
35241         
35242        
35243         
35244         this.duration = c.duration || .30;
35245         this.slideDuration = c.slideDuration || .45;
35246         this.config = c;
35247        
35248     },
35249     /**
35250      * Returns true if this region is currently visible.
35251      * @return {Boolean}
35252      */
35253     isVisible : function(){
35254         return this.visible;
35255     },
35256
35257     /**
35258      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35259      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35260      */
35261     //setCollapsedTitle : function(title){
35262     //    title = title || "&#160;";
35263      //   if(this.collapsedTitleTextEl){
35264       //      this.collapsedTitleTextEl.innerHTML = title;
35265        // }
35266     //},
35267
35268     getBox : function(){
35269         var b;
35270       //  if(!this.collapsed){
35271             b = this.el.getBox(false, true);
35272        // }else{
35273           //  b = this.collapsedEl.getBox(false, true);
35274         //}
35275         return b;
35276     },
35277
35278     getMargins : function(){
35279         return this.margins;
35280         //return this.collapsed ? this.cmargins : this.margins;
35281     },
35282 /*
35283     highlight : function(){
35284         this.el.addClass("x-layout-panel-dragover");
35285     },
35286
35287     unhighlight : function(){
35288         this.el.removeClass("x-layout-panel-dragover");
35289     },
35290 */
35291     updateBox : function(box)
35292     {
35293         if (!this.bodyEl) {
35294             return; // not rendered yet..
35295         }
35296         
35297         this.box = box;
35298         if(!this.collapsed){
35299             this.el.dom.style.left = box.x + "px";
35300             this.el.dom.style.top = box.y + "px";
35301             this.updateBody(box.width, box.height);
35302         }else{
35303             this.collapsedEl.dom.style.left = box.x + "px";
35304             this.collapsedEl.dom.style.top = box.y + "px";
35305             this.collapsedEl.setSize(box.width, box.height);
35306         }
35307         if(this.tabs){
35308             this.tabs.autoSizeTabs();
35309         }
35310     },
35311
35312     updateBody : function(w, h)
35313     {
35314         if(w !== null){
35315             this.el.setWidth(w);
35316             w -= this.el.getBorderWidth("rl");
35317             if(this.config.adjustments){
35318                 w += this.config.adjustments[0];
35319             }
35320         }
35321         if(h !== null && h > 0){
35322             this.el.setHeight(h);
35323             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35324             h -= this.el.getBorderWidth("tb");
35325             if(this.config.adjustments){
35326                 h += this.config.adjustments[1];
35327             }
35328             this.bodyEl.setHeight(h);
35329             if(this.tabs){
35330                 h = this.tabs.syncHeight(h);
35331             }
35332         }
35333         if(this.panelSize){
35334             w = w !== null ? w : this.panelSize.width;
35335             h = h !== null ? h : this.panelSize.height;
35336         }
35337         if(this.activePanel){
35338             var el = this.activePanel.getEl();
35339             w = w !== null ? w : el.getWidth();
35340             h = h !== null ? h : el.getHeight();
35341             this.panelSize = {width: w, height: h};
35342             this.activePanel.setSize(w, h);
35343         }
35344         if(Roo.isIE && this.tabs){
35345             this.tabs.el.repaint();
35346         }
35347     },
35348
35349     /**
35350      * Returns the container element for this region.
35351      * @return {Roo.Element}
35352      */
35353     getEl : function(){
35354         return this.el;
35355     },
35356
35357     /**
35358      * Hides this region.
35359      */
35360     hide : function(){
35361         //if(!this.collapsed){
35362             this.el.dom.style.left = "-2000px";
35363             this.el.hide();
35364         //}else{
35365          //   this.collapsedEl.dom.style.left = "-2000px";
35366          //   this.collapsedEl.hide();
35367        // }
35368         this.visible = false;
35369         this.fireEvent("visibilitychange", this, false);
35370     },
35371
35372     /**
35373      * Shows this region if it was previously hidden.
35374      */
35375     show : function(){
35376         //if(!this.collapsed){
35377             this.el.show();
35378         //}else{
35379         //    this.collapsedEl.show();
35380        // }
35381         this.visible = true;
35382         this.fireEvent("visibilitychange", this, true);
35383     },
35384 /*
35385     closeClicked : function(){
35386         if(this.activePanel){
35387             this.remove(this.activePanel);
35388         }
35389     },
35390
35391     collapseClick : function(e){
35392         if(this.isSlid){
35393            e.stopPropagation();
35394            this.slideIn();
35395         }else{
35396            e.stopPropagation();
35397            this.slideOut();
35398         }
35399     },
35400 */
35401     /**
35402      * Collapses this region.
35403      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35404      */
35405     /*
35406     collapse : function(skipAnim, skipCheck = false){
35407         if(this.collapsed) {
35408             return;
35409         }
35410         
35411         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35412             
35413             this.collapsed = true;
35414             if(this.split){
35415                 this.split.el.hide();
35416             }
35417             if(this.config.animate && skipAnim !== true){
35418                 this.fireEvent("invalidated", this);
35419                 this.animateCollapse();
35420             }else{
35421                 this.el.setLocation(-20000,-20000);
35422                 this.el.hide();
35423                 this.collapsedEl.show();
35424                 this.fireEvent("collapsed", this);
35425                 this.fireEvent("invalidated", this);
35426             }
35427         }
35428         
35429     },
35430 */
35431     animateCollapse : function(){
35432         // overridden
35433     },
35434
35435     /**
35436      * Expands this region if it was previously collapsed.
35437      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35438      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35439      */
35440     /*
35441     expand : function(e, skipAnim){
35442         if(e) {
35443             e.stopPropagation();
35444         }
35445         if(!this.collapsed || this.el.hasActiveFx()) {
35446             return;
35447         }
35448         if(this.isSlid){
35449             this.afterSlideIn();
35450             skipAnim = true;
35451         }
35452         this.collapsed = false;
35453         if(this.config.animate && skipAnim !== true){
35454             this.animateExpand();
35455         }else{
35456             this.el.show();
35457             if(this.split){
35458                 this.split.el.show();
35459             }
35460             this.collapsedEl.setLocation(-2000,-2000);
35461             this.collapsedEl.hide();
35462             this.fireEvent("invalidated", this);
35463             this.fireEvent("expanded", this);
35464         }
35465     },
35466 */
35467     animateExpand : function(){
35468         // overridden
35469     },
35470
35471     initTabs : function()
35472     {
35473         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35474         
35475         var ts = new Roo.bootstrap.panel.Tabs({
35476                 el: this.bodyEl.dom,
35477                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35478                 disableTooltips: this.config.disableTabTips,
35479                 toolbar : this.config.toolbar
35480             });
35481         
35482         if(this.config.hideTabs){
35483             ts.stripWrap.setDisplayed(false);
35484         }
35485         this.tabs = ts;
35486         ts.resizeTabs = this.config.resizeTabs === true;
35487         ts.minTabWidth = this.config.minTabWidth || 40;
35488         ts.maxTabWidth = this.config.maxTabWidth || 250;
35489         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35490         ts.monitorResize = false;
35491         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35492         ts.bodyEl.addClass('roo-layout-tabs-body');
35493         this.panels.each(this.initPanelAsTab, this);
35494     },
35495
35496     initPanelAsTab : function(panel){
35497         var ti = this.tabs.addTab(
35498             panel.getEl().id,
35499             panel.getTitle(),
35500             null,
35501             this.config.closeOnTab && panel.isClosable(),
35502             panel.tpl
35503         );
35504         if(panel.tabTip !== undefined){
35505             ti.setTooltip(panel.tabTip);
35506         }
35507         ti.on("activate", function(){
35508               this.setActivePanel(panel);
35509         }, this);
35510         
35511         if(this.config.closeOnTab){
35512             ti.on("beforeclose", function(t, e){
35513                 e.cancel = true;
35514                 this.remove(panel);
35515             }, this);
35516         }
35517         
35518         panel.tabItem = ti;
35519         
35520         return ti;
35521     },
35522
35523     updatePanelTitle : function(panel, title)
35524     {
35525         if(this.activePanel == panel){
35526             this.updateTitle(title);
35527         }
35528         if(this.tabs){
35529             var ti = this.tabs.getTab(panel.getEl().id);
35530             ti.setText(title);
35531             if(panel.tabTip !== undefined){
35532                 ti.setTooltip(panel.tabTip);
35533             }
35534         }
35535     },
35536
35537     updateTitle : function(title){
35538         if(this.titleTextEl && !this.config.title){
35539             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35540         }
35541     },
35542
35543     setActivePanel : function(panel)
35544     {
35545         panel = this.getPanel(panel);
35546         if(this.activePanel && this.activePanel != panel){
35547             if(this.activePanel.setActiveState(false) === false){
35548                 return;
35549             }
35550         }
35551         this.activePanel = panel;
35552         panel.setActiveState(true);
35553         if(this.panelSize){
35554             panel.setSize(this.panelSize.width, this.panelSize.height);
35555         }
35556         if(this.closeBtn){
35557             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35558         }
35559         this.updateTitle(panel.getTitle());
35560         if(this.tabs){
35561             this.fireEvent("invalidated", this);
35562         }
35563         this.fireEvent("panelactivated", this, panel);
35564     },
35565
35566     /**
35567      * Shows the specified panel.
35568      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35569      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35570      */
35571     showPanel : function(panel)
35572     {
35573         panel = this.getPanel(panel);
35574         if(panel){
35575             if(this.tabs){
35576                 var tab = this.tabs.getTab(panel.getEl().id);
35577                 if(tab.isHidden()){
35578                     this.tabs.unhideTab(tab.id);
35579                 }
35580                 tab.activate();
35581             }else{
35582                 this.setActivePanel(panel);
35583             }
35584         }
35585         return panel;
35586     },
35587
35588     /**
35589      * Get the active panel for this region.
35590      * @return {Roo.ContentPanel} The active panel or null
35591      */
35592     getActivePanel : function(){
35593         return this.activePanel;
35594     },
35595
35596     validateVisibility : function(){
35597         if(this.panels.getCount() < 1){
35598             this.updateTitle("&#160;");
35599             this.closeBtn.hide();
35600             this.hide();
35601         }else{
35602             if(!this.isVisible()){
35603                 this.show();
35604             }
35605         }
35606     },
35607
35608     /**
35609      * Adds the passed ContentPanel(s) to this region.
35610      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35611      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35612      */
35613     add : function(panel)
35614     {
35615         if(arguments.length > 1){
35616             for(var i = 0, len = arguments.length; i < len; i++) {
35617                 this.add(arguments[i]);
35618             }
35619             return null;
35620         }
35621         
35622         // if we have not been rendered yet, then we can not really do much of this..
35623         if (!this.bodyEl) {
35624             this.unrendered_panels.push(panel);
35625             return panel;
35626         }
35627         
35628         
35629         
35630         
35631         if(this.hasPanel(panel)){
35632             this.showPanel(panel);
35633             return panel;
35634         }
35635         panel.setRegion(this);
35636         this.panels.add(panel);
35637        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35638             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35639             // and hide them... ???
35640             this.bodyEl.dom.appendChild(panel.getEl().dom);
35641             if(panel.background !== true){
35642                 this.setActivePanel(panel);
35643             }
35644             this.fireEvent("paneladded", this, panel);
35645             return panel;
35646         }
35647         */
35648         if(!this.tabs){
35649             this.initTabs();
35650         }else{
35651             this.initPanelAsTab(panel);
35652         }
35653         
35654         
35655         if(panel.background !== true){
35656             this.tabs.activate(panel.getEl().id);
35657         }
35658         this.fireEvent("paneladded", this, panel);
35659         return panel;
35660     },
35661
35662     /**
35663      * Hides the tab for the specified panel.
35664      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35665      */
35666     hidePanel : function(panel){
35667         if(this.tabs && (panel = this.getPanel(panel))){
35668             this.tabs.hideTab(panel.getEl().id);
35669         }
35670     },
35671
35672     /**
35673      * Unhides the tab for a previously hidden panel.
35674      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35675      */
35676     unhidePanel : function(panel){
35677         if(this.tabs && (panel = this.getPanel(panel))){
35678             this.tabs.unhideTab(panel.getEl().id);
35679         }
35680     },
35681
35682     clearPanels : function(){
35683         while(this.panels.getCount() > 0){
35684              this.remove(this.panels.first());
35685         }
35686     },
35687
35688     /**
35689      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35690      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35691      * @param {Boolean} preservePanel Overrides the config preservePanel option
35692      * @return {Roo.ContentPanel} The panel that was removed
35693      */
35694     remove : function(panel, preservePanel)
35695     {
35696         panel = this.getPanel(panel);
35697         if(!panel){
35698             return null;
35699         }
35700         var e = {};
35701         this.fireEvent("beforeremove", this, panel, e);
35702         if(e.cancel === true){
35703             return null;
35704         }
35705         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35706         var panelId = panel.getId();
35707         this.panels.removeKey(panelId);
35708         if(preservePanel){
35709             document.body.appendChild(panel.getEl().dom);
35710         }
35711         if(this.tabs){
35712             this.tabs.removeTab(panel.getEl().id);
35713         }else if (!preservePanel){
35714             this.bodyEl.dom.removeChild(panel.getEl().dom);
35715         }
35716         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35717             var p = this.panels.first();
35718             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35719             tempEl.appendChild(p.getEl().dom);
35720             this.bodyEl.update("");
35721             this.bodyEl.dom.appendChild(p.getEl().dom);
35722             tempEl = null;
35723             this.updateTitle(p.getTitle());
35724             this.tabs = null;
35725             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35726             this.setActivePanel(p);
35727         }
35728         panel.setRegion(null);
35729         if(this.activePanel == panel){
35730             this.activePanel = null;
35731         }
35732         if(this.config.autoDestroy !== false && preservePanel !== true){
35733             try{panel.destroy();}catch(e){}
35734         }
35735         this.fireEvent("panelremoved", this, panel);
35736         return panel;
35737     },
35738
35739     /**
35740      * Returns the TabPanel component used by this region
35741      * @return {Roo.TabPanel}
35742      */
35743     getTabs : function(){
35744         return this.tabs;
35745     },
35746
35747     createTool : function(parentEl, className){
35748         var btn = Roo.DomHelper.append(parentEl, {
35749             tag: "div",
35750             cls: "x-layout-tools-button",
35751             children: [ {
35752                 tag: "div",
35753                 cls: "roo-layout-tools-button-inner " + className,
35754                 html: "&#160;"
35755             }]
35756         }, true);
35757         btn.addClassOnOver("roo-layout-tools-button-over");
35758         return btn;
35759     }
35760 });/*
35761  * Based on:
35762  * Ext JS Library 1.1.1
35763  * Copyright(c) 2006-2007, Ext JS, LLC.
35764  *
35765  * Originally Released Under LGPL - original licence link has changed is not relivant.
35766  *
35767  * Fork - LGPL
35768  * <script type="text/javascript">
35769  */
35770  
35771
35772
35773 /**
35774  * @class Roo.SplitLayoutRegion
35775  * @extends Roo.LayoutRegion
35776  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35777  */
35778 Roo.bootstrap.layout.Split = function(config){
35779     this.cursor = config.cursor;
35780     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35781 };
35782
35783 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35784 {
35785     splitTip : "Drag to resize.",
35786     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35787     useSplitTips : false,
35788
35789     applyConfig : function(config){
35790         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35791     },
35792     
35793     onRender : function(ctr,pos) {
35794         
35795         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35796         if(!this.config.split){
35797             return;
35798         }
35799         if(!this.split){
35800             
35801             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35802                             tag: "div",
35803                             id: this.el.id + "-split",
35804                             cls: "roo-layout-split roo-layout-split-"+this.position,
35805                             html: "&#160;"
35806             });
35807             /** The SplitBar for this region 
35808             * @type Roo.SplitBar */
35809             // does not exist yet...
35810             Roo.log([this.position, this.orientation]);
35811             
35812             this.split = new Roo.bootstrap.SplitBar({
35813                 dragElement : splitEl,
35814                 resizingElement: this.el,
35815                 orientation : this.orientation
35816             });
35817             
35818             this.split.on("moved", this.onSplitMove, this);
35819             this.split.useShim = this.config.useShim === true;
35820             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35821             if(this.useSplitTips){
35822                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35823             }
35824             //if(config.collapsible){
35825             //    this.split.el.on("dblclick", this.collapse,  this);
35826             //}
35827         }
35828         if(typeof this.config.minSize != "undefined"){
35829             this.split.minSize = this.config.minSize;
35830         }
35831         if(typeof this.config.maxSize != "undefined"){
35832             this.split.maxSize = this.config.maxSize;
35833         }
35834         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35835             this.hideSplitter();
35836         }
35837         
35838     },
35839
35840     getHMaxSize : function(){
35841          var cmax = this.config.maxSize || 10000;
35842          var center = this.mgr.getRegion("center");
35843          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35844     },
35845
35846     getVMaxSize : function(){
35847          var cmax = this.config.maxSize || 10000;
35848          var center = this.mgr.getRegion("center");
35849          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35850     },
35851
35852     onSplitMove : function(split, newSize){
35853         this.fireEvent("resized", this, newSize);
35854     },
35855     
35856     /** 
35857      * Returns the {@link Roo.SplitBar} for this region.
35858      * @return {Roo.SplitBar}
35859      */
35860     getSplitBar : function(){
35861         return this.split;
35862     },
35863     
35864     hide : function(){
35865         this.hideSplitter();
35866         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35867     },
35868
35869     hideSplitter : function(){
35870         if(this.split){
35871             this.split.el.setLocation(-2000,-2000);
35872             this.split.el.hide();
35873         }
35874     },
35875
35876     show : function(){
35877         if(this.split){
35878             this.split.el.show();
35879         }
35880         Roo.bootstrap.layout.Split.superclass.show.call(this);
35881     },
35882     
35883     beforeSlide: function(){
35884         if(Roo.isGecko){// firefox overflow auto bug workaround
35885             this.bodyEl.clip();
35886             if(this.tabs) {
35887                 this.tabs.bodyEl.clip();
35888             }
35889             if(this.activePanel){
35890                 this.activePanel.getEl().clip();
35891                 
35892                 if(this.activePanel.beforeSlide){
35893                     this.activePanel.beforeSlide();
35894                 }
35895             }
35896         }
35897     },
35898     
35899     afterSlide : function(){
35900         if(Roo.isGecko){// firefox overflow auto bug workaround
35901             this.bodyEl.unclip();
35902             if(this.tabs) {
35903                 this.tabs.bodyEl.unclip();
35904             }
35905             if(this.activePanel){
35906                 this.activePanel.getEl().unclip();
35907                 if(this.activePanel.afterSlide){
35908                     this.activePanel.afterSlide();
35909                 }
35910             }
35911         }
35912     },
35913
35914     initAutoHide : function(){
35915         if(this.autoHide !== false){
35916             if(!this.autoHideHd){
35917                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35918                 this.autoHideHd = {
35919                     "mouseout": function(e){
35920                         if(!e.within(this.el, true)){
35921                             st.delay(500);
35922                         }
35923                     },
35924                     "mouseover" : function(e){
35925                         st.cancel();
35926                     },
35927                     scope : this
35928                 };
35929             }
35930             this.el.on(this.autoHideHd);
35931         }
35932     },
35933
35934     clearAutoHide : function(){
35935         if(this.autoHide !== false){
35936             this.el.un("mouseout", this.autoHideHd.mouseout);
35937             this.el.un("mouseover", this.autoHideHd.mouseover);
35938         }
35939     },
35940
35941     clearMonitor : function(){
35942         Roo.get(document).un("click", this.slideInIf, this);
35943     },
35944
35945     // these names are backwards but not changed for compat
35946     slideOut : function(){
35947         if(this.isSlid || this.el.hasActiveFx()){
35948             return;
35949         }
35950         this.isSlid = true;
35951         if(this.collapseBtn){
35952             this.collapseBtn.hide();
35953         }
35954         this.closeBtnState = this.closeBtn.getStyle('display');
35955         this.closeBtn.hide();
35956         if(this.stickBtn){
35957             this.stickBtn.show();
35958         }
35959         this.el.show();
35960         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35961         this.beforeSlide();
35962         this.el.setStyle("z-index", 10001);
35963         this.el.slideIn(this.getSlideAnchor(), {
35964             callback: function(){
35965                 this.afterSlide();
35966                 this.initAutoHide();
35967                 Roo.get(document).on("click", this.slideInIf, this);
35968                 this.fireEvent("slideshow", this);
35969             },
35970             scope: this,
35971             block: true
35972         });
35973     },
35974
35975     afterSlideIn : function(){
35976         this.clearAutoHide();
35977         this.isSlid = false;
35978         this.clearMonitor();
35979         this.el.setStyle("z-index", "");
35980         if(this.collapseBtn){
35981             this.collapseBtn.show();
35982         }
35983         this.closeBtn.setStyle('display', this.closeBtnState);
35984         if(this.stickBtn){
35985             this.stickBtn.hide();
35986         }
35987         this.fireEvent("slidehide", this);
35988     },
35989
35990     slideIn : function(cb){
35991         if(!this.isSlid || this.el.hasActiveFx()){
35992             Roo.callback(cb);
35993             return;
35994         }
35995         this.isSlid = false;
35996         this.beforeSlide();
35997         this.el.slideOut(this.getSlideAnchor(), {
35998             callback: function(){
35999                 this.el.setLeftTop(-10000, -10000);
36000                 this.afterSlide();
36001                 this.afterSlideIn();
36002                 Roo.callback(cb);
36003             },
36004             scope: this,
36005             block: true
36006         });
36007     },
36008     
36009     slideInIf : function(e){
36010         if(!e.within(this.el)){
36011             this.slideIn();
36012         }
36013     },
36014
36015     animateCollapse : function(){
36016         this.beforeSlide();
36017         this.el.setStyle("z-index", 20000);
36018         var anchor = this.getSlideAnchor();
36019         this.el.slideOut(anchor, {
36020             callback : function(){
36021                 this.el.setStyle("z-index", "");
36022                 this.collapsedEl.slideIn(anchor, {duration:.3});
36023                 this.afterSlide();
36024                 this.el.setLocation(-10000,-10000);
36025                 this.el.hide();
36026                 this.fireEvent("collapsed", this);
36027             },
36028             scope: this,
36029             block: true
36030         });
36031     },
36032
36033     animateExpand : function(){
36034         this.beforeSlide();
36035         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36036         this.el.setStyle("z-index", 20000);
36037         this.collapsedEl.hide({
36038             duration:.1
36039         });
36040         this.el.slideIn(this.getSlideAnchor(), {
36041             callback : function(){
36042                 this.el.setStyle("z-index", "");
36043                 this.afterSlide();
36044                 if(this.split){
36045                     this.split.el.show();
36046                 }
36047                 this.fireEvent("invalidated", this);
36048                 this.fireEvent("expanded", this);
36049             },
36050             scope: this,
36051             block: true
36052         });
36053     },
36054
36055     anchors : {
36056         "west" : "left",
36057         "east" : "right",
36058         "north" : "top",
36059         "south" : "bottom"
36060     },
36061
36062     sanchors : {
36063         "west" : "l",
36064         "east" : "r",
36065         "north" : "t",
36066         "south" : "b"
36067     },
36068
36069     canchors : {
36070         "west" : "tl-tr",
36071         "east" : "tr-tl",
36072         "north" : "tl-bl",
36073         "south" : "bl-tl"
36074     },
36075
36076     getAnchor : function(){
36077         return this.anchors[this.position];
36078     },
36079
36080     getCollapseAnchor : function(){
36081         return this.canchors[this.position];
36082     },
36083
36084     getSlideAnchor : function(){
36085         return this.sanchors[this.position];
36086     },
36087
36088     getAlignAdj : function(){
36089         var cm = this.cmargins;
36090         switch(this.position){
36091             case "west":
36092                 return [0, 0];
36093             break;
36094             case "east":
36095                 return [0, 0];
36096             break;
36097             case "north":
36098                 return [0, 0];
36099             break;
36100             case "south":
36101                 return [0, 0];
36102             break;
36103         }
36104     },
36105
36106     getExpandAdj : function(){
36107         var c = this.collapsedEl, cm = this.cmargins;
36108         switch(this.position){
36109             case "west":
36110                 return [-(cm.right+c.getWidth()+cm.left), 0];
36111             break;
36112             case "east":
36113                 return [cm.right+c.getWidth()+cm.left, 0];
36114             break;
36115             case "north":
36116                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36117             break;
36118             case "south":
36119                 return [0, cm.top+cm.bottom+c.getHeight()];
36120             break;
36121         }
36122     }
36123 });/*
36124  * Based on:
36125  * Ext JS Library 1.1.1
36126  * Copyright(c) 2006-2007, Ext JS, LLC.
36127  *
36128  * Originally Released Under LGPL - original licence link has changed is not relivant.
36129  *
36130  * Fork - LGPL
36131  * <script type="text/javascript">
36132  */
36133 /*
36134  * These classes are private internal classes
36135  */
36136 Roo.bootstrap.layout.Center = function(config){
36137     config.region = "center";
36138     Roo.bootstrap.layout.Region.call(this, config);
36139     this.visible = true;
36140     this.minWidth = config.minWidth || 20;
36141     this.minHeight = config.minHeight || 20;
36142 };
36143
36144 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36145     hide : function(){
36146         // center panel can't be hidden
36147     },
36148     
36149     show : function(){
36150         // center panel can't be hidden
36151     },
36152     
36153     getMinWidth: function(){
36154         return this.minWidth;
36155     },
36156     
36157     getMinHeight: function(){
36158         return this.minHeight;
36159     }
36160 });
36161
36162
36163
36164
36165  
36166
36167
36168
36169
36170
36171 Roo.bootstrap.layout.North = function(config)
36172 {
36173     config.region = 'north';
36174     config.cursor = 'n-resize';
36175     
36176     Roo.bootstrap.layout.Split.call(this, config);
36177     
36178     
36179     if(this.split){
36180         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36181         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36182         this.split.el.addClass("roo-layout-split-v");
36183     }
36184     var size = config.initialSize || config.height;
36185     if(typeof size != "undefined"){
36186         this.el.setHeight(size);
36187     }
36188 };
36189 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36190 {
36191     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36192     
36193     
36194     
36195     getBox : function(){
36196         if(this.collapsed){
36197             return this.collapsedEl.getBox();
36198         }
36199         var box = this.el.getBox();
36200         if(this.split){
36201             box.height += this.split.el.getHeight();
36202         }
36203         return box;
36204     },
36205     
36206     updateBox : function(box){
36207         if(this.split && !this.collapsed){
36208             box.height -= this.split.el.getHeight();
36209             this.split.el.setLeft(box.x);
36210             this.split.el.setTop(box.y+box.height);
36211             this.split.el.setWidth(box.width);
36212         }
36213         if(this.collapsed){
36214             this.updateBody(box.width, null);
36215         }
36216         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36217     }
36218 });
36219
36220
36221
36222
36223
36224 Roo.bootstrap.layout.South = function(config){
36225     config.region = 'south';
36226     config.cursor = 's-resize';
36227     Roo.bootstrap.layout.Split.call(this, config);
36228     if(this.split){
36229         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36230         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36231         this.split.el.addClass("roo-layout-split-v");
36232     }
36233     var size = config.initialSize || config.height;
36234     if(typeof size != "undefined"){
36235         this.el.setHeight(size);
36236     }
36237 };
36238
36239 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36240     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36241     getBox : function(){
36242         if(this.collapsed){
36243             return this.collapsedEl.getBox();
36244         }
36245         var box = this.el.getBox();
36246         if(this.split){
36247             var sh = this.split.el.getHeight();
36248             box.height += sh;
36249             box.y -= sh;
36250         }
36251         return box;
36252     },
36253     
36254     updateBox : function(box){
36255         if(this.split && !this.collapsed){
36256             var sh = this.split.el.getHeight();
36257             box.height -= sh;
36258             box.y += sh;
36259             this.split.el.setLeft(box.x);
36260             this.split.el.setTop(box.y-sh);
36261             this.split.el.setWidth(box.width);
36262         }
36263         if(this.collapsed){
36264             this.updateBody(box.width, null);
36265         }
36266         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36267     }
36268 });
36269
36270 Roo.bootstrap.layout.East = function(config){
36271     config.region = "east";
36272     config.cursor = "e-resize";
36273     Roo.bootstrap.layout.Split.call(this, config);
36274     if(this.split){
36275         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36276         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36277         this.split.el.addClass("roo-layout-split-h");
36278     }
36279     var size = config.initialSize || config.width;
36280     if(typeof size != "undefined"){
36281         this.el.setWidth(size);
36282     }
36283 };
36284 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36285     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36286     getBox : function(){
36287         if(this.collapsed){
36288             return this.collapsedEl.getBox();
36289         }
36290         var box = this.el.getBox();
36291         if(this.split){
36292             var sw = this.split.el.getWidth();
36293             box.width += sw;
36294             box.x -= sw;
36295         }
36296         return box;
36297     },
36298
36299     updateBox : function(box){
36300         if(this.split && !this.collapsed){
36301             var sw = this.split.el.getWidth();
36302             box.width -= sw;
36303             this.split.el.setLeft(box.x);
36304             this.split.el.setTop(box.y);
36305             this.split.el.setHeight(box.height);
36306             box.x += sw;
36307         }
36308         if(this.collapsed){
36309             this.updateBody(null, box.height);
36310         }
36311         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36312     }
36313 });
36314
36315 Roo.bootstrap.layout.West = function(config){
36316     config.region = "west";
36317     config.cursor = "w-resize";
36318     
36319     Roo.bootstrap.layout.Split.call(this, config);
36320     if(this.split){
36321         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36322         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36323         this.split.el.addClass("roo-layout-split-h");
36324     }
36325     
36326 };
36327 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36328     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36329     
36330     onRender: function(ctr, pos)
36331     {
36332         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36333         var size = this.config.initialSize || this.config.width;
36334         if(typeof size != "undefined"){
36335             this.el.setWidth(size);
36336         }
36337     },
36338     
36339     getBox : function(){
36340         if(this.collapsed){
36341             return this.collapsedEl.getBox();
36342         }
36343         var box = this.el.getBox();
36344         if(this.split){
36345             box.width += this.split.el.getWidth();
36346         }
36347         return box;
36348     },
36349     
36350     updateBox : function(box){
36351         if(this.split && !this.collapsed){
36352             var sw = this.split.el.getWidth();
36353             box.width -= sw;
36354             this.split.el.setLeft(box.x+box.width);
36355             this.split.el.setTop(box.y);
36356             this.split.el.setHeight(box.height);
36357         }
36358         if(this.collapsed){
36359             this.updateBody(null, box.height);
36360         }
36361         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36362     }
36363 });
36364 Roo.namespace("Roo.bootstrap.panel");/*
36365  * Based on:
36366  * Ext JS Library 1.1.1
36367  * Copyright(c) 2006-2007, Ext JS, LLC.
36368  *
36369  * Originally Released Under LGPL - original licence link has changed is not relivant.
36370  *
36371  * Fork - LGPL
36372  * <script type="text/javascript">
36373  */
36374 /**
36375  * @class Roo.ContentPanel
36376  * @extends Roo.util.Observable
36377  * A basic ContentPanel element.
36378  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36379  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36380  * @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
36381  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36382  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36383  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36384  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36385  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36386  * @cfg {String} title          The title for this panel
36387  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36388  * @cfg {String} url            Calls {@link #setUrl} with this value
36389  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36390  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36391  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36392  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36393  * @cfg {Boolean} badges render the badges
36394
36395  * @constructor
36396  * Create a new ContentPanel.
36397  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36398  * @param {String/Object} config A string to set only the title or a config object
36399  * @param {String} content (optional) Set the HTML content for this panel
36400  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36401  */
36402 Roo.bootstrap.panel.Content = function( config){
36403     
36404     this.tpl = config.tpl || false;
36405     
36406     var el = config.el;
36407     var content = config.content;
36408
36409     if(config.autoCreate){ // xtype is available if this is called from factory
36410         el = Roo.id();
36411     }
36412     this.el = Roo.get(el);
36413     if(!this.el && config && config.autoCreate){
36414         if(typeof config.autoCreate == "object"){
36415             if(!config.autoCreate.id){
36416                 config.autoCreate.id = config.id||el;
36417             }
36418             this.el = Roo.DomHelper.append(document.body,
36419                         config.autoCreate, true);
36420         }else{
36421             var elcfg =  {   tag: "div",
36422                             cls: "roo-layout-inactive-content",
36423                             id: config.id||el
36424                             };
36425             if (config.html) {
36426                 elcfg.html = config.html;
36427                 
36428             }
36429                         
36430             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36431         }
36432     } 
36433     this.closable = false;
36434     this.loaded = false;
36435     this.active = false;
36436    
36437       
36438     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36439         
36440         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36441         
36442         this.wrapEl = this.el; //this.el.wrap();
36443         var ti = [];
36444         if (config.toolbar.items) {
36445             ti = config.toolbar.items ;
36446             delete config.toolbar.items ;
36447         }
36448         
36449         var nitems = [];
36450         this.toolbar.render(this.wrapEl, 'before');
36451         for(var i =0;i < ti.length;i++) {
36452           //  Roo.log(['add child', items[i]]);
36453             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36454         }
36455         this.toolbar.items = nitems;
36456         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36457         delete config.toolbar;
36458         
36459     }
36460     /*
36461     // xtype created footer. - not sure if will work as we normally have to render first..
36462     if (this.footer && !this.footer.el && this.footer.xtype) {
36463         if (!this.wrapEl) {
36464             this.wrapEl = this.el.wrap();
36465         }
36466     
36467         this.footer.container = this.wrapEl.createChild();
36468          
36469         this.footer = Roo.factory(this.footer, Roo);
36470         
36471     }
36472     */
36473     
36474      if(typeof config == "string"){
36475         this.title = config;
36476     }else{
36477         Roo.apply(this, config);
36478     }
36479     
36480     if(this.resizeEl){
36481         this.resizeEl = Roo.get(this.resizeEl, true);
36482     }else{
36483         this.resizeEl = this.el;
36484     }
36485     // handle view.xtype
36486     
36487  
36488     
36489     
36490     this.addEvents({
36491         /**
36492          * @event activate
36493          * Fires when this panel is activated. 
36494          * @param {Roo.ContentPanel} this
36495          */
36496         "activate" : true,
36497         /**
36498          * @event deactivate
36499          * Fires when this panel is activated. 
36500          * @param {Roo.ContentPanel} this
36501          */
36502         "deactivate" : true,
36503
36504         /**
36505          * @event resize
36506          * Fires when this panel is resized if fitToFrame is true.
36507          * @param {Roo.ContentPanel} this
36508          * @param {Number} width The width after any component adjustments
36509          * @param {Number} height The height after any component adjustments
36510          */
36511         "resize" : true,
36512         
36513          /**
36514          * @event render
36515          * Fires when this tab is created
36516          * @param {Roo.ContentPanel} this
36517          */
36518         "render" : true
36519         
36520         
36521         
36522     });
36523     
36524
36525     
36526     
36527     if(this.autoScroll){
36528         this.resizeEl.setStyle("overflow", "auto");
36529     } else {
36530         // fix randome scrolling
36531         //this.el.on('scroll', function() {
36532         //    Roo.log('fix random scolling');
36533         //    this.scrollTo('top',0); 
36534         //});
36535     }
36536     content = content || this.content;
36537     if(content){
36538         this.setContent(content);
36539     }
36540     if(config && config.url){
36541         this.setUrl(this.url, this.params, this.loadOnce);
36542     }
36543     
36544     
36545     
36546     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36547     
36548     if (this.view && typeof(this.view.xtype) != 'undefined') {
36549         this.view.el = this.el.appendChild(document.createElement("div"));
36550         this.view = Roo.factory(this.view); 
36551         this.view.render  &&  this.view.render(false, '');  
36552     }
36553     
36554     
36555     this.fireEvent('render', this);
36556 };
36557
36558 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36559     
36560     tabTip : '',
36561     
36562     setRegion : function(region){
36563         this.region = region;
36564         this.setActiveClass(region && !this.background);
36565     },
36566     
36567     
36568     setActiveClass: function(state)
36569     {
36570         if(state){
36571            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36572            this.el.setStyle('position','relative');
36573         }else{
36574            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36575            this.el.setStyle('position', 'absolute');
36576         } 
36577     },
36578     
36579     /**
36580      * Returns the toolbar for this Panel if one was configured. 
36581      * @return {Roo.Toolbar} 
36582      */
36583     getToolbar : function(){
36584         return this.toolbar;
36585     },
36586     
36587     setActiveState : function(active)
36588     {
36589         this.active = active;
36590         this.setActiveClass(active);
36591         if(!active){
36592             if(this.fireEvent("deactivate", this) === false){
36593                 return false;
36594             }
36595             return true;
36596         }
36597         this.fireEvent("activate", this);
36598         return true;
36599     },
36600     /**
36601      * Updates this panel's element
36602      * @param {String} content The new content
36603      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36604     */
36605     setContent : function(content, loadScripts){
36606         this.el.update(content, loadScripts);
36607     },
36608
36609     ignoreResize : function(w, h){
36610         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36611             return true;
36612         }else{
36613             this.lastSize = {width: w, height: h};
36614             return false;
36615         }
36616     },
36617     /**
36618      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36619      * @return {Roo.UpdateManager} The UpdateManager
36620      */
36621     getUpdateManager : function(){
36622         return this.el.getUpdateManager();
36623     },
36624      /**
36625      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36626      * @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:
36627 <pre><code>
36628 panel.load({
36629     url: "your-url.php",
36630     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36631     callback: yourFunction,
36632     scope: yourObject, //(optional scope)
36633     discardUrl: false,
36634     nocache: false,
36635     text: "Loading...",
36636     timeout: 30,
36637     scripts: false
36638 });
36639 </code></pre>
36640      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36641      * 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.
36642      * @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}
36643      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36644      * @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.
36645      * @return {Roo.ContentPanel} this
36646      */
36647     load : function(){
36648         var um = this.el.getUpdateManager();
36649         um.update.apply(um, arguments);
36650         return this;
36651     },
36652
36653
36654     /**
36655      * 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.
36656      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36657      * @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)
36658      * @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)
36659      * @return {Roo.UpdateManager} The UpdateManager
36660      */
36661     setUrl : function(url, params, loadOnce){
36662         if(this.refreshDelegate){
36663             this.removeListener("activate", this.refreshDelegate);
36664         }
36665         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36666         this.on("activate", this.refreshDelegate);
36667         return this.el.getUpdateManager();
36668     },
36669     
36670     _handleRefresh : function(url, params, loadOnce){
36671         if(!loadOnce || !this.loaded){
36672             var updater = this.el.getUpdateManager();
36673             updater.update(url, params, this._setLoaded.createDelegate(this));
36674         }
36675     },
36676     
36677     _setLoaded : function(){
36678         this.loaded = true;
36679     }, 
36680     
36681     /**
36682      * Returns this panel's id
36683      * @return {String} 
36684      */
36685     getId : function(){
36686         return this.el.id;
36687     },
36688     
36689     /** 
36690      * Returns this panel's element - used by regiosn to add.
36691      * @return {Roo.Element} 
36692      */
36693     getEl : function(){
36694         return this.wrapEl || this.el;
36695     },
36696     
36697    
36698     
36699     adjustForComponents : function(width, height)
36700     {
36701         //Roo.log('adjustForComponents ');
36702         if(this.resizeEl != this.el){
36703             width -= this.el.getFrameWidth('lr');
36704             height -= this.el.getFrameWidth('tb');
36705         }
36706         if(this.toolbar){
36707             var te = this.toolbar.getEl();
36708             te.setWidth(width);
36709             height -= te.getHeight();
36710         }
36711         if(this.footer){
36712             var te = this.footer.getEl();
36713             te.setWidth(width);
36714             height -= te.getHeight();
36715         }
36716         
36717         
36718         if(this.adjustments){
36719             width += this.adjustments[0];
36720             height += this.adjustments[1];
36721         }
36722         return {"width": width, "height": height};
36723     },
36724     
36725     setSize : function(width, height){
36726         if(this.fitToFrame && !this.ignoreResize(width, height)){
36727             if(this.fitContainer && this.resizeEl != this.el){
36728                 this.el.setSize(width, height);
36729             }
36730             var size = this.adjustForComponents(width, height);
36731             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36732             this.fireEvent('resize', this, size.width, size.height);
36733         }
36734     },
36735     
36736     /**
36737      * Returns this panel's title
36738      * @return {String} 
36739      */
36740     getTitle : function(){
36741         
36742         if (typeof(this.title) != 'object') {
36743             return this.title;
36744         }
36745         
36746         var t = '';
36747         for (var k in this.title) {
36748             if (!this.title.hasOwnProperty(k)) {
36749                 continue;
36750             }
36751             
36752             if (k.indexOf('-') >= 0) {
36753                 var s = k.split('-');
36754                 for (var i = 0; i<s.length; i++) {
36755                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36756                 }
36757             } else {
36758                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36759             }
36760         }
36761         return t;
36762     },
36763     
36764     /**
36765      * Set this panel's title
36766      * @param {String} title
36767      */
36768     setTitle : function(title){
36769         this.title = title;
36770         if(this.region){
36771             this.region.updatePanelTitle(this, title);
36772         }
36773     },
36774     
36775     /**
36776      * Returns true is this panel was configured to be closable
36777      * @return {Boolean} 
36778      */
36779     isClosable : function(){
36780         return this.closable;
36781     },
36782     
36783     beforeSlide : function(){
36784         this.el.clip();
36785         this.resizeEl.clip();
36786     },
36787     
36788     afterSlide : function(){
36789         this.el.unclip();
36790         this.resizeEl.unclip();
36791     },
36792     
36793     /**
36794      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36795      *   Will fail silently if the {@link #setUrl} method has not been called.
36796      *   This does not activate the panel, just updates its content.
36797      */
36798     refresh : function(){
36799         if(this.refreshDelegate){
36800            this.loaded = false;
36801            this.refreshDelegate();
36802         }
36803     },
36804     
36805     /**
36806      * Destroys this panel
36807      */
36808     destroy : function(){
36809         this.el.removeAllListeners();
36810         var tempEl = document.createElement("span");
36811         tempEl.appendChild(this.el.dom);
36812         tempEl.innerHTML = "";
36813         this.el.remove();
36814         this.el = null;
36815     },
36816     
36817     /**
36818      * form - if the content panel contains a form - this is a reference to it.
36819      * @type {Roo.form.Form}
36820      */
36821     form : false,
36822     /**
36823      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36824      *    This contains a reference to it.
36825      * @type {Roo.View}
36826      */
36827     view : false,
36828     
36829       /**
36830      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36831      * <pre><code>
36832
36833 layout.addxtype({
36834        xtype : 'Form',
36835        items: [ .... ]
36836    }
36837 );
36838
36839 </code></pre>
36840      * @param {Object} cfg Xtype definition of item to add.
36841      */
36842     
36843     
36844     getChildContainer: function () {
36845         return this.getEl();
36846     }
36847     
36848     
36849     /*
36850         var  ret = new Roo.factory(cfg);
36851         return ret;
36852         
36853         
36854         // add form..
36855         if (cfg.xtype.match(/^Form$/)) {
36856             
36857             var el;
36858             //if (this.footer) {
36859             //    el = this.footer.container.insertSibling(false, 'before');
36860             //} else {
36861                 el = this.el.createChild();
36862             //}
36863
36864             this.form = new  Roo.form.Form(cfg);
36865             
36866             
36867             if ( this.form.allItems.length) {
36868                 this.form.render(el.dom);
36869             }
36870             return this.form;
36871         }
36872         // should only have one of theses..
36873         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36874             // views.. should not be just added - used named prop 'view''
36875             
36876             cfg.el = this.el.appendChild(document.createElement("div"));
36877             // factory?
36878             
36879             var ret = new Roo.factory(cfg);
36880              
36881              ret.render && ret.render(false, ''); // render blank..
36882             this.view = ret;
36883             return ret;
36884         }
36885         return false;
36886     }
36887     \*/
36888 });
36889  
36890 /**
36891  * @class Roo.bootstrap.panel.Grid
36892  * @extends Roo.bootstrap.panel.Content
36893  * @constructor
36894  * Create a new GridPanel.
36895  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36896  * @param {Object} config A the config object
36897   
36898  */
36899
36900
36901
36902 Roo.bootstrap.panel.Grid = function(config)
36903 {
36904     
36905       
36906     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36907         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36908
36909     config.el = this.wrapper;
36910     //this.el = this.wrapper;
36911     
36912       if (config.container) {
36913         // ctor'ed from a Border/panel.grid
36914         
36915         
36916         this.wrapper.setStyle("overflow", "hidden");
36917         this.wrapper.addClass('roo-grid-container');
36918
36919     }
36920     
36921     
36922     if(config.toolbar){
36923         var tool_el = this.wrapper.createChild();    
36924         this.toolbar = Roo.factory(config.toolbar);
36925         var ti = [];
36926         if (config.toolbar.items) {
36927             ti = config.toolbar.items ;
36928             delete config.toolbar.items ;
36929         }
36930         
36931         var nitems = [];
36932         this.toolbar.render(tool_el);
36933         for(var i =0;i < ti.length;i++) {
36934           //  Roo.log(['add child', items[i]]);
36935             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36936         }
36937         this.toolbar.items = nitems;
36938         
36939         delete config.toolbar;
36940     }
36941     
36942     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36943     config.grid.scrollBody = true;;
36944     config.grid.monitorWindowResize = false; // turn off autosizing
36945     config.grid.autoHeight = false;
36946     config.grid.autoWidth = false;
36947     
36948     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36949     
36950     if (config.background) {
36951         // render grid on panel activation (if panel background)
36952         this.on('activate', function(gp) {
36953             if (!gp.grid.rendered) {
36954                 gp.grid.render(this.wrapper);
36955                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36956             }
36957         });
36958             
36959     } else {
36960         this.grid.render(this.wrapper);
36961         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36962
36963     }
36964     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36965     // ??? needed ??? config.el = this.wrapper;
36966     
36967     
36968     
36969   
36970     // xtype created footer. - not sure if will work as we normally have to render first..
36971     if (this.footer && !this.footer.el && this.footer.xtype) {
36972         
36973         var ctr = this.grid.getView().getFooterPanel(true);
36974         this.footer.dataSource = this.grid.dataSource;
36975         this.footer = Roo.factory(this.footer, Roo);
36976         this.footer.render(ctr);
36977         
36978     }
36979     
36980     
36981     
36982     
36983      
36984 };
36985
36986 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36987     getId : function(){
36988         return this.grid.id;
36989     },
36990     
36991     /**
36992      * Returns the grid for this panel
36993      * @return {Roo.bootstrap.Table} 
36994      */
36995     getGrid : function(){
36996         return this.grid;    
36997     },
36998     
36999     setSize : function(width, height){
37000         if(!this.ignoreResize(width, height)){
37001             var grid = this.grid;
37002             var size = this.adjustForComponents(width, height);
37003             var gridel = grid.getGridEl();
37004             gridel.setSize(size.width, size.height);
37005             /*
37006             var thd = grid.getGridEl().select('thead',true).first();
37007             var tbd = grid.getGridEl().select('tbody', true).first();
37008             if (tbd) {
37009                 tbd.setSize(width, height - thd.getHeight());
37010             }
37011             */
37012             grid.autoSize();
37013         }
37014     },
37015      
37016     
37017     
37018     beforeSlide : function(){
37019         this.grid.getView().scroller.clip();
37020     },
37021     
37022     afterSlide : function(){
37023         this.grid.getView().scroller.unclip();
37024     },
37025     
37026     destroy : function(){
37027         this.grid.destroy();
37028         delete this.grid;
37029         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37030     }
37031 });
37032
37033 /**
37034  * @class Roo.bootstrap.panel.Nest
37035  * @extends Roo.bootstrap.panel.Content
37036  * @constructor
37037  * Create a new Panel, that can contain a layout.Border.
37038  * 
37039  * 
37040  * @param {Roo.BorderLayout} layout The layout for this panel
37041  * @param {String/Object} config A string to set only the title or a config object
37042  */
37043 Roo.bootstrap.panel.Nest = function(config)
37044 {
37045     // construct with only one argument..
37046     /* FIXME - implement nicer consturctors
37047     if (layout.layout) {
37048         config = layout;
37049         layout = config.layout;
37050         delete config.layout;
37051     }
37052     if (layout.xtype && !layout.getEl) {
37053         // then layout needs constructing..
37054         layout = Roo.factory(layout, Roo);
37055     }
37056     */
37057     
37058     config.el =  config.layout.getEl();
37059     
37060     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37061     
37062     config.layout.monitorWindowResize = false; // turn off autosizing
37063     this.layout = config.layout;
37064     this.layout.getEl().addClass("roo-layout-nested-layout");
37065     
37066     
37067     
37068     
37069 };
37070
37071 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37072
37073     setSize : function(width, height){
37074         if(!this.ignoreResize(width, height)){
37075             var size = this.adjustForComponents(width, height);
37076             var el = this.layout.getEl();
37077             if (size.height < 1) {
37078                 el.setWidth(size.width);   
37079             } else {
37080                 el.setSize(size.width, size.height);
37081             }
37082             var touch = el.dom.offsetWidth;
37083             this.layout.layout();
37084             // ie requires a double layout on the first pass
37085             if(Roo.isIE && !this.initialized){
37086                 this.initialized = true;
37087                 this.layout.layout();
37088             }
37089         }
37090     },
37091     
37092     // activate all subpanels if not currently active..
37093     
37094     setActiveState : function(active){
37095         this.active = active;
37096         this.setActiveClass(active);
37097         
37098         if(!active){
37099             this.fireEvent("deactivate", this);
37100             return;
37101         }
37102         
37103         this.fireEvent("activate", this);
37104         // not sure if this should happen before or after..
37105         if (!this.layout) {
37106             return; // should not happen..
37107         }
37108         var reg = false;
37109         for (var r in this.layout.regions) {
37110             reg = this.layout.getRegion(r);
37111             if (reg.getActivePanel()) {
37112                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37113                 reg.setActivePanel(reg.getActivePanel());
37114                 continue;
37115             }
37116             if (!reg.panels.length) {
37117                 continue;
37118             }
37119             reg.showPanel(reg.getPanel(0));
37120         }
37121         
37122         
37123         
37124         
37125     },
37126     
37127     /**
37128      * Returns the nested BorderLayout for this panel
37129      * @return {Roo.BorderLayout} 
37130      */
37131     getLayout : function(){
37132         return this.layout;
37133     },
37134     
37135      /**
37136      * Adds a xtype elements to the layout of the nested panel
37137      * <pre><code>
37138
37139 panel.addxtype({
37140        xtype : 'ContentPanel',
37141        region: 'west',
37142        items: [ .... ]
37143    }
37144 );
37145
37146 panel.addxtype({
37147         xtype : 'NestedLayoutPanel',
37148         region: 'west',
37149         layout: {
37150            center: { },
37151            west: { }   
37152         },
37153         items : [ ... list of content panels or nested layout panels.. ]
37154    }
37155 );
37156 </code></pre>
37157      * @param {Object} cfg Xtype definition of item to add.
37158      */
37159     addxtype : function(cfg) {
37160         return this.layout.addxtype(cfg);
37161     
37162     }
37163 });        /*
37164  * Based on:
37165  * Ext JS Library 1.1.1
37166  * Copyright(c) 2006-2007, Ext JS, LLC.
37167  *
37168  * Originally Released Under LGPL - original licence link has changed is not relivant.
37169  *
37170  * Fork - LGPL
37171  * <script type="text/javascript">
37172  */
37173 /**
37174  * @class Roo.TabPanel
37175  * @extends Roo.util.Observable
37176  * A lightweight tab container.
37177  * <br><br>
37178  * Usage:
37179  * <pre><code>
37180 // basic tabs 1, built from existing content
37181 var tabs = new Roo.TabPanel("tabs1");
37182 tabs.addTab("script", "View Script");
37183 tabs.addTab("markup", "View Markup");
37184 tabs.activate("script");
37185
37186 // more advanced tabs, built from javascript
37187 var jtabs = new Roo.TabPanel("jtabs");
37188 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37189
37190 // set up the UpdateManager
37191 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37192 var updater = tab2.getUpdateManager();
37193 updater.setDefaultUrl("ajax1.htm");
37194 tab2.on('activate', updater.refresh, updater, true);
37195
37196 // Use setUrl for Ajax loading
37197 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37198 tab3.setUrl("ajax2.htm", null, true);
37199
37200 // Disabled tab
37201 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37202 tab4.disable();
37203
37204 jtabs.activate("jtabs-1");
37205  * </code></pre>
37206  * @constructor
37207  * Create a new TabPanel.
37208  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37209  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37210  */
37211 Roo.bootstrap.panel.Tabs = function(config){
37212     /**
37213     * The container element for this TabPanel.
37214     * @type Roo.Element
37215     */
37216     this.el = Roo.get(config.el);
37217     delete config.el;
37218     if(config){
37219         if(typeof config == "boolean"){
37220             this.tabPosition = config ? "bottom" : "top";
37221         }else{
37222             Roo.apply(this, config);
37223         }
37224     }
37225     
37226     if(this.tabPosition == "bottom"){
37227         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37228         this.el.addClass("roo-tabs-bottom");
37229     }
37230     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37231     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37232     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37233     if(Roo.isIE){
37234         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37235     }
37236     if(this.tabPosition != "bottom"){
37237         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37238          * @type Roo.Element
37239          */
37240         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37241         this.el.addClass("roo-tabs-top");
37242     }
37243     this.items = [];
37244
37245     this.bodyEl.setStyle("position", "relative");
37246
37247     this.active = null;
37248     this.activateDelegate = this.activate.createDelegate(this);
37249
37250     this.addEvents({
37251         /**
37252          * @event tabchange
37253          * Fires when the active tab changes
37254          * @param {Roo.TabPanel} this
37255          * @param {Roo.TabPanelItem} activePanel The new active tab
37256          */
37257         "tabchange": true,
37258         /**
37259          * @event beforetabchange
37260          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37261          * @param {Roo.TabPanel} this
37262          * @param {Object} e Set cancel to true on this object to cancel the tab change
37263          * @param {Roo.TabPanelItem} tab The tab being changed to
37264          */
37265         "beforetabchange" : true
37266     });
37267
37268     Roo.EventManager.onWindowResize(this.onResize, this);
37269     this.cpad = this.el.getPadding("lr");
37270     this.hiddenCount = 0;
37271
37272
37273     // toolbar on the tabbar support...
37274     if (this.toolbar) {
37275         alert("no toolbar support yet");
37276         this.toolbar  = false;
37277         /*
37278         var tcfg = this.toolbar;
37279         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37280         this.toolbar = new Roo.Toolbar(tcfg);
37281         if (Roo.isSafari) {
37282             var tbl = tcfg.container.child('table', true);
37283             tbl.setAttribute('width', '100%');
37284         }
37285         */
37286         
37287     }
37288    
37289
37290
37291     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37292 };
37293
37294 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37295     /*
37296      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37297      */
37298     tabPosition : "top",
37299     /*
37300      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37301      */
37302     currentTabWidth : 0,
37303     /*
37304      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37305      */
37306     minTabWidth : 40,
37307     /*
37308      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37309      */
37310     maxTabWidth : 250,
37311     /*
37312      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37313      */
37314     preferredTabWidth : 175,
37315     /*
37316      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37317      */
37318     resizeTabs : false,
37319     /*
37320      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37321      */
37322     monitorResize : true,
37323     /*
37324      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37325      */
37326     toolbar : false,
37327
37328     /**
37329      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37330      * @param {String} id The id of the div to use <b>or create</b>
37331      * @param {String} text The text for the tab
37332      * @param {String} content (optional) Content to put in the TabPanelItem body
37333      * @param {Boolean} closable (optional) True to create a close icon on the tab
37334      * @return {Roo.TabPanelItem} The created TabPanelItem
37335      */
37336     addTab : function(id, text, content, closable, tpl)
37337     {
37338         var item = new Roo.bootstrap.panel.TabItem({
37339             panel: this,
37340             id : id,
37341             text : text,
37342             closable : closable,
37343             tpl : tpl
37344         });
37345         this.addTabItem(item);
37346         if(content){
37347             item.setContent(content);
37348         }
37349         return item;
37350     },
37351
37352     /**
37353      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37354      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37355      * @return {Roo.TabPanelItem}
37356      */
37357     getTab : function(id){
37358         return this.items[id];
37359     },
37360
37361     /**
37362      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37363      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37364      */
37365     hideTab : function(id){
37366         var t = this.items[id];
37367         if(!t.isHidden()){
37368            t.setHidden(true);
37369            this.hiddenCount++;
37370            this.autoSizeTabs();
37371         }
37372     },
37373
37374     /**
37375      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37376      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37377      */
37378     unhideTab : function(id){
37379         var t = this.items[id];
37380         if(t.isHidden()){
37381            t.setHidden(false);
37382            this.hiddenCount--;
37383            this.autoSizeTabs();
37384         }
37385     },
37386
37387     /**
37388      * Adds an existing {@link Roo.TabPanelItem}.
37389      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37390      */
37391     addTabItem : function(item){
37392         this.items[item.id] = item;
37393         this.items.push(item);
37394       //  if(this.resizeTabs){
37395     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37396   //         this.autoSizeTabs();
37397 //        }else{
37398 //            item.autoSize();
37399        // }
37400     },
37401
37402     /**
37403      * Removes a {@link Roo.TabPanelItem}.
37404      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37405      */
37406     removeTab : function(id){
37407         var items = this.items;
37408         var tab = items[id];
37409         if(!tab) { return; }
37410         var index = items.indexOf(tab);
37411         if(this.active == tab && items.length > 1){
37412             var newTab = this.getNextAvailable(index);
37413             if(newTab) {
37414                 newTab.activate();
37415             }
37416         }
37417         this.stripEl.dom.removeChild(tab.pnode.dom);
37418         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37419             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37420         }
37421         items.splice(index, 1);
37422         delete this.items[tab.id];
37423         tab.fireEvent("close", tab);
37424         tab.purgeListeners();
37425         this.autoSizeTabs();
37426     },
37427
37428     getNextAvailable : function(start){
37429         var items = this.items;
37430         var index = start;
37431         // look for a next tab that will slide over to
37432         // replace the one being removed
37433         while(index < items.length){
37434             var item = items[++index];
37435             if(item && !item.isHidden()){
37436                 return item;
37437             }
37438         }
37439         // if one isn't found select the previous tab (on the left)
37440         index = start;
37441         while(index >= 0){
37442             var item = items[--index];
37443             if(item && !item.isHidden()){
37444                 return item;
37445             }
37446         }
37447         return null;
37448     },
37449
37450     /**
37451      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37452      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37453      */
37454     disableTab : function(id){
37455         var tab = this.items[id];
37456         if(tab && this.active != tab){
37457             tab.disable();
37458         }
37459     },
37460
37461     /**
37462      * Enables a {@link Roo.TabPanelItem} that is disabled.
37463      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37464      */
37465     enableTab : function(id){
37466         var tab = this.items[id];
37467         tab.enable();
37468     },
37469
37470     /**
37471      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37472      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37473      * @return {Roo.TabPanelItem} The TabPanelItem.
37474      */
37475     activate : function(id){
37476         var tab = this.items[id];
37477         if(!tab){
37478             return null;
37479         }
37480         if(tab == this.active || tab.disabled){
37481             return tab;
37482         }
37483         var e = {};
37484         this.fireEvent("beforetabchange", this, e, tab);
37485         if(e.cancel !== true && !tab.disabled){
37486             if(this.active){
37487                 this.active.hide();
37488             }
37489             this.active = this.items[id];
37490             this.active.show();
37491             this.fireEvent("tabchange", this, this.active);
37492         }
37493         return tab;
37494     },
37495
37496     /**
37497      * Gets the active {@link Roo.TabPanelItem}.
37498      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37499      */
37500     getActiveTab : function(){
37501         return this.active;
37502     },
37503
37504     /**
37505      * Updates the tab body element to fit the height of the container element
37506      * for overflow scrolling
37507      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37508      */
37509     syncHeight : function(targetHeight){
37510         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37511         var bm = this.bodyEl.getMargins();
37512         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37513         this.bodyEl.setHeight(newHeight);
37514         return newHeight;
37515     },
37516
37517     onResize : function(){
37518         if(this.monitorResize){
37519             this.autoSizeTabs();
37520         }
37521     },
37522
37523     /**
37524      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37525      */
37526     beginUpdate : function(){
37527         this.updating = true;
37528     },
37529
37530     /**
37531      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37532      */
37533     endUpdate : function(){
37534         this.updating = false;
37535         this.autoSizeTabs();
37536     },
37537
37538     /**
37539      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37540      */
37541     autoSizeTabs : function(){
37542         var count = this.items.length;
37543         var vcount = count - this.hiddenCount;
37544         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37545             return;
37546         }
37547         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37548         var availWidth = Math.floor(w / vcount);
37549         var b = this.stripBody;
37550         if(b.getWidth() > w){
37551             var tabs = this.items;
37552             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37553             if(availWidth < this.minTabWidth){
37554                 /*if(!this.sleft){    // incomplete scrolling code
37555                     this.createScrollButtons();
37556                 }
37557                 this.showScroll();
37558                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37559             }
37560         }else{
37561             if(this.currentTabWidth < this.preferredTabWidth){
37562                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37563             }
37564         }
37565     },
37566
37567     /**
37568      * Returns the number of tabs in this TabPanel.
37569      * @return {Number}
37570      */
37571      getCount : function(){
37572          return this.items.length;
37573      },
37574
37575     /**
37576      * Resizes all the tabs to the passed width
37577      * @param {Number} The new width
37578      */
37579     setTabWidth : function(width){
37580         this.currentTabWidth = width;
37581         for(var i = 0, len = this.items.length; i < len; i++) {
37582                 if(!this.items[i].isHidden()) {
37583                 this.items[i].setWidth(width);
37584             }
37585         }
37586     },
37587
37588     /**
37589      * Destroys this TabPanel
37590      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37591      */
37592     destroy : function(removeEl){
37593         Roo.EventManager.removeResizeListener(this.onResize, this);
37594         for(var i = 0, len = this.items.length; i < len; i++){
37595             this.items[i].purgeListeners();
37596         }
37597         if(removeEl === true){
37598             this.el.update("");
37599             this.el.remove();
37600         }
37601     },
37602     
37603     createStrip : function(container)
37604     {
37605         var strip = document.createElement("nav");
37606         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37607         container.appendChild(strip);
37608         return strip;
37609     },
37610     
37611     createStripList : function(strip)
37612     {
37613         // div wrapper for retard IE
37614         // returns the "tr" element.
37615         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37616         //'<div class="x-tabs-strip-wrap">'+
37617           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37618           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37619         return strip.firstChild; //.firstChild.firstChild.firstChild;
37620     },
37621     createBody : function(container)
37622     {
37623         var body = document.createElement("div");
37624         Roo.id(body, "tab-body");
37625         //Roo.fly(body).addClass("x-tabs-body");
37626         Roo.fly(body).addClass("tab-content");
37627         container.appendChild(body);
37628         return body;
37629     },
37630     createItemBody :function(bodyEl, id){
37631         var body = Roo.getDom(id);
37632         if(!body){
37633             body = document.createElement("div");
37634             body.id = id;
37635         }
37636         //Roo.fly(body).addClass("x-tabs-item-body");
37637         Roo.fly(body).addClass("tab-pane");
37638          bodyEl.insertBefore(body, bodyEl.firstChild);
37639         return body;
37640     },
37641     /** @private */
37642     createStripElements :  function(stripEl, text, closable, tpl)
37643     {
37644         var td = document.createElement("li"); // was td..
37645         
37646         
37647         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37648         
37649         
37650         stripEl.appendChild(td);
37651         /*if(closable){
37652             td.className = "x-tabs-closable";
37653             if(!this.closeTpl){
37654                 this.closeTpl = new Roo.Template(
37655                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37656                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37657                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37658                 );
37659             }
37660             var el = this.closeTpl.overwrite(td, {"text": text});
37661             var close = el.getElementsByTagName("div")[0];
37662             var inner = el.getElementsByTagName("em")[0];
37663             return {"el": el, "close": close, "inner": inner};
37664         } else {
37665         */
37666         // not sure what this is..
37667 //            if(!this.tabTpl){
37668                 //this.tabTpl = new Roo.Template(
37669                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37670                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37671                 //);
37672 //                this.tabTpl = new Roo.Template(
37673 //                   '<a href="#">' +
37674 //                   '<span unselectable="on"' +
37675 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37676 //                            ' >{text}</span></a>'
37677 //                );
37678 //                
37679 //            }
37680
37681
37682             var template = tpl || this.tabTpl || false;
37683             
37684             if(!template){
37685                 
37686                 template = new Roo.Template(
37687                    '<a href="#">' +
37688                    '<span unselectable="on"' +
37689                             (this.disableTooltips ? '' : ' title="{text}"') +
37690                             ' >{text}</span></a>'
37691                 );
37692             }
37693             
37694             switch (typeof(template)) {
37695                 case 'object' :
37696                     break;
37697                 case 'string' :
37698                     template = new Roo.Template(template);
37699                     break;
37700                 default :
37701                     break;
37702             }
37703             
37704             var el = template.overwrite(td, {"text": text});
37705             
37706             var inner = el.getElementsByTagName("span")[0];
37707             
37708             return {"el": el, "inner": inner};
37709             
37710     }
37711         
37712     
37713 });
37714
37715 /**
37716  * @class Roo.TabPanelItem
37717  * @extends Roo.util.Observable
37718  * Represents an individual item (tab plus body) in a TabPanel.
37719  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37720  * @param {String} id The id of this TabPanelItem
37721  * @param {String} text The text for the tab of this TabPanelItem
37722  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37723  */
37724 Roo.bootstrap.panel.TabItem = function(config){
37725     /**
37726      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37727      * @type Roo.TabPanel
37728      */
37729     this.tabPanel = config.panel;
37730     /**
37731      * The id for this TabPanelItem
37732      * @type String
37733      */
37734     this.id = config.id;
37735     /** @private */
37736     this.disabled = false;
37737     /** @private */
37738     this.text = config.text;
37739     /** @private */
37740     this.loaded = false;
37741     this.closable = config.closable;
37742
37743     /**
37744      * The body element for this TabPanelItem.
37745      * @type Roo.Element
37746      */
37747     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37748     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37749     this.bodyEl.setStyle("display", "block");
37750     this.bodyEl.setStyle("zoom", "1");
37751     //this.hideAction();
37752
37753     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37754     /** @private */
37755     this.el = Roo.get(els.el);
37756     this.inner = Roo.get(els.inner, true);
37757     this.textEl = Roo.get(this.el.dom.firstChild, true);
37758     this.pnode = Roo.get(els.el.parentNode, true);
37759 //    this.el.on("mousedown", this.onTabMouseDown, this);
37760     this.el.on("click", this.onTabClick, this);
37761     /** @private */
37762     if(config.closable){
37763         var c = Roo.get(els.close, true);
37764         c.dom.title = this.closeText;
37765         c.addClassOnOver("close-over");
37766         c.on("click", this.closeClick, this);
37767      }
37768
37769     this.addEvents({
37770          /**
37771          * @event activate
37772          * Fires when this tab becomes the active tab.
37773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37774          * @param {Roo.TabPanelItem} this
37775          */
37776         "activate": true,
37777         /**
37778          * @event beforeclose
37779          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37780          * @param {Roo.TabPanelItem} this
37781          * @param {Object} e Set cancel to true on this object to cancel the close.
37782          */
37783         "beforeclose": true,
37784         /**
37785          * @event close
37786          * Fires when this tab is closed.
37787          * @param {Roo.TabPanelItem} this
37788          */
37789          "close": true,
37790         /**
37791          * @event deactivate
37792          * Fires when this tab is no longer the active tab.
37793          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37794          * @param {Roo.TabPanelItem} this
37795          */
37796          "deactivate" : true
37797     });
37798     this.hidden = false;
37799
37800     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37801 };
37802
37803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37804            {
37805     purgeListeners : function(){
37806        Roo.util.Observable.prototype.purgeListeners.call(this);
37807        this.el.removeAllListeners();
37808     },
37809     /**
37810      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37811      */
37812     show : function(){
37813         this.pnode.addClass("active");
37814         this.showAction();
37815         if(Roo.isOpera){
37816             this.tabPanel.stripWrap.repaint();
37817         }
37818         this.fireEvent("activate", this.tabPanel, this);
37819     },
37820
37821     /**
37822      * Returns true if this tab is the active tab.
37823      * @return {Boolean}
37824      */
37825     isActive : function(){
37826         return this.tabPanel.getActiveTab() == this;
37827     },
37828
37829     /**
37830      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37831      */
37832     hide : function(){
37833         this.pnode.removeClass("active");
37834         this.hideAction();
37835         this.fireEvent("deactivate", this.tabPanel, this);
37836     },
37837
37838     hideAction : function(){
37839         this.bodyEl.hide();
37840         this.bodyEl.setStyle("position", "absolute");
37841         this.bodyEl.setLeft("-20000px");
37842         this.bodyEl.setTop("-20000px");
37843     },
37844
37845     showAction : function(){
37846         this.bodyEl.setStyle("position", "relative");
37847         this.bodyEl.setTop("");
37848         this.bodyEl.setLeft("");
37849         this.bodyEl.show();
37850     },
37851
37852     /**
37853      * Set the tooltip for the tab.
37854      * @param {String} tooltip The tab's tooltip
37855      */
37856     setTooltip : function(text){
37857         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37858             this.textEl.dom.qtip = text;
37859             this.textEl.dom.removeAttribute('title');
37860         }else{
37861             this.textEl.dom.title = text;
37862         }
37863     },
37864
37865     onTabClick : function(e){
37866         e.preventDefault();
37867         this.tabPanel.activate(this.id);
37868     },
37869
37870     onTabMouseDown : function(e){
37871         e.preventDefault();
37872         this.tabPanel.activate(this.id);
37873     },
37874 /*
37875     getWidth : function(){
37876         return this.inner.getWidth();
37877     },
37878
37879     setWidth : function(width){
37880         var iwidth = width - this.pnode.getPadding("lr");
37881         this.inner.setWidth(iwidth);
37882         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37883         this.pnode.setWidth(width);
37884     },
37885 */
37886     /**
37887      * Show or hide the tab
37888      * @param {Boolean} hidden True to hide or false to show.
37889      */
37890     setHidden : function(hidden){
37891         this.hidden = hidden;
37892         this.pnode.setStyle("display", hidden ? "none" : "");
37893     },
37894
37895     /**
37896      * Returns true if this tab is "hidden"
37897      * @return {Boolean}
37898      */
37899     isHidden : function(){
37900         return this.hidden;
37901     },
37902
37903     /**
37904      * Returns the text for this tab
37905      * @return {String}
37906      */
37907     getText : function(){
37908         return this.text;
37909     },
37910     /*
37911     autoSize : function(){
37912         //this.el.beginMeasure();
37913         this.textEl.setWidth(1);
37914         /*
37915          *  #2804 [new] Tabs in Roojs
37916          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37917          */
37918         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37919         //this.el.endMeasure();
37920     //},
37921
37922     /**
37923      * Sets the text for the tab (Note: this also sets the tooltip text)
37924      * @param {String} text The tab's text and tooltip
37925      */
37926     setText : function(text){
37927         this.text = text;
37928         this.textEl.update(text);
37929         this.setTooltip(text);
37930         //if(!this.tabPanel.resizeTabs){
37931         //    this.autoSize();
37932         //}
37933     },
37934     /**
37935      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37936      */
37937     activate : function(){
37938         this.tabPanel.activate(this.id);
37939     },
37940
37941     /**
37942      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37943      */
37944     disable : function(){
37945         if(this.tabPanel.active != this){
37946             this.disabled = true;
37947             this.pnode.addClass("disabled");
37948         }
37949     },
37950
37951     /**
37952      * Enables this TabPanelItem if it was previously disabled.
37953      */
37954     enable : function(){
37955         this.disabled = false;
37956         this.pnode.removeClass("disabled");
37957     },
37958
37959     /**
37960      * Sets the content for this TabPanelItem.
37961      * @param {String} content The content
37962      * @param {Boolean} loadScripts true to look for and load scripts
37963      */
37964     setContent : function(content, loadScripts){
37965         this.bodyEl.update(content, loadScripts);
37966     },
37967
37968     /**
37969      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37970      * @return {Roo.UpdateManager} The UpdateManager
37971      */
37972     getUpdateManager : function(){
37973         return this.bodyEl.getUpdateManager();
37974     },
37975
37976     /**
37977      * Set a URL to be used to load the content for this TabPanelItem.
37978      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37979      * @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)
37980      * @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)
37981      * @return {Roo.UpdateManager} The UpdateManager
37982      */
37983     setUrl : function(url, params, loadOnce){
37984         if(this.refreshDelegate){
37985             this.un('activate', this.refreshDelegate);
37986         }
37987         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37988         this.on("activate", this.refreshDelegate);
37989         return this.bodyEl.getUpdateManager();
37990     },
37991
37992     /** @private */
37993     _handleRefresh : function(url, params, loadOnce){
37994         if(!loadOnce || !this.loaded){
37995             var updater = this.bodyEl.getUpdateManager();
37996             updater.update(url, params, this._setLoaded.createDelegate(this));
37997         }
37998     },
37999
38000     /**
38001      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38002      *   Will fail silently if the setUrl method has not been called.
38003      *   This does not activate the panel, just updates its content.
38004      */
38005     refresh : function(){
38006         if(this.refreshDelegate){
38007            this.loaded = false;
38008            this.refreshDelegate();
38009         }
38010     },
38011
38012     /** @private */
38013     _setLoaded : function(){
38014         this.loaded = true;
38015     },
38016
38017     /** @private */
38018     closeClick : function(e){
38019         var o = {};
38020         e.stopEvent();
38021         this.fireEvent("beforeclose", this, o);
38022         if(o.cancel !== true){
38023             this.tabPanel.removeTab(this.id);
38024         }
38025     },
38026     /**
38027      * The text displayed in the tooltip for the close icon.
38028      * @type String
38029      */
38030     closeText : "Close this tab"
38031 });
38032 /**
38033 *    This script refer to:
38034 *    Title: International Telephone Input
38035 *    Author: Jack O'Connor
38036 *    Code version:  v12.1.12
38037 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38038 **/
38039
38040 Roo.bootstrap.PhoneInputData = function() {
38041     var d = [
38042       [
38043         "Afghanistan (‫افغانستان‬‎)",
38044         "af",
38045         "93"
38046       ],
38047       [
38048         "Albania (Shqipëri)",
38049         "al",
38050         "355"
38051       ],
38052       [
38053         "Algeria (‫الجزائر‬‎)",
38054         "dz",
38055         "213"
38056       ],
38057       [
38058         "American Samoa",
38059         "as",
38060         "1684"
38061       ],
38062       [
38063         "Andorra",
38064         "ad",
38065         "376"
38066       ],
38067       [
38068         "Angola",
38069         "ao",
38070         "244"
38071       ],
38072       [
38073         "Anguilla",
38074         "ai",
38075         "1264"
38076       ],
38077       [
38078         "Antigua and Barbuda",
38079         "ag",
38080         "1268"
38081       ],
38082       [
38083         "Argentina",
38084         "ar",
38085         "54"
38086       ],
38087       [
38088         "Armenia (Հայաստան)",
38089         "am",
38090         "374"
38091       ],
38092       [
38093         "Aruba",
38094         "aw",
38095         "297"
38096       ],
38097       [
38098         "Australia",
38099         "au",
38100         "61",
38101         0
38102       ],
38103       [
38104         "Austria (Österreich)",
38105         "at",
38106         "43"
38107       ],
38108       [
38109         "Azerbaijan (Azərbaycan)",
38110         "az",
38111         "994"
38112       ],
38113       [
38114         "Bahamas",
38115         "bs",
38116         "1242"
38117       ],
38118       [
38119         "Bahrain (‫البحرين‬‎)",
38120         "bh",
38121         "973"
38122       ],
38123       [
38124         "Bangladesh (বাংলাদেশ)",
38125         "bd",
38126         "880"
38127       ],
38128       [
38129         "Barbados",
38130         "bb",
38131         "1246"
38132       ],
38133       [
38134         "Belarus (Беларусь)",
38135         "by",
38136         "375"
38137       ],
38138       [
38139         "Belgium (België)",
38140         "be",
38141         "32"
38142       ],
38143       [
38144         "Belize",
38145         "bz",
38146         "501"
38147       ],
38148       [
38149         "Benin (Bénin)",
38150         "bj",
38151         "229"
38152       ],
38153       [
38154         "Bermuda",
38155         "bm",
38156         "1441"
38157       ],
38158       [
38159         "Bhutan (འབྲུག)",
38160         "bt",
38161         "975"
38162       ],
38163       [
38164         "Bolivia",
38165         "bo",
38166         "591"
38167       ],
38168       [
38169         "Bosnia and Herzegovina (Босна и Херцеговина)",
38170         "ba",
38171         "387"
38172       ],
38173       [
38174         "Botswana",
38175         "bw",
38176         "267"
38177       ],
38178       [
38179         "Brazil (Brasil)",
38180         "br",
38181         "55"
38182       ],
38183       [
38184         "British Indian Ocean Territory",
38185         "io",
38186         "246"
38187       ],
38188       [
38189         "British Virgin Islands",
38190         "vg",
38191         "1284"
38192       ],
38193       [
38194         "Brunei",
38195         "bn",
38196         "673"
38197       ],
38198       [
38199         "Bulgaria (България)",
38200         "bg",
38201         "359"
38202       ],
38203       [
38204         "Burkina Faso",
38205         "bf",
38206         "226"
38207       ],
38208       [
38209         "Burundi (Uburundi)",
38210         "bi",
38211         "257"
38212       ],
38213       [
38214         "Cambodia (កម្ពុជា)",
38215         "kh",
38216         "855"
38217       ],
38218       [
38219         "Cameroon (Cameroun)",
38220         "cm",
38221         "237"
38222       ],
38223       [
38224         "Canada",
38225         "ca",
38226         "1",
38227         1,
38228         ["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"]
38229       ],
38230       [
38231         "Cape Verde (Kabu Verdi)",
38232         "cv",
38233         "238"
38234       ],
38235       [
38236         "Caribbean Netherlands",
38237         "bq",
38238         "599",
38239         1
38240       ],
38241       [
38242         "Cayman Islands",
38243         "ky",
38244         "1345"
38245       ],
38246       [
38247         "Central African Republic (République centrafricaine)",
38248         "cf",
38249         "236"
38250       ],
38251       [
38252         "Chad (Tchad)",
38253         "td",
38254         "235"
38255       ],
38256       [
38257         "Chile",
38258         "cl",
38259         "56"
38260       ],
38261       [
38262         "China (中国)",
38263         "cn",
38264         "86"
38265       ],
38266       [
38267         "Christmas Island",
38268         "cx",
38269         "61",
38270         2
38271       ],
38272       [
38273         "Cocos (Keeling) Islands",
38274         "cc",
38275         "61",
38276         1
38277       ],
38278       [
38279         "Colombia",
38280         "co",
38281         "57"
38282       ],
38283       [
38284         "Comoros (‫جزر القمر‬‎)",
38285         "km",
38286         "269"
38287       ],
38288       [
38289         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38290         "cd",
38291         "243"
38292       ],
38293       [
38294         "Congo (Republic) (Congo-Brazzaville)",
38295         "cg",
38296         "242"
38297       ],
38298       [
38299         "Cook Islands",
38300         "ck",
38301         "682"
38302       ],
38303       [
38304         "Costa Rica",
38305         "cr",
38306         "506"
38307       ],
38308       [
38309         "Côte d’Ivoire",
38310         "ci",
38311         "225"
38312       ],
38313       [
38314         "Croatia (Hrvatska)",
38315         "hr",
38316         "385"
38317       ],
38318       [
38319         "Cuba",
38320         "cu",
38321         "53"
38322       ],
38323       [
38324         "Curaçao",
38325         "cw",
38326         "599",
38327         0
38328       ],
38329       [
38330         "Cyprus (Κύπρος)",
38331         "cy",
38332         "357"
38333       ],
38334       [
38335         "Czech Republic (Česká republika)",
38336         "cz",
38337         "420"
38338       ],
38339       [
38340         "Denmark (Danmark)",
38341         "dk",
38342         "45"
38343       ],
38344       [
38345         "Djibouti",
38346         "dj",
38347         "253"
38348       ],
38349       [
38350         "Dominica",
38351         "dm",
38352         "1767"
38353       ],
38354       [
38355         "Dominican Republic (República Dominicana)",
38356         "do",
38357         "1",
38358         2,
38359         ["809", "829", "849"]
38360       ],
38361       [
38362         "Ecuador",
38363         "ec",
38364         "593"
38365       ],
38366       [
38367         "Egypt (‫مصر‬‎)",
38368         "eg",
38369         "20"
38370       ],
38371       [
38372         "El Salvador",
38373         "sv",
38374         "503"
38375       ],
38376       [
38377         "Equatorial Guinea (Guinea Ecuatorial)",
38378         "gq",
38379         "240"
38380       ],
38381       [
38382         "Eritrea",
38383         "er",
38384         "291"
38385       ],
38386       [
38387         "Estonia (Eesti)",
38388         "ee",
38389         "372"
38390       ],
38391       [
38392         "Ethiopia",
38393         "et",
38394         "251"
38395       ],
38396       [
38397         "Falkland Islands (Islas Malvinas)",
38398         "fk",
38399         "500"
38400       ],
38401       [
38402         "Faroe Islands (Føroyar)",
38403         "fo",
38404         "298"
38405       ],
38406       [
38407         "Fiji",
38408         "fj",
38409         "679"
38410       ],
38411       [
38412         "Finland (Suomi)",
38413         "fi",
38414         "358",
38415         0
38416       ],
38417       [
38418         "France",
38419         "fr",
38420         "33"
38421       ],
38422       [
38423         "French Guiana (Guyane française)",
38424         "gf",
38425         "594"
38426       ],
38427       [
38428         "French Polynesia (Polynésie française)",
38429         "pf",
38430         "689"
38431       ],
38432       [
38433         "Gabon",
38434         "ga",
38435         "241"
38436       ],
38437       [
38438         "Gambia",
38439         "gm",
38440         "220"
38441       ],
38442       [
38443         "Georgia (საქართველო)",
38444         "ge",
38445         "995"
38446       ],
38447       [
38448         "Germany (Deutschland)",
38449         "de",
38450         "49"
38451       ],
38452       [
38453         "Ghana (Gaana)",
38454         "gh",
38455         "233"
38456       ],
38457       [
38458         "Gibraltar",
38459         "gi",
38460         "350"
38461       ],
38462       [
38463         "Greece (Ελλάδα)",
38464         "gr",
38465         "30"
38466       ],
38467       [
38468         "Greenland (Kalaallit Nunaat)",
38469         "gl",
38470         "299"
38471       ],
38472       [
38473         "Grenada",
38474         "gd",
38475         "1473"
38476       ],
38477       [
38478         "Guadeloupe",
38479         "gp",
38480         "590",
38481         0
38482       ],
38483       [
38484         "Guam",
38485         "gu",
38486         "1671"
38487       ],
38488       [
38489         "Guatemala",
38490         "gt",
38491         "502"
38492       ],
38493       [
38494         "Guernsey",
38495         "gg",
38496         "44",
38497         1
38498       ],
38499       [
38500         "Guinea (Guinée)",
38501         "gn",
38502         "224"
38503       ],
38504       [
38505         "Guinea-Bissau (Guiné Bissau)",
38506         "gw",
38507         "245"
38508       ],
38509       [
38510         "Guyana",
38511         "gy",
38512         "592"
38513       ],
38514       [
38515         "Haiti",
38516         "ht",
38517         "509"
38518       ],
38519       [
38520         "Honduras",
38521         "hn",
38522         "504"
38523       ],
38524       [
38525         "Hong Kong (香港)",
38526         "hk",
38527         "852"
38528       ],
38529       [
38530         "Hungary (Magyarország)",
38531         "hu",
38532         "36"
38533       ],
38534       [
38535         "Iceland (Ísland)",
38536         "is",
38537         "354"
38538       ],
38539       [
38540         "India (भारत)",
38541         "in",
38542         "91"
38543       ],
38544       [
38545         "Indonesia",
38546         "id",
38547         "62"
38548       ],
38549       [
38550         "Iran (‫ایران‬‎)",
38551         "ir",
38552         "98"
38553       ],
38554       [
38555         "Iraq (‫العراق‬‎)",
38556         "iq",
38557         "964"
38558       ],
38559       [
38560         "Ireland",
38561         "ie",
38562         "353"
38563       ],
38564       [
38565         "Isle of Man",
38566         "im",
38567         "44",
38568         2
38569       ],
38570       [
38571         "Israel (‫ישראל‬‎)",
38572         "il",
38573         "972"
38574       ],
38575       [
38576         "Italy (Italia)",
38577         "it",
38578         "39",
38579         0
38580       ],
38581       [
38582         "Jamaica",
38583         "jm",
38584         "1876"
38585       ],
38586       [
38587         "Japan (日本)",
38588         "jp",
38589         "81"
38590       ],
38591       [
38592         "Jersey",
38593         "je",
38594         "44",
38595         3
38596       ],
38597       [
38598         "Jordan (‫الأردن‬‎)",
38599         "jo",
38600         "962"
38601       ],
38602       [
38603         "Kazakhstan (Казахстан)",
38604         "kz",
38605         "7",
38606         1
38607       ],
38608       [
38609         "Kenya",
38610         "ke",
38611         "254"
38612       ],
38613       [
38614         "Kiribati",
38615         "ki",
38616         "686"
38617       ],
38618       [
38619         "Kosovo",
38620         "xk",
38621         "383"
38622       ],
38623       [
38624         "Kuwait (‫الكويت‬‎)",
38625         "kw",
38626         "965"
38627       ],
38628       [
38629         "Kyrgyzstan (Кыргызстан)",
38630         "kg",
38631         "996"
38632       ],
38633       [
38634         "Laos (ລາວ)",
38635         "la",
38636         "856"
38637       ],
38638       [
38639         "Latvia (Latvija)",
38640         "lv",
38641         "371"
38642       ],
38643       [
38644         "Lebanon (‫لبنان‬‎)",
38645         "lb",
38646         "961"
38647       ],
38648       [
38649         "Lesotho",
38650         "ls",
38651         "266"
38652       ],
38653       [
38654         "Liberia",
38655         "lr",
38656         "231"
38657       ],
38658       [
38659         "Libya (‫ليبيا‬‎)",
38660         "ly",
38661         "218"
38662       ],
38663       [
38664         "Liechtenstein",
38665         "li",
38666         "423"
38667       ],
38668       [
38669         "Lithuania (Lietuva)",
38670         "lt",
38671         "370"
38672       ],
38673       [
38674         "Luxembourg",
38675         "lu",
38676         "352"
38677       ],
38678       [
38679         "Macau (澳門)",
38680         "mo",
38681         "853"
38682       ],
38683       [
38684         "Macedonia (FYROM) (Македонија)",
38685         "mk",
38686         "389"
38687       ],
38688       [
38689         "Madagascar (Madagasikara)",
38690         "mg",
38691         "261"
38692       ],
38693       [
38694         "Malawi",
38695         "mw",
38696         "265"
38697       ],
38698       [
38699         "Malaysia",
38700         "my",
38701         "60"
38702       ],
38703       [
38704         "Maldives",
38705         "mv",
38706         "960"
38707       ],
38708       [
38709         "Mali",
38710         "ml",
38711         "223"
38712       ],
38713       [
38714         "Malta",
38715         "mt",
38716         "356"
38717       ],
38718       [
38719         "Marshall Islands",
38720         "mh",
38721         "692"
38722       ],
38723       [
38724         "Martinique",
38725         "mq",
38726         "596"
38727       ],
38728       [
38729         "Mauritania (‫موريتانيا‬‎)",
38730         "mr",
38731         "222"
38732       ],
38733       [
38734         "Mauritius (Moris)",
38735         "mu",
38736         "230"
38737       ],
38738       [
38739         "Mayotte",
38740         "yt",
38741         "262",
38742         1
38743       ],
38744       [
38745         "Mexico (México)",
38746         "mx",
38747         "52"
38748       ],
38749       [
38750         "Micronesia",
38751         "fm",
38752         "691"
38753       ],
38754       [
38755         "Moldova (Republica Moldova)",
38756         "md",
38757         "373"
38758       ],
38759       [
38760         "Monaco",
38761         "mc",
38762         "377"
38763       ],
38764       [
38765         "Mongolia (Монгол)",
38766         "mn",
38767         "976"
38768       ],
38769       [
38770         "Montenegro (Crna Gora)",
38771         "me",
38772         "382"
38773       ],
38774       [
38775         "Montserrat",
38776         "ms",
38777         "1664"
38778       ],
38779       [
38780         "Morocco (‫المغرب‬‎)",
38781         "ma",
38782         "212",
38783         0
38784       ],
38785       [
38786         "Mozambique (Moçambique)",
38787         "mz",
38788         "258"
38789       ],
38790       [
38791         "Myanmar (Burma) (မြန်မာ)",
38792         "mm",
38793         "95"
38794       ],
38795       [
38796         "Namibia (Namibië)",
38797         "na",
38798         "264"
38799       ],
38800       [
38801         "Nauru",
38802         "nr",
38803         "674"
38804       ],
38805       [
38806         "Nepal (नेपाल)",
38807         "np",
38808         "977"
38809       ],
38810       [
38811         "Netherlands (Nederland)",
38812         "nl",
38813         "31"
38814       ],
38815       [
38816         "New Caledonia (Nouvelle-Calédonie)",
38817         "nc",
38818         "687"
38819       ],
38820       [
38821         "New Zealand",
38822         "nz",
38823         "64"
38824       ],
38825       [
38826         "Nicaragua",
38827         "ni",
38828         "505"
38829       ],
38830       [
38831         "Niger (Nijar)",
38832         "ne",
38833         "227"
38834       ],
38835       [
38836         "Nigeria",
38837         "ng",
38838         "234"
38839       ],
38840       [
38841         "Niue",
38842         "nu",
38843         "683"
38844       ],
38845       [
38846         "Norfolk Island",
38847         "nf",
38848         "672"
38849       ],
38850       [
38851         "North Korea (조선 민주주의 인민 공화국)",
38852         "kp",
38853         "850"
38854       ],
38855       [
38856         "Northern Mariana Islands",
38857         "mp",
38858         "1670"
38859       ],
38860       [
38861         "Norway (Norge)",
38862         "no",
38863         "47",
38864         0
38865       ],
38866       [
38867         "Oman (‫عُمان‬‎)",
38868         "om",
38869         "968"
38870       ],
38871       [
38872         "Pakistan (‫پاکستان‬‎)",
38873         "pk",
38874         "92"
38875       ],
38876       [
38877         "Palau",
38878         "pw",
38879         "680"
38880       ],
38881       [
38882         "Palestine (‫فلسطين‬‎)",
38883         "ps",
38884         "970"
38885       ],
38886       [
38887         "Panama (Panamá)",
38888         "pa",
38889         "507"
38890       ],
38891       [
38892         "Papua New Guinea",
38893         "pg",
38894         "675"
38895       ],
38896       [
38897         "Paraguay",
38898         "py",
38899         "595"
38900       ],
38901       [
38902         "Peru (Perú)",
38903         "pe",
38904         "51"
38905       ],
38906       [
38907         "Philippines",
38908         "ph",
38909         "63"
38910       ],
38911       [
38912         "Poland (Polska)",
38913         "pl",
38914         "48"
38915       ],
38916       [
38917         "Portugal",
38918         "pt",
38919         "351"
38920       ],
38921       [
38922         "Puerto Rico",
38923         "pr",
38924         "1",
38925         3,
38926         ["787", "939"]
38927       ],
38928       [
38929         "Qatar (‫قطر‬‎)",
38930         "qa",
38931         "974"
38932       ],
38933       [
38934         "Réunion (La Réunion)",
38935         "re",
38936         "262",
38937         0
38938       ],
38939       [
38940         "Romania (România)",
38941         "ro",
38942         "40"
38943       ],
38944       [
38945         "Russia (Россия)",
38946         "ru",
38947         "7",
38948         0
38949       ],
38950       [
38951         "Rwanda",
38952         "rw",
38953         "250"
38954       ],
38955       [
38956         "Saint Barthélemy",
38957         "bl",
38958         "590",
38959         1
38960       ],
38961       [
38962         "Saint Helena",
38963         "sh",
38964         "290"
38965       ],
38966       [
38967         "Saint Kitts and Nevis",
38968         "kn",
38969         "1869"
38970       ],
38971       [
38972         "Saint Lucia",
38973         "lc",
38974         "1758"
38975       ],
38976       [
38977         "Saint Martin (Saint-Martin (partie française))",
38978         "mf",
38979         "590",
38980         2
38981       ],
38982       [
38983         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38984         "pm",
38985         "508"
38986       ],
38987       [
38988         "Saint Vincent and the Grenadines",
38989         "vc",
38990         "1784"
38991       ],
38992       [
38993         "Samoa",
38994         "ws",
38995         "685"
38996       ],
38997       [
38998         "San Marino",
38999         "sm",
39000         "378"
39001       ],
39002       [
39003         "São Tomé and Príncipe (São Tomé e Príncipe)",
39004         "st",
39005         "239"
39006       ],
39007       [
39008         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39009         "sa",
39010         "966"
39011       ],
39012       [
39013         "Senegal (Sénégal)",
39014         "sn",
39015         "221"
39016       ],
39017       [
39018         "Serbia (Србија)",
39019         "rs",
39020         "381"
39021       ],
39022       [
39023         "Seychelles",
39024         "sc",
39025         "248"
39026       ],
39027       [
39028         "Sierra Leone",
39029         "sl",
39030         "232"
39031       ],
39032       [
39033         "Singapore",
39034         "sg",
39035         "65"
39036       ],
39037       [
39038         "Sint Maarten",
39039         "sx",
39040         "1721"
39041       ],
39042       [
39043         "Slovakia (Slovensko)",
39044         "sk",
39045         "421"
39046       ],
39047       [
39048         "Slovenia (Slovenija)",
39049         "si",
39050         "386"
39051       ],
39052       [
39053         "Solomon Islands",
39054         "sb",
39055         "677"
39056       ],
39057       [
39058         "Somalia (Soomaaliya)",
39059         "so",
39060         "252"
39061       ],
39062       [
39063         "South Africa",
39064         "za",
39065         "27"
39066       ],
39067       [
39068         "South Korea (대한민국)",
39069         "kr",
39070         "82"
39071       ],
39072       [
39073         "South Sudan (‫جنوب السودان‬‎)",
39074         "ss",
39075         "211"
39076       ],
39077       [
39078         "Spain (España)",
39079         "es",
39080         "34"
39081       ],
39082       [
39083         "Sri Lanka (ශ්‍රී ලංකාව)",
39084         "lk",
39085         "94"
39086       ],
39087       [
39088         "Sudan (‫السودان‬‎)",
39089         "sd",
39090         "249"
39091       ],
39092       [
39093         "Suriname",
39094         "sr",
39095         "597"
39096       ],
39097       [
39098         "Svalbard and Jan Mayen",
39099         "sj",
39100         "47",
39101         1
39102       ],
39103       [
39104         "Swaziland",
39105         "sz",
39106         "268"
39107       ],
39108       [
39109         "Sweden (Sverige)",
39110         "se",
39111         "46"
39112       ],
39113       [
39114         "Switzerland (Schweiz)",
39115         "ch",
39116         "41"
39117       ],
39118       [
39119         "Syria (‫سوريا‬‎)",
39120         "sy",
39121         "963"
39122       ],
39123       [
39124         "Taiwan (台灣)",
39125         "tw",
39126         "886"
39127       ],
39128       [
39129         "Tajikistan",
39130         "tj",
39131         "992"
39132       ],
39133       [
39134         "Tanzania",
39135         "tz",
39136         "255"
39137       ],
39138       [
39139         "Thailand (ไทย)",
39140         "th",
39141         "66"
39142       ],
39143       [
39144         "Timor-Leste",
39145         "tl",
39146         "670"
39147       ],
39148       [
39149         "Togo",
39150         "tg",
39151         "228"
39152       ],
39153       [
39154         "Tokelau",
39155         "tk",
39156         "690"
39157       ],
39158       [
39159         "Tonga",
39160         "to",
39161         "676"
39162       ],
39163       [
39164         "Trinidad and Tobago",
39165         "tt",
39166         "1868"
39167       ],
39168       [
39169         "Tunisia (‫تونس‬‎)",
39170         "tn",
39171         "216"
39172       ],
39173       [
39174         "Turkey (Türkiye)",
39175         "tr",
39176         "90"
39177       ],
39178       [
39179         "Turkmenistan",
39180         "tm",
39181         "993"
39182       ],
39183       [
39184         "Turks and Caicos Islands",
39185         "tc",
39186         "1649"
39187       ],
39188       [
39189         "Tuvalu",
39190         "tv",
39191         "688"
39192       ],
39193       [
39194         "U.S. Virgin Islands",
39195         "vi",
39196         "1340"
39197       ],
39198       [
39199         "Uganda",
39200         "ug",
39201         "256"
39202       ],
39203       [
39204         "Ukraine (Україна)",
39205         "ua",
39206         "380"
39207       ],
39208       [
39209         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39210         "ae",
39211         "971"
39212       ],
39213       [
39214         "United Kingdom",
39215         "gb",
39216         "44",
39217         0
39218       ],
39219       [
39220         "United States",
39221         "us",
39222         "1",
39223         0
39224       ],
39225       [
39226         "Uruguay",
39227         "uy",
39228         "598"
39229       ],
39230       [
39231         "Uzbekistan (Oʻzbekiston)",
39232         "uz",
39233         "998"
39234       ],
39235       [
39236         "Vanuatu",
39237         "vu",
39238         "678"
39239       ],
39240       [
39241         "Vatican City (Città del Vaticano)",
39242         "va",
39243         "39",
39244         1
39245       ],
39246       [
39247         "Venezuela",
39248         "ve",
39249         "58"
39250       ],
39251       [
39252         "Vietnam (Việt Nam)",
39253         "vn",
39254         "84"
39255       ],
39256       [
39257         "Wallis and Futuna (Wallis-et-Futuna)",
39258         "wf",
39259         "681"
39260       ],
39261       [
39262         "Western Sahara (‫الصحراء الغربية‬‎)",
39263         "eh",
39264         "212",
39265         1
39266       ],
39267       [
39268         "Yemen (‫اليمن‬‎)",
39269         "ye",
39270         "967"
39271       ],
39272       [
39273         "Zambia",
39274         "zm",
39275         "260"
39276       ],
39277       [
39278         "Zimbabwe",
39279         "zw",
39280         "263"
39281       ],
39282       [
39283         "Åland Islands",
39284         "ax",
39285         "358",
39286         1
39287       ]
39288   ];
39289   
39290   return d;
39291 }/**
39292 *    This script refer to:
39293 *    Title: International Telephone Input
39294 *    Author: Jack O'Connor
39295 *    Code version:  v12.1.12
39296 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39297 **/
39298
39299 /**
39300  * @class Roo.bootstrap.PhoneInput
39301  * @extends Roo.bootstrap.TriggerField
39302  * An input with International dial-code selection
39303  
39304  * @cfg {String} defaultDialCode default '+852'
39305  * @cfg {Array} preferedCountries default []
39306   
39307  * @constructor
39308  * Create a new PhoneInput.
39309  * @param {Object} config Configuration options
39310  */
39311
39312 Roo.bootstrap.PhoneInput = function(config) {
39313     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39314 };
39315
39316 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39317         
39318         listWidth: undefined,
39319         
39320         selectedClass: 'active',
39321         
39322         invalidClass : "has-warning",
39323         
39324         validClass: 'has-success',
39325         
39326         allowed: '0123456789',
39327         
39328         /**
39329          * @cfg {String} defaultDialCode The default dial code when initializing the input
39330          */
39331         defaultDialCode: '+852',
39332         
39333         /**
39334          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39335          */
39336         preferedCountries: false,
39337         
39338         getAutoCreate : function()
39339         {
39340             var data = Roo.bootstrap.PhoneInputData();
39341             var align = this.labelAlign || this.parentLabelAlign();
39342             var id = Roo.id();
39343             
39344             this.allCountries = [];
39345             this.dialCodeMapping = [];
39346             
39347             for (var i = 0; i < data.length; i++) {
39348               var c = data[i];
39349               this.allCountries[i] = {
39350                 name: c[0],
39351                 iso2: c[1],
39352                 dialCode: c[2],
39353                 priority: c[3] || 0,
39354                 areaCodes: c[4] || null
39355               };
39356               this.dialCodeMapping[c[2]] = {
39357                   name: c[0],
39358                   iso2: c[1],
39359                   priority: c[3] || 0,
39360                   areaCodes: c[4] || null
39361               };
39362             }
39363             
39364             var cfg = {
39365                 cls: 'form-group',
39366                 cn: []
39367             };
39368             
39369             var input =  {
39370                 tag: 'input',
39371                 id : id,
39372                 cls : 'form-control tel-input',
39373                 autocomplete: 'new-password'
39374             };
39375             
39376             var hiddenInput = {
39377                 tag: 'input',
39378                 type: 'hidden',
39379                 cls: 'hidden-tel-input'
39380             };
39381             
39382             if (this.name) {
39383                 hiddenInput.name = this.name;
39384             }
39385             
39386             if (this.disabled) {
39387                 input.disabled = true;
39388             }
39389             
39390             var flag_container = {
39391                 tag: 'div',
39392                 cls: 'flag-box',
39393                 cn: [
39394                     {
39395                         tag: 'div',
39396                         cls: 'flag'
39397                     },
39398                     {
39399                         tag: 'div',
39400                         cls: 'caret'
39401                     }
39402                 ]
39403             };
39404             
39405             var box = {
39406                 tag: 'div',
39407                 cls: this.hasFeedback ? 'has-feedback' : '',
39408                 cn: [
39409                     hiddenInput,
39410                     input,
39411                     {
39412                         tag: 'input',
39413                         cls: 'dial-code-holder',
39414                         disabled: true
39415                     }
39416                 ]
39417             };
39418             
39419             var container = {
39420                 cls: 'roo-select2-container input-group',
39421                 cn: [
39422                     flag_container,
39423                     box
39424                 ]
39425             };
39426             
39427             if (this.fieldLabel.length) {
39428                 var indicator = {
39429                     tag: 'i',
39430                     tooltip: 'This field is required'
39431                 };
39432                 
39433                 var label = {
39434                     tag: 'label',
39435                     'for':  id,
39436                     cls: 'control-label',
39437                     cn: []
39438                 };
39439                 
39440                 var label_text = {
39441                     tag: 'span',
39442                     html: this.fieldLabel
39443                 };
39444                 
39445                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39446                 label.cn = [
39447                     indicator,
39448                     label_text
39449                 ];
39450                 
39451                 if(this.indicatorpos == 'right') {
39452                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39453                     label.cn = [
39454                         label_text,
39455                         indicator
39456                     ];
39457                 }
39458                 
39459                 if(align == 'left') {
39460                     container = {
39461                         tag: 'div',
39462                         cn: [
39463                             container
39464                         ]
39465                     };
39466                     
39467                     if(this.labelWidth > 12){
39468                         label.style = "width: " + this.labelWidth + 'px';
39469                     }
39470                     if(this.labelWidth < 13 && this.labelmd == 0){
39471                         this.labelmd = this.labelWidth;
39472                     }
39473                     if(this.labellg > 0){
39474                         label.cls += ' col-lg-' + this.labellg;
39475                         input.cls += ' col-lg-' + (12 - this.labellg);
39476                     }
39477                     if(this.labelmd > 0){
39478                         label.cls += ' col-md-' + this.labelmd;
39479                         container.cls += ' col-md-' + (12 - this.labelmd);
39480                     }
39481                     if(this.labelsm > 0){
39482                         label.cls += ' col-sm-' + this.labelsm;
39483                         container.cls += ' col-sm-' + (12 - this.labelsm);
39484                     }
39485                     if(this.labelxs > 0){
39486                         label.cls += ' col-xs-' + this.labelxs;
39487                         container.cls += ' col-xs-' + (12 - this.labelxs);
39488                     }
39489                 }
39490             }
39491             
39492             cfg.cn = [
39493                 label,
39494                 container
39495             ];
39496             
39497             var settings = this;
39498             
39499             ['xs','sm','md','lg'].map(function(size){
39500                 if (settings[size]) {
39501                     cfg.cls += ' col-' + size + '-' + settings[size];
39502                 }
39503             });
39504             
39505             this.store = new Roo.data.Store({
39506                 proxy : new Roo.data.MemoryProxy({}),
39507                 reader : new Roo.data.JsonReader({
39508                     fields : [
39509                         {
39510                             'name' : 'name',
39511                             'type' : 'string'
39512                         },
39513                         {
39514                             'name' : 'iso2',
39515                             'type' : 'string'
39516                         },
39517                         {
39518                             'name' : 'dialCode',
39519                             'type' : 'string'
39520                         },
39521                         {
39522                             'name' : 'priority',
39523                             'type' : 'string'
39524                         },
39525                         {
39526                             'name' : 'areaCodes',
39527                             'type' : 'string'
39528                         }
39529                     ]
39530                 })
39531             });
39532             
39533             if(!this.preferedCountries) {
39534                 this.preferedCountries = [
39535                     'hk',
39536                     'gb',
39537                     'us'
39538                 ];
39539             }
39540             
39541             var p = this.preferedCountries.reverse();
39542             
39543             if(p) {
39544                 for (var i = 0; i < p.length; i++) {
39545                     for (var j = 0; j < this.allCountries.length; j++) {
39546                         if(this.allCountries[j].iso2 == p[i]) {
39547                             var t = this.allCountries[j];
39548                             this.allCountries.splice(j,1);
39549                             this.allCountries.unshift(t);
39550                         }
39551                     } 
39552                 }
39553             }
39554             
39555             this.store.proxy.data = {
39556                 success: true,
39557                 data: this.allCountries
39558             };
39559             
39560             return cfg;
39561         },
39562         
39563         initEvents : function()
39564         {
39565             this.createList();
39566             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39567             
39568             this.indicator = this.indicatorEl();
39569             this.flag = this.flagEl();
39570             this.dialCodeHolder = this.dialCodeHolderEl();
39571             
39572             this.trigger = this.el.select('div.flag-box',true).first();
39573             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39574             
39575             var _this = this;
39576             
39577             (function(){
39578                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39579                 _this.list.setWidth(lw);
39580             }).defer(100);
39581             
39582             this.list.on('mouseover', this.onViewOver, this);
39583             this.list.on('mousemove', this.onViewMove, this);
39584             this.inputEl().on("keyup", this.onKeyUp, this);
39585             
39586             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39587
39588             this.view = new Roo.View(this.list, this.tpl, {
39589                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39590             });
39591             
39592             this.view.on('click', this.onViewClick, this);
39593             this.setValue(this.defaultDialCode);
39594         },
39595         
39596         onTriggerClick : function(e)
39597         {
39598             Roo.log('trigger click');
39599             if(this.disabled){
39600                 return;
39601             }
39602             
39603             if(this.isExpanded()){
39604                 this.collapse();
39605                 this.hasFocus = false;
39606             }else {
39607                 this.store.load({});
39608                 this.hasFocus = true;
39609                 this.expand();
39610             }
39611         },
39612         
39613         isExpanded : function()
39614         {
39615             return this.list.isVisible();
39616         },
39617         
39618         collapse : function()
39619         {
39620             if(!this.isExpanded()){
39621                 return;
39622             }
39623             this.list.hide();
39624             Roo.get(document).un('mousedown', this.collapseIf, this);
39625             Roo.get(document).un('mousewheel', this.collapseIf, this);
39626             this.fireEvent('collapse', this);
39627             this.validate();
39628         },
39629         
39630         expand : function()
39631         {
39632             Roo.log('expand');
39633
39634             if(this.isExpanded() || !this.hasFocus){
39635                 return;
39636             }
39637             
39638             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39639             this.list.setWidth(lw);
39640             
39641             this.list.show();
39642             this.restrictHeight();
39643             
39644             Roo.get(document).on('mousedown', this.collapseIf, this);
39645             Roo.get(document).on('mousewheel', this.collapseIf, this);
39646             
39647             this.fireEvent('expand', this);
39648         },
39649         
39650         restrictHeight : function()
39651         {
39652             this.list.alignTo(this.inputEl(), this.listAlign);
39653             this.list.alignTo(this.inputEl(), this.listAlign);
39654         },
39655         
39656         onViewOver : function(e, t)
39657         {
39658             if(this.inKeyMode){
39659                 return;
39660             }
39661             var item = this.view.findItemFromChild(t);
39662             
39663             if(item){
39664                 var index = this.view.indexOf(item);
39665                 this.select(index, false);
39666             }
39667         },
39668
39669         // private
39670         onViewClick : function(view, doFocus, el, e)
39671         {
39672             var index = this.view.getSelectedIndexes()[0];
39673             
39674             var r = this.store.getAt(index);
39675             
39676             if(r){
39677                 this.onSelect(r, index);
39678             }
39679             if(doFocus !== false && !this.blockFocus){
39680                 this.inputEl().focus();
39681             }
39682         },
39683         
39684         onViewMove : function(e, t)
39685         {
39686             this.inKeyMode = false;
39687         },
39688         
39689         select : function(index, scrollIntoView)
39690         {
39691             this.selectedIndex = index;
39692             this.view.select(index);
39693             if(scrollIntoView !== false){
39694                 var el = this.view.getNode(index);
39695                 if(el){
39696                     this.list.scrollChildIntoView(el, false);
39697                 }
39698             }
39699         },
39700         
39701         createList : function()
39702         {
39703             this.list = Roo.get(document.body).createChild({
39704                 tag: 'ul',
39705                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39706                 style: 'display:none'
39707             });
39708             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39709         },
39710         
39711         collapseIf : function(e)
39712         {
39713             var in_combo  = e.within(this.el);
39714             var in_list =  e.within(this.list);
39715             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39716             
39717             if (in_combo || in_list || is_list) {
39718                 return;
39719             }
39720             this.collapse();
39721         },
39722         
39723         onSelect : function(record, index)
39724         {
39725             if(this.fireEvent('beforeselect', this, record, index) !== false){
39726                 
39727                 this.setFlagClass(record.data.iso2);
39728                 this.setDialCode(record.data.dialCode);
39729                 this.hasFocus = false;
39730                 this.collapse();
39731                 this.fireEvent('select', this, record, index);
39732             }
39733         },
39734         
39735         flagEl : function()
39736         {
39737             var flag = this.el.select('div.flag',true).first();
39738             if(!flag){
39739                 return false;
39740             }
39741             return flag;
39742         },
39743         
39744         dialCodeHolderEl : function()
39745         {
39746             var d = this.el.select('input.dial-code-holder',true).first();
39747             if(!d){
39748                 return false;
39749             }
39750             return d;
39751         },
39752         
39753         setDialCode : function(v)
39754         {
39755             this.dialCodeHolder.dom.value = '+'+v;
39756         },
39757         
39758         setFlagClass : function(n)
39759         {
39760             this.flag.dom.className = 'flag '+n;
39761         },
39762         
39763         getValue : function()
39764         {
39765             var v = this.inputEl().getValue();
39766             if(this.dialCodeHolder) {
39767                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39768             }
39769             return v;
39770         },
39771         
39772         setValue : function(v)
39773         {
39774             var d = this.getDialCode(v);
39775             
39776             //invalid dial code
39777             if(v.length == 0 || !d || d.length == 0) {
39778                 if(this.rendered){
39779                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39780                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39781                 }
39782                 return;
39783             }
39784             
39785             //valid dial code
39786             this.setFlagClass(this.dialCodeMapping[d].iso2);
39787             this.setDialCode(d);
39788             this.inputEl().dom.value = v.replace('+'+d,'');
39789             this.hiddenEl().dom.value = this.getValue();
39790             
39791             this.validate();
39792         },
39793         
39794         getDialCode : function(v = '')
39795         {
39796             if (v.length == 0) {
39797                 return this.dialCodeHolder.dom.value;
39798             }
39799             
39800             var dialCode = "";
39801             if (v.charAt(0) != "+") {
39802                 return false;
39803             }
39804             var numericChars = "";
39805             for (var i = 1; i < v.length; i++) {
39806               var c = v.charAt(i);
39807               if (!isNaN(c)) {
39808                 numericChars += c;
39809                 if (this.dialCodeMapping[numericChars]) {
39810                   dialCode = v.substr(1, i);
39811                 }
39812                 if (numericChars.length == 4) {
39813                   break;
39814                 }
39815               }
39816             }
39817             return dialCode;
39818         },
39819         
39820         reset : function()
39821         {
39822             this.setValue(this.defaultDialCode);
39823             this.validate();
39824         },
39825         
39826         hiddenEl : function()
39827         {
39828             return this.el.select('input.hidden-tel-input',true).first();
39829         },
39830         
39831         onKeyUp : function(e){
39832             
39833             var k = e.getKey();
39834             var c = e.getCharCode();
39835             
39836             if(
39837                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39838                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39839             ){
39840                 e.stopEvent();
39841             }
39842             
39843             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39844             //     return;
39845             // }
39846             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39847                 e.stopEvent();
39848             }
39849             
39850             this.setValue(this.getValue());
39851         }
39852         
39853 });
39854 /**
39855  * @class Roo.bootstrap.MoneyField
39856  * @extends Roo.bootstrap.ComboBox
39857  * Bootstrap MoneyField class
39858  * 
39859  * @constructor
39860  * Create a new MoneyField.
39861  * @param {Object} config Configuration options
39862  */
39863
39864 Roo.bootstrap.MoneyField = function(config) {
39865     
39866     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39867     
39868 };
39869
39870 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39871     
39872     /**
39873      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39874      */
39875     allowDecimals : true,
39876     /**
39877      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39878      */
39879     decimalSeparator : ".",
39880     /**
39881      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39882      */
39883     decimalPrecision : 2,
39884     /**
39885      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39886      */
39887     allowNegative : true,
39888     /**
39889      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39890      */
39891     minValue : Number.NEGATIVE_INFINITY,
39892     /**
39893      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39894      */
39895     maxValue : Number.MAX_VALUE,
39896     /**
39897      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39898      */
39899     minText : "The minimum value for this field is {0}",
39900     /**
39901      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39902      */
39903     maxText : "The maximum value for this field is {0}",
39904     /**
39905      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39906      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39907      */
39908     nanText : "{0} is not a valid number",
39909     /**
39910      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39911      */
39912     castInt : true,
39913     
39914     inputlg : 9,
39915     inputmd : 9,
39916     inputsm : 9,
39917     inputxs : 6,
39918     
39919     store : false,
39920     
39921     getAutoCreate : function()
39922     {
39923         var align = this.labelAlign || this.parentLabelAlign();
39924         
39925         var id = Roo.id();
39926
39927         var cfg = {
39928             cls: 'form-group',
39929             cn: []
39930         };
39931
39932         var input =  {
39933             tag: 'input',
39934             id : id,
39935             cls : 'form-control roo-money-amount-input',
39936             autocomplete: 'new-password'
39937         };
39938         
39939         if (this.name) {
39940             input.name = this.name;
39941         }
39942
39943         if (this.disabled) {
39944             input.disabled = true;
39945         }
39946
39947         var clg = 12 - this.inputlg;
39948         var cmd = 12 - this.inputmd;
39949         var csm = 12 - this.inputsm;
39950         var cxs = 12 - this.inputxs;
39951         
39952         var container = {
39953             tag : 'div',
39954             cls : 'row roo-money-field',
39955             cn : [
39956                 {
39957                     tag : 'div',
39958                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39959                     cn : [
39960                         {
39961                             tag : 'div',
39962                             cls: 'roo-select2-container input-group',
39963                             cn: [
39964                                 {
39965                                     tag : 'input',
39966                                     cls : 'form-control roo-money-currency-input',
39967                                     autocomplete: 'new-password',
39968                                     readOnly : 1,
39969                                     name : this.currencyName
39970                                 },
39971                                 {
39972                                     tag :'span',
39973                                     cls : 'input-group-addon',
39974                                     cn : [
39975                                         {
39976                                             tag: 'span',
39977                                             cls: 'caret'
39978                                         }
39979                                     ]
39980                                 }
39981                             ]
39982                         }
39983                     ]
39984                 },
39985                 {
39986                     tag : 'div',
39987                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39988                     cn : [
39989                         {
39990                             tag: 'div',
39991                             cls: this.hasFeedback ? 'has-feedback' : '',
39992                             cn: [
39993                                 input
39994                             ]
39995                         }
39996                     ]
39997                 }
39998             ]
39999             
40000         };
40001         
40002         if (this.fieldLabel.length) {
40003             var indicator = {
40004                 tag: 'i',
40005                 tooltip: 'This field is required'
40006             };
40007
40008             var label = {
40009                 tag: 'label',
40010                 'for':  id,
40011                 cls: 'control-label',
40012                 cn: []
40013             };
40014
40015             var label_text = {
40016                 tag: 'span',
40017                 html: this.fieldLabel
40018             };
40019
40020             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40021             label.cn = [
40022                 indicator,
40023                 label_text
40024             ];
40025
40026             if(this.indicatorpos == 'right') {
40027                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40028                 label.cn = [
40029                     label_text,
40030                     indicator
40031                 ];
40032             }
40033
40034             if(align == 'left') {
40035                 container = {
40036                     tag: 'div',
40037                     cn: [
40038                         container
40039                     ]
40040                 };
40041
40042                 if(this.labelWidth > 12){
40043                     label.style = "width: " + this.labelWidth + 'px';
40044                 }
40045                 if(this.labelWidth < 13 && this.labelmd == 0){
40046                     this.labelmd = this.labelWidth;
40047                 }
40048                 if(this.labellg > 0){
40049                     label.cls += ' col-lg-' + this.labellg;
40050                     input.cls += ' col-lg-' + (12 - this.labellg);
40051                 }
40052                 if(this.labelmd > 0){
40053                     label.cls += ' col-md-' + this.labelmd;
40054                     container.cls += ' col-md-' + (12 - this.labelmd);
40055                 }
40056                 if(this.labelsm > 0){
40057                     label.cls += ' col-sm-' + this.labelsm;
40058                     container.cls += ' col-sm-' + (12 - this.labelsm);
40059                 }
40060                 if(this.labelxs > 0){
40061                     label.cls += ' col-xs-' + this.labelxs;
40062                     container.cls += ' col-xs-' + (12 - this.labelxs);
40063                 }
40064             }
40065         }
40066
40067         cfg.cn = [
40068             label,
40069             container
40070         ];
40071
40072         var settings = this;
40073
40074         ['xs','sm','md','lg'].map(function(size){
40075             if (settings[size]) {
40076                 cfg.cls += ' col-' + size + '-' + settings[size];
40077             }
40078         });
40079         
40080         return cfg;
40081         
40082     },
40083     
40084     initEvents : function()
40085     {
40086         this.indicator = this.indicatorEl();
40087         
40088         this.initCurrencyEvent();
40089         
40090         this.initNumberEvent();
40091         
40092     },
40093     
40094     initCurrencyEvent : function()
40095     {
40096         if (!this.store) {
40097             throw "can not find store for combo";
40098         }
40099         
40100         this.store = Roo.factory(this.store, Roo.data);
40101         this.store.parent = this;
40102         
40103         this.createList();
40104         
40105         this.triggerEl = this.el.select('.input-group-addon', true).first();
40106         
40107         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40108         
40109         var _this = this;
40110         
40111         (function(){
40112             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40113             _this.list.setWidth(lw);
40114         }).defer(100);
40115         
40116         this.list.on('mouseover', this.onViewOver, this);
40117         this.list.on('mousemove', this.onViewMove, this);
40118         this.list.on('scroll', this.onViewScroll, this);
40119         
40120         if(!this.tpl){
40121             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40122         }
40123         
40124         this.view = new Roo.View(this.list, this.tpl, {
40125             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40126         });
40127         
40128         this.view.on('click', this.onViewClick, this);
40129         
40130         this.store.on('beforeload', this.onBeforeLoad, this);
40131         this.store.on('load', this.onLoad, this);
40132         this.store.on('loadexception', this.onLoadException, this);
40133         
40134         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40135             "up" : function(e){
40136                 this.inKeyMode = true;
40137                 this.selectPrev();
40138             },
40139
40140             "down" : function(e){
40141                 if(!this.isExpanded()){
40142                     this.onTriggerClick();
40143                 }else{
40144                     this.inKeyMode = true;
40145                     this.selectNext();
40146                 }
40147             },
40148
40149             "enter" : function(e){
40150                 this.collapse();
40151                 
40152                 if(this.fireEvent("specialkey", this, e)){
40153                     this.onViewClick(false);
40154                 }
40155                 
40156                 return true;
40157             },
40158
40159             "esc" : function(e){
40160                 this.collapse();
40161             },
40162
40163             "tab" : function(e){
40164                 this.collapse();
40165                 
40166                 if(this.fireEvent("specialkey", this, e)){
40167                     this.onViewClick(false);
40168                 }
40169                 
40170                 return true;
40171             },
40172
40173             scope : this,
40174
40175             doRelay : function(foo, bar, hname){
40176                 if(hname == 'down' || this.scope.isExpanded()){
40177                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40178                 }
40179                 return true;
40180             },
40181
40182             forceKeyDown: true
40183         });
40184         
40185         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40186         
40187     },
40188     
40189     initNumberEvent : function(e)
40190     {
40191         this.inputEl().on("keydown" , this.fireKey,  this);
40192         this.inputEl().on("focus", this.onFocus,  this);
40193         this.inputEl().on("blur", this.onBlur,  this);
40194         
40195         this.inputEl().relayEvent('keyup', this);
40196         
40197         if(this.indicator){
40198             this.indicator.addClass('invisible');
40199         }
40200  
40201         this.originalValue = this.getValue();
40202         
40203         if(this.validationEvent == 'keyup'){
40204             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40205             this.inputEl().on('keyup', this.filterValidation, this);
40206         }
40207         else if(this.validationEvent !== false){
40208             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40209         }
40210         
40211         if(this.selectOnFocus){
40212             this.on("focus", this.preFocus, this);
40213             
40214         }
40215         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40216             this.inputEl().on("keypress", this.filterKeys, this);
40217         } else {
40218             this.inputEl().relayEvent('keypress', this);
40219         }
40220         
40221         var allowed = "0123456789";
40222         
40223         if(this.allowDecimals){
40224             allowed += this.decimalSeparator;
40225         }
40226         
40227         if(this.allowNegative){
40228             allowed += "-";
40229         }
40230         
40231         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40232         
40233         var keyPress = function(e){
40234             
40235             var k = e.getKey();
40236             
40237             var c = e.getCharCode();
40238             
40239             if(
40240                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40241                     allowed.indexOf(String.fromCharCode(c)) === -1
40242             ){
40243                 e.stopEvent();
40244                 return;
40245             }
40246             
40247             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40248                 return;
40249             }
40250             
40251             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40252                 e.stopEvent();
40253             }
40254         };
40255         
40256         this.inputEl().on("keypress", keyPress, this);
40257         
40258     },
40259     
40260     onTriggerClick : function(e)
40261     {   
40262         if(this.disabled){
40263             return;
40264         }
40265         
40266         this.page = 0;
40267         this.loadNext = false;
40268         
40269         if(this.isExpanded()){
40270             this.collapse();
40271             return;
40272         }
40273         
40274         this.hasFocus = true;
40275         
40276         if(this.triggerAction == 'all') {
40277             this.doQuery(this.allQuery, true);
40278             return;
40279         }
40280         
40281         this.doQuery(this.getRawValue());
40282     },
40283     
40284     getCurrency : function()
40285     {   
40286         var v = this.currencyEl().getValue();
40287         
40288         return v;
40289     },
40290     
40291     restrictHeight : function()
40292     {
40293         this.list.alignTo(this.currencyEl(), this.listAlign);
40294         this.list.alignTo(this.currencyEl(), this.listAlign);
40295     },
40296     
40297     onViewClick : function(view, doFocus, el, e)
40298     {
40299         var index = this.view.getSelectedIndexes()[0];
40300         
40301         var r = this.store.getAt(index);
40302         
40303         if(r){
40304             this.onSelect(r, index);
40305         }
40306     },
40307     
40308     onSelect : function(record, index){
40309         
40310         if(this.fireEvent('beforeselect', this, record, index) !== false){
40311         
40312             this.setFromCurrencyData(index > -1 ? record.data : false);
40313             
40314             this.collapse();
40315             
40316             this.fireEvent('select', this, record, index);
40317         }
40318     },
40319     
40320     setFromCurrencyData : function(o)
40321     {
40322         var currency = '';
40323         
40324         this.lastCurrency = o;
40325         
40326         if (this.currencyField) {
40327             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40328         } else {
40329             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40330         }
40331         
40332         this.lastSelectionText = currency;
40333         
40334         this.setCurrency(currency);
40335     },
40336     
40337     setFromData : function(o)
40338     {
40339         var c = {};
40340         
40341         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40342         
40343         this.setFromCurrencyData(c);
40344         
40345         var value = '';
40346         
40347         if (this.name) {
40348             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40349         } else {
40350             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40351         }
40352         
40353         this.setValue(value);
40354         
40355     },
40356     
40357     setCurrency : function(v)
40358     {   
40359         this.currencyValue = v;
40360         
40361         if(this.rendered){
40362             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40363             this.validate();
40364         }
40365     },
40366     
40367     setValue : function(v)
40368     {
40369         v = this.fixPrecision(v);
40370         
40371         v = String(v).replace(".", this.decimalSeparator);
40372         
40373         this.value = v;
40374         
40375         if(this.rendered){
40376             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40377             this.validate();
40378         }
40379     },
40380     
40381     getRawValue : function()
40382     {
40383         var v = this.inputEl().getValue();
40384         
40385         return v;
40386     },
40387     
40388     getValue : function()
40389     {
40390         return this.fixPrecision(this.parseValue(this.getRawValue()));
40391     },
40392     
40393     parseValue : function(value)
40394     {
40395         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40396         return isNaN(value) ? '' : value;
40397     },
40398     
40399     fixPrecision : function(value)
40400     {
40401         var nan = isNaN(value);
40402         
40403         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40404             return nan ? '' : value;
40405         }
40406         
40407         return parseFloat(value).toFixed(this.decimalPrecision);
40408     },
40409     
40410     decimalPrecisionFcn : function(v)
40411     {
40412         return Math.floor(v);
40413     },
40414     
40415     validateValue : function(value)
40416     {
40417         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40418             return false;
40419         }
40420         
40421         var num = this.parseValue(value);
40422         
40423         if(isNaN(num)){
40424             this.markInvalid(String.format(this.nanText, value));
40425             return false;
40426         }
40427         
40428         if(num < this.minValue){
40429             this.markInvalid(String.format(this.minText, this.minValue));
40430             return false;
40431         }
40432         
40433         if(num > this.maxValue){
40434             this.markInvalid(String.format(this.maxText, this.maxValue));
40435             return false;
40436         }
40437         
40438         return true;
40439     },
40440     
40441     validate : function()
40442     {
40443         if(this.disabled || this.allowBlank){
40444             this.markValid();
40445             return true;
40446         }
40447         
40448         var currency = this.getCurrency();
40449         
40450         if(this.validateValue(this.getRawValue()) && currency.length){
40451             this.markValid();
40452             return true;
40453         }
40454         
40455         this.markInvalid();
40456         return false;
40457     },
40458     
40459     getName: function()
40460     {
40461         return this.name;
40462     },
40463     
40464     beforeBlur : function()
40465     {
40466         if(!this.castInt){
40467             return;
40468         }
40469         
40470         var v = this.parseValue(this.getRawValue());
40471         
40472         if(v){
40473             this.setValue(v);
40474         }
40475     },
40476     
40477     onBlur : function()
40478     {
40479         this.beforeBlur();
40480         
40481         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40482             //this.el.removeClass(this.focusClass);
40483         }
40484         
40485         this.hasFocus = false;
40486         
40487         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40488             this.validate();
40489         }
40490         
40491         var v = this.getValue();
40492         
40493         if(String(v) !== String(this.startValue)){
40494             this.fireEvent('change', this, v, this.startValue);
40495         }
40496         
40497         this.fireEvent("blur", this);
40498     },
40499     
40500     inputEl : function()
40501     {
40502         return this.el.select('.roo-money-amount-input', true).first();
40503     },
40504     
40505     currencyEl : function()
40506     {
40507         return this.el.select('.roo-money-currency-input', true).first();
40508     }
40509     
40510 });