roojs-bootstrap.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     getItems : function()
8042     {
8043         var r=new Roo.util.MixedCollection(false, function(o){
8044             return o.id || (o.id = Roo.id());
8045         });
8046         var iter = function(el) {
8047             if (el.inputEl) {
8048                 r.add(el);
8049             }
8050             if (!el.items) {
8051                 return;
8052             }
8053             Roo.each(el.items,function(e) {
8054                 iter(e);
8055             });
8056
8057
8058         };
8059
8060         iter(this);
8061         return r;
8062
8063
8064
8065
8066     }
8067
8068 });
8069
8070 Roo.apply(Roo.bootstrap.Form, {
8071     
8072     popover : {
8073         
8074         padding : 5,
8075         
8076         isApplied : false,
8077         
8078         isMasked : false,
8079         
8080         form : false,
8081         
8082         target : false,
8083         
8084         toolTip : false,
8085         
8086         intervalID : false,
8087         
8088         maskEl : false,
8089         
8090         apply : function()
8091         {
8092             if(this.isApplied){
8093                 return;
8094             }
8095             
8096             this.maskEl = {
8097                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8098                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8099                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8100                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8101             };
8102             
8103             this.maskEl.top.enableDisplayMode("block");
8104             this.maskEl.left.enableDisplayMode("block");
8105             this.maskEl.bottom.enableDisplayMode("block");
8106             this.maskEl.right.enableDisplayMode("block");
8107             
8108             this.toolTip = new Roo.bootstrap.Tooltip({
8109                 cls : 'roo-form-error-popover',
8110                 alignment : {
8111                     'left' : ['r-l', [-2,0], 'right'],
8112                     'right' : ['l-r', [2,0], 'left'],
8113                     'bottom' : ['tl-bl', [0,2], 'top'],
8114                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8115                 }
8116             });
8117             
8118             this.toolTip.render(Roo.get(document.body));
8119
8120             this.toolTip.el.enableDisplayMode("block");
8121             
8122             Roo.get(document.body).on('click', function(){
8123                 this.unmask();
8124             }, this);
8125             
8126             Roo.get(document.body).on('touchstart', function(){
8127                 this.unmask();
8128             }, this);
8129             
8130             this.isApplied = true
8131         },
8132         
8133         mask : function(form, target)
8134         {
8135             this.form = form;
8136             
8137             this.target = target;
8138             
8139             if(!this.form.errorMask || !target.el){
8140                 return;
8141             }
8142             
8143             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8144             
8145             Roo.log(scrollable);
8146             
8147             var ot = this.target.el.calcOffsetsTo(scrollable);
8148             
8149             var scrollTo = ot[1] - this.form.maskOffset;
8150             
8151             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8152             
8153             scrollable.scrollTo('top', scrollTo);
8154             
8155             var box = this.target.el.getBox();
8156             Roo.log(box);
8157             var zIndex = Roo.bootstrap.Modal.zIndex++;
8158
8159             
8160             this.maskEl.top.setStyle('position', 'absolute');
8161             this.maskEl.top.setStyle('z-index', zIndex);
8162             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8163             this.maskEl.top.setLeft(0);
8164             this.maskEl.top.setTop(0);
8165             this.maskEl.top.show();
8166             
8167             this.maskEl.left.setStyle('position', 'absolute');
8168             this.maskEl.left.setStyle('z-index', zIndex);
8169             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8170             this.maskEl.left.setLeft(0);
8171             this.maskEl.left.setTop(box.y - this.padding);
8172             this.maskEl.left.show();
8173
8174             this.maskEl.bottom.setStyle('position', 'absolute');
8175             this.maskEl.bottom.setStyle('z-index', zIndex);
8176             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8177             this.maskEl.bottom.setLeft(0);
8178             this.maskEl.bottom.setTop(box.bottom + this.padding);
8179             this.maskEl.bottom.show();
8180
8181             this.maskEl.right.setStyle('position', 'absolute');
8182             this.maskEl.right.setStyle('z-index', zIndex);
8183             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8184             this.maskEl.right.setLeft(box.right + this.padding);
8185             this.maskEl.right.setTop(box.y - this.padding);
8186             this.maskEl.right.show();
8187
8188             this.toolTip.bindEl = this.target.el;
8189
8190             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8191
8192             var tip = this.target.blankText;
8193
8194             if(this.target.getValue() !== '' ) {
8195                 
8196                 if (this.target.invalidText.length) {
8197                     tip = this.target.invalidText;
8198                 } else if (this.target.regexText.length){
8199                     tip = this.target.regexText;
8200                 }
8201             }
8202
8203             this.toolTip.show(tip);
8204
8205             this.intervalID = window.setInterval(function() {
8206                 Roo.bootstrap.Form.popover.unmask();
8207             }, 10000);
8208
8209             window.onwheel = function(){ return false;};
8210             
8211             (function(){ this.isMasked = true; }).defer(500, this);
8212             
8213         },
8214         
8215         unmask : function()
8216         {
8217             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8218                 return;
8219             }
8220             
8221             this.maskEl.top.setStyle('position', 'absolute');
8222             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8223             this.maskEl.top.hide();
8224
8225             this.maskEl.left.setStyle('position', 'absolute');
8226             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8227             this.maskEl.left.hide();
8228
8229             this.maskEl.bottom.setStyle('position', 'absolute');
8230             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8231             this.maskEl.bottom.hide();
8232
8233             this.maskEl.right.setStyle('position', 'absolute');
8234             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8235             this.maskEl.right.hide();
8236             
8237             this.toolTip.hide();
8238             
8239             this.toolTip.el.hide();
8240             
8241             window.onwheel = function(){ return true;};
8242             
8243             if(this.intervalID){
8244                 window.clearInterval(this.intervalID);
8245                 this.intervalID = false;
8246             }
8247             
8248             this.isMasked = false;
8249             
8250         }
8251         
8252     }
8253     
8254 });
8255
8256 /*
8257  * Based on:
8258  * Ext JS Library 1.1.1
8259  * Copyright(c) 2006-2007, Ext JS, LLC.
8260  *
8261  * Originally Released Under LGPL - original licence link has changed is not relivant.
8262  *
8263  * Fork - LGPL
8264  * <script type="text/javascript">
8265  */
8266 /**
8267  * @class Roo.form.VTypes
8268  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8269  * @singleton
8270  */
8271 Roo.form.VTypes = function(){
8272     // closure these in so they are only created once.
8273     var alpha = /^[a-zA-Z_]+$/;
8274     var alphanum = /^[a-zA-Z0-9_]+$/;
8275     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8276     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8277
8278     // All these messages and functions are configurable
8279     return {
8280         /**
8281          * The function used to validate email addresses
8282          * @param {String} value The email address
8283          */
8284         'email' : function(v){
8285             return email.test(v);
8286         },
8287         /**
8288          * The error text to display when the email validation function returns false
8289          * @type String
8290          */
8291         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8292         /**
8293          * The keystroke filter mask to be applied on email input
8294          * @type RegExp
8295          */
8296         'emailMask' : /[a-z0-9_\.\-@]/i,
8297
8298         /**
8299          * The function used to validate URLs
8300          * @param {String} value The URL
8301          */
8302         'url' : function(v){
8303             return url.test(v);
8304         },
8305         /**
8306          * The error text to display when the url validation function returns false
8307          * @type String
8308          */
8309         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8310         
8311         /**
8312          * The function used to validate alpha values
8313          * @param {String} value The value
8314          */
8315         'alpha' : function(v){
8316             return alpha.test(v);
8317         },
8318         /**
8319          * The error text to display when the alpha validation function returns false
8320          * @type String
8321          */
8322         'alphaText' : 'This field should only contain letters and _',
8323         /**
8324          * The keystroke filter mask to be applied on alpha input
8325          * @type RegExp
8326          */
8327         'alphaMask' : /[a-z_]/i,
8328
8329         /**
8330          * The function used to validate alphanumeric values
8331          * @param {String} value The value
8332          */
8333         'alphanum' : function(v){
8334             return alphanum.test(v);
8335         },
8336         /**
8337          * The error text to display when the alphanumeric validation function returns false
8338          * @type String
8339          */
8340         'alphanumText' : 'This field should only contain letters, numbers and _',
8341         /**
8342          * The keystroke filter mask to be applied on alphanumeric input
8343          * @type RegExp
8344          */
8345         'alphanumMask' : /[a-z0-9_]/i
8346     };
8347 }();/*
8348  * - LGPL
8349  *
8350  * Input
8351  * 
8352  */
8353
8354 /**
8355  * @class Roo.bootstrap.Input
8356  * @extends Roo.bootstrap.Component
8357  * Bootstrap Input class
8358  * @cfg {Boolean} disabled is it disabled
8359  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8360  * @cfg {String} name name of the input
8361  * @cfg {string} fieldLabel - the label associated
8362  * @cfg {string} placeholder - placeholder to put in text.
8363  * @cfg {string}  before - input group add on before
8364  * @cfg {string} after - input group add on after
8365  * @cfg {string} size - (lg|sm) or leave empty..
8366  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8367  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8368  * @cfg {Number} md colspan out of 12 for computer-sized screens
8369  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8370  * @cfg {string} value default value of the input
8371  * @cfg {Number} labelWidth set the width of label 
8372  * @cfg {Number} labellg set the width of label (1-12)
8373  * @cfg {Number} labelmd set the width of label (1-12)
8374  * @cfg {Number} labelsm set the width of label (1-12)
8375  * @cfg {Number} labelxs set the width of label (1-12)
8376  * @cfg {String} labelAlign (top|left)
8377  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8378  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8379  * @cfg {String} indicatorpos (left|right) default left
8380
8381  * @cfg {String} align (left|center|right) Default left
8382  * @cfg {Boolean} forceFeedback (true|false) Default false
8383  * 
8384  * 
8385  * 
8386  * 
8387  * @constructor
8388  * Create a new Input
8389  * @param {Object} config The config object
8390  */
8391
8392 Roo.bootstrap.Input = function(config){
8393     
8394     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8395     
8396     this.addEvents({
8397         /**
8398          * @event focus
8399          * Fires when this field receives input focus.
8400          * @param {Roo.form.Field} this
8401          */
8402         focus : true,
8403         /**
8404          * @event blur
8405          * Fires when this field loses input focus.
8406          * @param {Roo.form.Field} this
8407          */
8408         blur : true,
8409         /**
8410          * @event specialkey
8411          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8412          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8413          * @param {Roo.form.Field} this
8414          * @param {Roo.EventObject} e The event object
8415          */
8416         specialkey : true,
8417         /**
8418          * @event change
8419          * Fires just before the field blurs if the field value has changed.
8420          * @param {Roo.form.Field} this
8421          * @param {Mixed} newValue The new value
8422          * @param {Mixed} oldValue The original value
8423          */
8424         change : true,
8425         /**
8426          * @event invalid
8427          * Fires after the field has been marked as invalid.
8428          * @param {Roo.form.Field} this
8429          * @param {String} msg The validation message
8430          */
8431         invalid : true,
8432         /**
8433          * @event valid
8434          * Fires after the field has been validated with no errors.
8435          * @param {Roo.form.Field} this
8436          */
8437         valid : true,
8438          /**
8439          * @event keyup
8440          * Fires after the key up
8441          * @param {Roo.form.Field} this
8442          * @param {Roo.EventObject}  e The event Object
8443          */
8444         keyup : true
8445     });
8446 };
8447
8448 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8449      /**
8450      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8451       automatic validation (defaults to "keyup").
8452      */
8453     validationEvent : "keyup",
8454      /**
8455      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8456      */
8457     validateOnBlur : true,
8458     /**
8459      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8460      */
8461     validationDelay : 250,
8462      /**
8463      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8464      */
8465     focusClass : "x-form-focus",  // not needed???
8466     
8467        
8468     /**
8469      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8470      */
8471     invalidClass : "has-warning",
8472     
8473     /**
8474      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8475      */
8476     validClass : "has-success",
8477     
8478     /**
8479      * @cfg {Boolean} hasFeedback (true|false) default true
8480      */
8481     hasFeedback : true,
8482     
8483     /**
8484      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8485      */
8486     invalidFeedbackClass : "glyphicon-warning-sign",
8487     
8488     /**
8489      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8490      */
8491     validFeedbackClass : "glyphicon-ok",
8492     
8493     /**
8494      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8495      */
8496     selectOnFocus : false,
8497     
8498      /**
8499      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8500      */
8501     maskRe : null,
8502        /**
8503      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8504      */
8505     vtype : null,
8506     
8507       /**
8508      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8509      */
8510     disableKeyFilter : false,
8511     
8512        /**
8513      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8514      */
8515     disabled : false,
8516      /**
8517      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8518      */
8519     allowBlank : true,
8520     /**
8521      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8522      */
8523     blankText : "Please complete this mandatory field",
8524     
8525      /**
8526      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8527      */
8528     minLength : 0,
8529     /**
8530      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8531      */
8532     maxLength : Number.MAX_VALUE,
8533     /**
8534      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8535      */
8536     minLengthText : "The minimum length for this field is {0}",
8537     /**
8538      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8539      */
8540     maxLengthText : "The maximum length for this field is {0}",
8541   
8542     
8543     /**
8544      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8545      * If available, this function will be called only after the basic validators all return true, and will be passed the
8546      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8547      */
8548     validator : null,
8549     /**
8550      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8551      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8552      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8553      */
8554     regex : null,
8555     /**
8556      * @cfg {String} regexText -- Depricated - use Invalid Text
8557      */
8558     regexText : "",
8559     
8560     /**
8561      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8562      */
8563     invalidText : "",
8564     
8565     
8566     
8567     autocomplete: false,
8568     
8569     
8570     fieldLabel : '',
8571     inputType : 'text',
8572     
8573     name : false,
8574     placeholder: false,
8575     before : false,
8576     after : false,
8577     size : false,
8578     hasFocus : false,
8579     preventMark: false,
8580     isFormField : true,
8581     value : '',
8582     labelWidth : 2,
8583     labelAlign : false,
8584     readOnly : false,
8585     align : false,
8586     formatedValue : false,
8587     forceFeedback : false,
8588     
8589     indicatorpos : 'left',
8590     
8591     labellg : 0,
8592     labelmd : 0,
8593     labelsm : 0,
8594     labelxs : 0,
8595     
8596     parentLabelAlign : function()
8597     {
8598         var parent = this;
8599         while (parent.parent()) {
8600             parent = parent.parent();
8601             if (typeof(parent.labelAlign) !='undefined') {
8602                 return parent.labelAlign;
8603             }
8604         }
8605         return 'left';
8606         
8607     },
8608     
8609     getAutoCreate : function()
8610     {
8611         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8612         
8613         var id = Roo.id();
8614         
8615         var cfg = {};
8616         
8617         if(this.inputType != 'hidden'){
8618             cfg.cls = 'form-group' //input-group
8619         }
8620         
8621         var input =  {
8622             tag: 'input',
8623             id : id,
8624             type : this.inputType,
8625             value : this.value,
8626             cls : 'form-control',
8627             placeholder : this.placeholder || '',
8628             autocomplete : this.autocomplete || 'new-password'
8629         };
8630         
8631         if(this.align){
8632             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8633         }
8634         
8635         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8636             input.maxLength = this.maxLength;
8637         }
8638         
8639         if (this.disabled) {
8640             input.disabled=true;
8641         }
8642         
8643         if (this.readOnly) {
8644             input.readonly=true;
8645         }
8646         
8647         if (this.name) {
8648             input.name = this.name;
8649         }
8650         
8651         if (this.size) {
8652             input.cls += ' input-' + this.size;
8653         }
8654         
8655         var settings=this;
8656         ['xs','sm','md','lg'].map(function(size){
8657             if (settings[size]) {
8658                 cfg.cls += ' col-' + size + '-' + settings[size];
8659             }
8660         });
8661         
8662         var inputblock = input;
8663         
8664         var feedback = {
8665             tag: 'span',
8666             cls: 'glyphicon form-control-feedback'
8667         };
8668             
8669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8670             
8671             inputblock = {
8672                 cls : 'has-feedback',
8673                 cn :  [
8674                     input,
8675                     feedback
8676                 ] 
8677             };  
8678         }
8679         
8680         if (this.before || this.after) {
8681             
8682             inputblock = {
8683                 cls : 'input-group',
8684                 cn :  [] 
8685             };
8686             
8687             if (this.before && typeof(this.before) == 'string') {
8688                 
8689                 inputblock.cn.push({
8690                     tag :'span',
8691                     cls : 'roo-input-before input-group-addon',
8692                     html : this.before
8693                 });
8694             }
8695             if (this.before && typeof(this.before) == 'object') {
8696                 this.before = Roo.factory(this.before);
8697                 
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-before input-group-' +
8701                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8702                 });
8703             }
8704             
8705             inputblock.cn.push(input);
8706             
8707             if (this.after && typeof(this.after) == 'string') {
8708                 inputblock.cn.push({
8709                     tag :'span',
8710                     cls : 'roo-input-after input-group-addon',
8711                     html : this.after
8712                 });
8713             }
8714             if (this.after && typeof(this.after) == 'object') {
8715                 this.after = Roo.factory(this.after);
8716                 
8717                 inputblock.cn.push({
8718                     tag :'span',
8719                     cls : 'roo-input-after input-group-' +
8720                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8721                 });
8722             }
8723             
8724             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8725                 inputblock.cls += ' has-feedback';
8726                 inputblock.cn.push(feedback);
8727             }
8728         };
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732             cfg.cls += ' roo-form-group-label-left';
8733             
8734             cfg.cn = [
8735                 {
8736                     tag : 'i',
8737                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8738                     tooltip : 'This field is required'
8739                 },
8740                 {
8741                     tag: 'label',
8742                     'for' :  id,
8743                     cls : 'control-label',
8744                     html : this.fieldLabel
8745
8746                 },
8747                 {
8748                     cls : "", 
8749                     cn: [
8750                         inputblock
8751                     ]
8752                 }
8753             ];
8754             
8755             var labelCfg = cfg.cn[1];
8756             var contentCfg = cfg.cn[2];
8757             
8758             if(this.indicatorpos == 'right'){
8759                 cfg.cn = [
8760                     {
8761                         tag: 'label',
8762                         'for' :  id,
8763                         cls : 'control-label',
8764                         cn : [
8765                             {
8766                                 tag : 'span',
8767                                 html : this.fieldLabel
8768                             },
8769                             {
8770                                 tag : 'i',
8771                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8772                                 tooltip : 'This field is required'
8773                             }
8774                         ]
8775                     },
8776                     {
8777                         cls : "",
8778                         cn: [
8779                             inputblock
8780                         ]
8781                     }
8782
8783                 ];
8784                 
8785                 labelCfg = cfg.cn[0];
8786                 contentCfg = cfg.cn[1];
8787             
8788             }
8789             
8790             if(this.labelWidth > 12){
8791                 labelCfg.style = "width: " + this.labelWidth + 'px';
8792             }
8793             
8794             if(this.labelWidth < 13 && this.labelmd == 0){
8795                 this.labelmd = this.labelWidth;
8796             }
8797             
8798             if(this.labellg > 0){
8799                 labelCfg.cls += ' col-lg-' + this.labellg;
8800                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8801             }
8802             
8803             if(this.labelmd > 0){
8804                 labelCfg.cls += ' col-md-' + this.labelmd;
8805                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8806             }
8807             
8808             if(this.labelsm > 0){
8809                 labelCfg.cls += ' col-sm-' + this.labelsm;
8810                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8811             }
8812             
8813             if(this.labelxs > 0){
8814                 labelCfg.cls += ' col-xs-' + this.labelxs;
8815                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8816             }
8817             
8818             
8819         } else if ( this.fieldLabel.length) {
8820                 
8821             cfg.cn = [
8822                 {
8823                     tag : 'i',
8824                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8825                     tooltip : 'This field is required'
8826                 },
8827                 {
8828                     tag: 'label',
8829                    //cls : 'input-group-addon',
8830                     html : this.fieldLabel
8831
8832                 },
8833
8834                inputblock
8835
8836            ];
8837            
8838            if(this.indicatorpos == 'right'){
8839                 
8840                 cfg.cn = [
8841                     {
8842                         tag: 'label',
8843                        //cls : 'input-group-addon',
8844                         html : this.fieldLabel
8845
8846                     },
8847                     {
8848                         tag : 'i',
8849                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8850                         tooltip : 'This field is required'
8851                     },
8852
8853                    inputblock
8854
8855                ];
8856
8857             }
8858
8859         } else {
8860             
8861             cfg.cn = [
8862
8863                     inputblock
8864
8865             ];
8866                 
8867                 
8868         };
8869         
8870         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8871            cfg.cls += ' navbar-form';
8872         }
8873         
8874         if (this.parentType === 'NavGroup') {
8875            cfg.cls += ' navbar-form';
8876            cfg.tag = 'li';
8877         }
8878         
8879         return cfg;
8880         
8881     },
8882     /**
8883      * return the real input element.
8884      */
8885     inputEl: function ()
8886     {
8887         return this.el.select('input.form-control',true).first();
8888     },
8889     
8890     tooltipEl : function()
8891     {
8892         return this.inputEl();
8893     },
8894     
8895     indicatorEl : function()
8896     {
8897         var indicator = this.el.select('i.roo-required-indicator',true).first();
8898         
8899         if(!indicator){
8900             return false;
8901         }
8902         
8903         return indicator;
8904         
8905     },
8906     
8907     setDisabled : function(v)
8908     {
8909         var i  = this.inputEl().dom;
8910         if (!v) {
8911             i.removeAttribute('disabled');
8912             return;
8913             
8914         }
8915         i.setAttribute('disabled','true');
8916     },
8917     initEvents : function()
8918     {
8919           
8920         this.inputEl().on("keydown" , this.fireKey,  this);
8921         this.inputEl().on("focus", this.onFocus,  this);
8922         this.inputEl().on("blur", this.onBlur,  this);
8923         
8924         this.inputEl().relayEvent('keyup', this);
8925         
8926         this.indicator = this.indicatorEl();
8927         
8928         if(this.indicator){
8929             this.indicator.addClass('invisible');
8930             
8931         }
8932  
8933         // reference to original value for reset
8934         this.originalValue = this.getValue();
8935         //Roo.form.TextField.superclass.initEvents.call(this);
8936         if(this.validationEvent == 'keyup'){
8937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8938             this.inputEl().on('keyup', this.filterValidation, this);
8939         }
8940         else if(this.validationEvent !== false){
8941             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8942         }
8943         
8944         if(this.selectOnFocus){
8945             this.on("focus", this.preFocus, this);
8946             
8947         }
8948         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8949             this.inputEl().on("keypress", this.filterKeys, this);
8950         } else {
8951             this.inputEl().relayEvent('keypress', this);
8952         }
8953        /* if(this.grow){
8954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8955             this.el.on("click", this.autoSize,  this);
8956         }
8957         */
8958         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8959             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8960         }
8961         
8962         if (typeof(this.before) == 'object') {
8963             this.before.render(this.el.select('.roo-input-before',true).first());
8964         }
8965         if (typeof(this.after) == 'object') {
8966             this.after.render(this.el.select('.roo-input-after',true).first());
8967         }
8968         
8969         
8970     },
8971     filterValidation : function(e){
8972         if(!e.isNavKeyPress()){
8973             this.validationTask.delay(this.validationDelay);
8974         }
8975     },
8976      /**
8977      * Validates the field value
8978      * @return {Boolean} True if the value is valid, else false
8979      */
8980     validate : function(){
8981         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8982         if(this.disabled || this.validateValue(this.getRawValue())){
8983             this.markValid();
8984             return true;
8985         }
8986         
8987         this.markInvalid();
8988         return false;
8989     },
8990     
8991     
8992     /**
8993      * Validates a value according to the field's validation rules and marks the field as invalid
8994      * if the validation fails
8995      * @param {Mixed} value The value to validate
8996      * @return {Boolean} True if the value is valid, else false
8997      */
8998     validateValue : function(value){
8999         if(value.length < 1)  { // if it's blank
9000             if(this.allowBlank){
9001                 return true;
9002             }            
9003             return this.inputEl().hasClass('hide') ? true : false;
9004         }
9005         
9006         if(value.length < this.minLength){
9007             return false;
9008         }
9009         if(value.length > this.maxLength){
9010             return false;
9011         }
9012         if(this.vtype){
9013             var vt = Roo.form.VTypes;
9014             if(!vt[this.vtype](value, this)){
9015                 return false;
9016             }
9017         }
9018         if(typeof this.validator == "function"){
9019             var msg = this.validator(value);
9020             if(msg !== true){
9021                 return false;
9022             }
9023             if (typeof(msg) == 'string') {
9024                 this.invalidText = msg;
9025             }
9026         }
9027         
9028         if(this.regex && !this.regex.test(value)){
9029             return false;
9030         }
9031         
9032         return true;
9033     },
9034
9035     
9036     
9037      // private
9038     fireKey : function(e){
9039         //Roo.log('field ' + e.getKey());
9040         if(e.isNavKeyPress()){
9041             this.fireEvent("specialkey", this, e);
9042         }
9043     },
9044     focus : function (selectText){
9045         if(this.rendered){
9046             this.inputEl().focus();
9047             if(selectText === true){
9048                 this.inputEl().dom.select();
9049             }
9050         }
9051         return this;
9052     } ,
9053     
9054     onFocus : function(){
9055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9056            // this.el.addClass(this.focusClass);
9057         }
9058         if(!this.hasFocus){
9059             this.hasFocus = true;
9060             this.startValue = this.getValue();
9061             this.fireEvent("focus", this);
9062         }
9063     },
9064     
9065     beforeBlur : Roo.emptyFn,
9066
9067     
9068     // private
9069     onBlur : function(){
9070         this.beforeBlur();
9071         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9072             //this.el.removeClass(this.focusClass);
9073         }
9074         this.hasFocus = false;
9075         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9076             this.validate();
9077         }
9078         var v = this.getValue();
9079         if(String(v) !== String(this.startValue)){
9080             this.fireEvent('change', this, v, this.startValue);
9081         }
9082         this.fireEvent("blur", this);
9083     },
9084     
9085     /**
9086      * Resets the current field value to the originally loaded value and clears any validation messages
9087      */
9088     reset : function(){
9089         this.setValue(this.originalValue);
9090         this.validate();
9091     },
9092      /**
9093      * Returns the name of the field
9094      * @return {Mixed} name The name field
9095      */
9096     getName: function(){
9097         return this.name;
9098     },
9099      /**
9100      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getValue : function(){
9104         
9105         var v = this.inputEl().getValue();
9106         
9107         return v;
9108     },
9109     /**
9110      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9111      * @return {Mixed} value The field value
9112      */
9113     getRawValue : function(){
9114         var v = this.inputEl().getValue();
9115         
9116         return v;
9117     },
9118     
9119     /**
9120      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9121      * @param {Mixed} value The value to set
9122      */
9123     setRawValue : function(v){
9124         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9125     },
9126     
9127     selectText : function(start, end){
9128         var v = this.getRawValue();
9129         if(v.length > 0){
9130             start = start === undefined ? 0 : start;
9131             end = end === undefined ? v.length : end;
9132             var d = this.inputEl().dom;
9133             if(d.setSelectionRange){
9134                 d.setSelectionRange(start, end);
9135             }else if(d.createTextRange){
9136                 var range = d.createTextRange();
9137                 range.moveStart("character", start);
9138                 range.moveEnd("character", v.length-end);
9139                 range.select();
9140             }
9141         }
9142     },
9143     
9144     /**
9145      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9146      * @param {Mixed} value The value to set
9147      */
9148     setValue : function(v){
9149         this.value = v;
9150         if(this.rendered){
9151             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9152             this.validate();
9153         }
9154     },
9155     
9156     /*
9157     processValue : function(value){
9158         if(this.stripCharsRe){
9159             var newValue = value.replace(this.stripCharsRe, '');
9160             if(newValue !== value){
9161                 this.setRawValue(newValue);
9162                 return newValue;
9163             }
9164         }
9165         return value;
9166     },
9167   */
9168     preFocus : function(){
9169         
9170         if(this.selectOnFocus){
9171             this.inputEl().dom.select();
9172         }
9173     },
9174     filterKeys : function(e){
9175         var k = e.getKey();
9176         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9177             return;
9178         }
9179         var c = e.getCharCode(), cc = String.fromCharCode(c);
9180         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9181             return;
9182         }
9183         if(!this.maskRe.test(cc)){
9184             e.stopEvent();
9185         }
9186     },
9187      /**
9188      * Clear any invalid styles/messages for this field
9189      */
9190     clearInvalid : function(){
9191         
9192         if(!this.el || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196      
9197         this.el.removeClass(this.invalidClass);
9198         
9199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9200             
9201             var feedback = this.el.select('.form-control-feedback', true).first();
9202             
9203             if(feedback){
9204                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9205             }
9206             
9207         }
9208         
9209         this.fireEvent('valid', this);
9210     },
9211     
9212      /**
9213      * Mark this field as valid
9214      */
9215     markValid : function()
9216     {
9217         if(!this.el  || this.preventMark){ // not rendered...
9218             return;
9219         }
9220         
9221         this.el.removeClass([this.invalidClass, this.validClass]);
9222         
9223         var feedback = this.el.select('.form-control-feedback', true).first();
9224             
9225         if(feedback){
9226             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9227         }
9228
9229         if(this.disabled){
9230             return;
9231         }
9232         
9233         if(this.allowBlank && !this.getRawValue().length){
9234             return;
9235         }
9236         
9237         if(this.indicator){
9238             this.indicator.removeClass('visible');
9239             this.indicator.addClass('invisible');
9240         }
9241         
9242         this.el.addClass(this.validClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9251             }
9252             
9253         }
9254         
9255         this.fireEvent('valid', this);
9256     },
9257     
9258      /**
9259      * Mark this field as invalid
9260      * @param {String} msg The validation message
9261      */
9262     markInvalid : function(msg)
9263     {
9264         if(!this.el  || this.preventMark){ // not rendered
9265             return;
9266         }
9267         
9268         this.el.removeClass([this.invalidClass, this.validClass]);
9269         
9270         var feedback = this.el.select('.form-control-feedback', true).first();
9271             
9272         if(feedback){
9273             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274         }
9275
9276         if(this.disabled){
9277             return;
9278         }
9279         
9280         if(this.allowBlank && !this.getRawValue().length){
9281             return;
9282         }
9283         
9284         if(this.indicator){
9285             this.indicator.removeClass('invisible');
9286             this.indicator.addClass('visible');
9287         }
9288         
9289         this.el.addClass(this.invalidClass);
9290         
9291         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9292             
9293             var feedback = this.el.select('.form-control-feedback', true).first();
9294             
9295             if(feedback){
9296                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9297                 
9298                 if(this.getValue().length || this.forceFeedback){
9299                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9300                 }
9301                 
9302             }
9303             
9304         }
9305         
9306         this.fireEvent('invalid', this, msg);
9307     },
9308     // private
9309     SafariOnKeyDown : function(event)
9310     {
9311         // this is a workaround for a password hang bug on chrome/ webkit.
9312         if (this.inputEl().dom.type != 'password') {
9313             return;
9314         }
9315         
9316         var isSelectAll = false;
9317         
9318         if(this.inputEl().dom.selectionEnd > 0){
9319             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9320         }
9321         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9322             event.preventDefault();
9323             this.setValue('');
9324             return;
9325         }
9326         
9327         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9328             
9329             event.preventDefault();
9330             // this is very hacky as keydown always get's upper case.
9331             //
9332             var cc = String.fromCharCode(event.getCharCode());
9333             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9334             
9335         }
9336     },
9337     adjustWidth : function(tag, w){
9338         tag = tag.toLowerCase();
9339         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9340             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9341                 if(tag == 'input'){
9342                     return w + 2;
9343                 }
9344                 if(tag == 'textarea'){
9345                     return w-2;
9346                 }
9347             }else if(Roo.isOpera){
9348                 if(tag == 'input'){
9349                     return w + 2;
9350                 }
9351                 if(tag == 'textarea'){
9352                     return w-2;
9353                 }
9354             }
9355         }
9356         return w;
9357     },
9358     
9359     setFieldLabel : function(v)
9360     {
9361         if(!this.rendered){
9362             return;
9363         }
9364         
9365         this.fieldLabel = v;
9366         
9367         if(this.indicator){
9368             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9369             return;
9370         }
9371         
9372         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9373     }
9374 });
9375
9376  
9377 /*
9378  * - LGPL
9379  *
9380  * Input
9381  * 
9382  */
9383
9384 /**
9385  * @class Roo.bootstrap.TextArea
9386  * @extends Roo.bootstrap.Input
9387  * Bootstrap TextArea class
9388  * @cfg {Number} cols Specifies the visible width of a text area
9389  * @cfg {Number} rows Specifies the visible number of lines in a text area
9390  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9391  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9392  * @cfg {string} html text
9393  * 
9394  * @constructor
9395  * Create a new TextArea
9396  * @param {Object} config The config object
9397  */
9398
9399 Roo.bootstrap.TextArea = function(config){
9400     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9401    
9402 };
9403
9404 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9405      
9406     cols : false,
9407     rows : 5,
9408     readOnly : false,
9409     warp : 'soft',
9410     resize : false,
9411     value: false,
9412     html: false,
9413     
9414     getAutoCreate : function(){
9415         
9416         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9417         
9418         var id = Roo.id();
9419         
9420         var cfg = {};
9421         
9422         if(this.inputType != 'hidden'){
9423             cfg.cls = 'form-group' //input-group
9424         }
9425         
9426         var input =  {
9427             tag: 'textarea',
9428             id : id,
9429             warp : this.warp,
9430             rows : this.rows,
9431             value : this.value || '',
9432             html: this.html || '',
9433             cls : 'form-control',
9434             placeholder : this.placeholder || '' 
9435             
9436         };
9437         
9438         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9439             input.maxLength = this.maxLength;
9440         }
9441         
9442         if(this.resize){
9443             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9444         }
9445         
9446         if(this.cols){
9447             input.cols = this.cols;
9448         }
9449         
9450         if (this.readOnly) {
9451             input.readonly = true;
9452         }
9453         
9454         if (this.name) {
9455             input.name = this.name;
9456         }
9457         
9458         if (this.size) {
9459             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9460         }
9461         
9462         var settings=this;
9463         ['xs','sm','md','lg'].map(function(size){
9464             if (settings[size]) {
9465                 cfg.cls += ' col-' + size + '-' + settings[size];
9466             }
9467         });
9468         
9469         var inputblock = input;
9470         
9471         if(this.hasFeedback && !this.allowBlank){
9472             
9473             var feedback = {
9474                 tag: 'span',
9475                 cls: 'glyphicon form-control-feedback'
9476             };
9477
9478             inputblock = {
9479                 cls : 'has-feedback',
9480                 cn :  [
9481                     input,
9482                     feedback
9483                 ] 
9484             };  
9485         }
9486         
9487         
9488         if (this.before || this.after) {
9489             
9490             inputblock = {
9491                 cls : 'input-group',
9492                 cn :  [] 
9493             };
9494             if (this.before) {
9495                 inputblock.cn.push({
9496                     tag :'span',
9497                     cls : 'input-group-addon',
9498                     html : this.before
9499                 });
9500             }
9501             
9502             inputblock.cn.push(input);
9503             
9504             if(this.hasFeedback && !this.allowBlank){
9505                 inputblock.cls += ' has-feedback';
9506                 inputblock.cn.push(feedback);
9507             }
9508             
9509             if (this.after) {
9510                 inputblock.cn.push({
9511                     tag :'span',
9512                     cls : 'input-group-addon',
9513                     html : this.after
9514                 });
9515             }
9516             
9517         }
9518         
9519         if (align ==='left' && this.fieldLabel.length) {
9520             cfg.cn = [
9521                 {
9522                     tag: 'label',
9523                     'for' :  id,
9524                     cls : 'control-label',
9525                     html : this.fieldLabel
9526                 },
9527                 {
9528                     cls : "",
9529                     cn: [
9530                         inputblock
9531                     ]
9532                 }
9533
9534             ];
9535             
9536             if(this.labelWidth > 12){
9537                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9538             }
9539
9540             if(this.labelWidth < 13 && this.labelmd == 0){
9541                 this.labelmd = this.labelWidth;
9542             }
9543
9544             if(this.labellg > 0){
9545                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9546                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9547             }
9548
9549             if(this.labelmd > 0){
9550                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9551                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9552             }
9553
9554             if(this.labelsm > 0){
9555                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9556                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9557             }
9558
9559             if(this.labelxs > 0){
9560                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9561                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9562             }
9563             
9564         } else if ( this.fieldLabel.length) {
9565             cfg.cn = [
9566
9567                {
9568                    tag: 'label',
9569                    //cls : 'input-group-addon',
9570                    html : this.fieldLabel
9571
9572                },
9573
9574                inputblock
9575
9576            ];
9577
9578         } else {
9579
9580             cfg.cn = [
9581
9582                 inputblock
9583
9584             ];
9585                 
9586         }
9587         
9588         if (this.disabled) {
9589             input.disabled=true;
9590         }
9591         
9592         return cfg;
9593         
9594     },
9595     /**
9596      * return the real textarea element.
9597      */
9598     inputEl: function ()
9599     {
9600         return this.el.select('textarea.form-control',true).first();
9601     },
9602     
9603     /**
9604      * Clear any invalid styles/messages for this field
9605      */
9606     clearInvalid : function()
9607     {
9608         
9609         if(!this.el || this.preventMark){ // not rendered
9610             return;
9611         }
9612         
9613         var label = this.el.select('label', true).first();
9614         var icon = this.el.select('i.fa-star', true).first();
9615         
9616         if(label && icon){
9617             icon.remove();
9618         }
9619         
9620         this.el.removeClass(this.invalidClass);
9621         
9622         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9623             
9624             var feedback = this.el.select('.form-control-feedback', true).first();
9625             
9626             if(feedback){
9627                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9628             }
9629             
9630         }
9631         
9632         this.fireEvent('valid', this);
9633     },
9634     
9635      /**
9636      * Mark this field as valid
9637      */
9638     markValid : function()
9639     {
9640         if(!this.el  || this.preventMark){ // not rendered
9641             return;
9642         }
9643         
9644         this.el.removeClass([this.invalidClass, this.validClass]);
9645         
9646         var feedback = this.el.select('.form-control-feedback', true).first();
9647             
9648         if(feedback){
9649             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9650         }
9651
9652         if(this.disabled || this.allowBlank){
9653             return;
9654         }
9655         
9656         var label = this.el.select('label', true).first();
9657         var icon = this.el.select('i.fa-star', true).first();
9658         
9659         if(label && icon){
9660             icon.remove();
9661         }
9662         
9663         this.el.addClass(this.validClass);
9664         
9665         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9666             
9667             var feedback = this.el.select('.form-control-feedback', true).first();
9668             
9669             if(feedback){
9670                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9671                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9672             }
9673             
9674         }
9675         
9676         this.fireEvent('valid', this);
9677     },
9678     
9679      /**
9680      * Mark this field as invalid
9681      * @param {String} msg The validation message
9682      */
9683     markInvalid : function(msg)
9684     {
9685         if(!this.el  || this.preventMark){ // not rendered
9686             return;
9687         }
9688         
9689         this.el.removeClass([this.invalidClass, this.validClass]);
9690         
9691         var feedback = this.el.select('.form-control-feedback', true).first();
9692             
9693         if(feedback){
9694             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9695         }
9696
9697         if(this.disabled || this.allowBlank){
9698             return;
9699         }
9700         
9701         var label = this.el.select('label', true).first();
9702         var icon = this.el.select('i.fa-star', true).first();
9703         
9704         if(!this.getValue().length && label && !icon){
9705             this.el.createChild({
9706                 tag : 'i',
9707                 cls : 'text-danger fa fa-lg fa-star',
9708                 tooltip : 'This field is required',
9709                 style : 'margin-right:5px;'
9710             }, label, true);
9711         }
9712
9713         this.el.addClass(this.invalidClass);
9714         
9715         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9716             
9717             var feedback = this.el.select('.form-control-feedback', true).first();
9718             
9719             if(feedback){
9720                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9721                 
9722                 if(this.getValue().length || this.forceFeedback){
9723                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9724                 }
9725                 
9726             }
9727             
9728         }
9729         
9730         this.fireEvent('invalid', this, msg);
9731     }
9732 });
9733
9734  
9735 /*
9736  * - LGPL
9737  *
9738  * trigger field - base class for combo..
9739  * 
9740  */
9741  
9742 /**
9743  * @class Roo.bootstrap.TriggerField
9744  * @extends Roo.bootstrap.Input
9745  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9746  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9747  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9748  * for which you can provide a custom implementation.  For example:
9749  * <pre><code>
9750 var trigger = new Roo.bootstrap.TriggerField();
9751 trigger.onTriggerClick = myTriggerFn;
9752 trigger.applyTo('my-field');
9753 </code></pre>
9754  *
9755  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9756  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9757  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9758  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9759  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9760
9761  * @constructor
9762  * Create a new TriggerField.
9763  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9764  * to the base TextField)
9765  */
9766 Roo.bootstrap.TriggerField = function(config){
9767     this.mimicing = false;
9768     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9769 };
9770
9771 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9772     /**
9773      * @cfg {String} triggerClass A CSS class to apply to the trigger
9774      */
9775      /**
9776      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9777      */
9778     hideTrigger:false,
9779
9780     /**
9781      * @cfg {Boolean} removable (true|false) special filter default false
9782      */
9783     removable : false,
9784     
9785     /** @cfg {Boolean} grow @hide */
9786     /** @cfg {Number} growMin @hide */
9787     /** @cfg {Number} growMax @hide */
9788
9789     /**
9790      * @hide 
9791      * @method
9792      */
9793     autoSize: Roo.emptyFn,
9794     // private
9795     monitorTab : true,
9796     // private
9797     deferHeight : true,
9798
9799     
9800     actionMode : 'wrap',
9801     
9802     caret : false,
9803     
9804     
9805     getAutoCreate : function(){
9806        
9807         var align = this.labelAlign || this.parentLabelAlign();
9808         
9809         var id = Roo.id();
9810         
9811         var cfg = {
9812             cls: 'form-group' //input-group
9813         };
9814         
9815         
9816         var input =  {
9817             tag: 'input',
9818             id : id,
9819             type : this.inputType,
9820             cls : 'form-control',
9821             autocomplete: 'new-password',
9822             placeholder : this.placeholder || '' 
9823             
9824         };
9825         if (this.name) {
9826             input.name = this.name;
9827         }
9828         if (this.size) {
9829             input.cls += ' input-' + this.size;
9830         }
9831         
9832         if (this.disabled) {
9833             input.disabled=true;
9834         }
9835         
9836         var inputblock = input;
9837         
9838         if(this.hasFeedback && !this.allowBlank){
9839             
9840             var feedback = {
9841                 tag: 'span',
9842                 cls: 'glyphicon form-control-feedback'
9843             };
9844             
9845             if(this.removable && !this.editable && !this.tickable){
9846                 inputblock = {
9847                     cls : 'has-feedback',
9848                     cn :  [
9849                         inputblock,
9850                         {
9851                             tag: 'button',
9852                             html : 'x',
9853                             cls : 'roo-combo-removable-btn close'
9854                         },
9855                         feedback
9856                     ] 
9857                 };
9858             } else {
9859                 inputblock = {
9860                     cls : 'has-feedback',
9861                     cn :  [
9862                         inputblock,
9863                         feedback
9864                     ] 
9865                 };
9866             }
9867
9868         } else {
9869             if(this.removable && !this.editable && !this.tickable){
9870                 inputblock = {
9871                     cls : 'roo-removable',
9872                     cn :  [
9873                         inputblock,
9874                         {
9875                             tag: 'button',
9876                             html : 'x',
9877                             cls : 'roo-combo-removable-btn close'
9878                         }
9879                     ] 
9880                 };
9881             }
9882         }
9883         
9884         if (this.before || this.after) {
9885             
9886             inputblock = {
9887                 cls : 'input-group',
9888                 cn :  [] 
9889             };
9890             if (this.before) {
9891                 inputblock.cn.push({
9892                     tag :'span',
9893                     cls : 'input-group-addon',
9894                     html : this.before
9895                 });
9896             }
9897             
9898             inputblock.cn.push(input);
9899             
9900             if(this.hasFeedback && !this.allowBlank){
9901                 inputblock.cls += ' has-feedback';
9902                 inputblock.cn.push(feedback);
9903             }
9904             
9905             if (this.after) {
9906                 inputblock.cn.push({
9907                     tag :'span',
9908                     cls : 'input-group-addon',
9909                     html : this.after
9910                 });
9911             }
9912             
9913         };
9914         
9915         var box = {
9916             tag: 'div',
9917             cn: [
9918                 {
9919                     tag: 'input',
9920                     type : 'hidden',
9921                     cls: 'form-hidden-field'
9922                 },
9923                 inputblock
9924             ]
9925             
9926         };
9927         
9928         if(this.multiple){
9929             box = {
9930                 tag: 'div',
9931                 cn: [
9932                     {
9933                         tag: 'input',
9934                         type : 'hidden',
9935                         cls: 'form-hidden-field'
9936                     },
9937                     {
9938                         tag: 'ul',
9939                         cls: 'roo-select2-choices',
9940                         cn:[
9941                             {
9942                                 tag: 'li',
9943                                 cls: 'roo-select2-search-field',
9944                                 cn: [
9945
9946                                     inputblock
9947                                 ]
9948                             }
9949                         ]
9950                     }
9951                 ]
9952             }
9953         };
9954         
9955         var combobox = {
9956             cls: 'roo-select2-container input-group',
9957             cn: [
9958                 box
9959 //                {
9960 //                    tag: 'ul',
9961 //                    cls: 'typeahead typeahead-long dropdown-menu',
9962 //                    style: 'display:none'
9963 //                }
9964             ]
9965         };
9966         
9967         if(!this.multiple && this.showToggleBtn){
9968             
9969             var caret = {
9970                         tag: 'span',
9971                         cls: 'caret'
9972              };
9973             if (this.caret != false) {
9974                 caret = {
9975                      tag: 'i',
9976                      cls: 'fa fa-' + this.caret
9977                 };
9978                 
9979             }
9980             
9981             combobox.cn.push({
9982                 tag :'span',
9983                 cls : 'input-group-addon btn dropdown-toggle',
9984                 cn : [
9985                     caret,
9986                     {
9987                         tag: 'span',
9988                         cls: 'combobox-clear',
9989                         cn  : [
9990                             {
9991                                 tag : 'i',
9992                                 cls: 'icon-remove'
9993                             }
9994                         ]
9995                     }
9996                 ]
9997
9998             })
9999         }
10000         
10001         if(this.multiple){
10002             combobox.cls += ' roo-select2-container-multi';
10003         }
10004         
10005         if (align ==='left' && this.fieldLabel.length) {
10006             
10007             cfg.cls += ' roo-form-group-label-left';
10008
10009             cfg.cn = [
10010                 {
10011                     tag : 'i',
10012                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10013                     tooltip : 'This field is required'
10014                 },
10015                 {
10016                     tag: 'label',
10017                     'for' :  id,
10018                     cls : 'control-label',
10019                     html : this.fieldLabel
10020
10021                 },
10022                 {
10023                     cls : "", 
10024                     cn: [
10025                         combobox
10026                     ]
10027                 }
10028
10029             ];
10030             
10031             var labelCfg = cfg.cn[1];
10032             var contentCfg = cfg.cn[2];
10033             
10034             if(this.indicatorpos == 'right'){
10035                 cfg.cn = [
10036                     {
10037                         tag: 'label',
10038                         'for' :  id,
10039                         cls : 'control-label',
10040                         cn : [
10041                             {
10042                                 tag : 'span',
10043                                 html : this.fieldLabel
10044                             },
10045                             {
10046                                 tag : 'i',
10047                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10048                                 tooltip : 'This field is required'
10049                             }
10050                         ]
10051                     },
10052                     {
10053                         cls : "", 
10054                         cn: [
10055                             combobox
10056                         ]
10057                     }
10058
10059                 ];
10060                 
10061                 labelCfg = cfg.cn[0];
10062                 contentCfg = cfg.cn[1];
10063             }
10064             
10065             if(this.labelWidth > 12){
10066                 labelCfg.style = "width: " + this.labelWidth + 'px';
10067             }
10068             
10069             if(this.labelWidth < 13 && this.labelmd == 0){
10070                 this.labelmd = this.labelWidth;
10071             }
10072             
10073             if(this.labellg > 0){
10074                 labelCfg.cls += ' col-lg-' + this.labellg;
10075                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10076             }
10077             
10078             if(this.labelmd > 0){
10079                 labelCfg.cls += ' col-md-' + this.labelmd;
10080                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10081             }
10082             
10083             if(this.labelsm > 0){
10084                 labelCfg.cls += ' col-sm-' + this.labelsm;
10085                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10086             }
10087             
10088             if(this.labelxs > 0){
10089                 labelCfg.cls += ' col-xs-' + this.labelxs;
10090                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10091             }
10092             
10093         } else if ( this.fieldLabel.length) {
10094 //                Roo.log(" label");
10095             cfg.cn = [
10096                 {
10097                    tag : 'i',
10098                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10099                    tooltip : 'This field is required'
10100                },
10101                {
10102                    tag: 'label',
10103                    //cls : 'input-group-addon',
10104                    html : this.fieldLabel
10105
10106                },
10107
10108                combobox
10109
10110             ];
10111             
10112             if(this.indicatorpos == 'right'){
10113                 
10114                 cfg.cn = [
10115                     {
10116                        tag: 'label',
10117                        cn : [
10118                            {
10119                                tag : 'span',
10120                                html : this.fieldLabel
10121                            },
10122                            {
10123                               tag : 'i',
10124                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10125                               tooltip : 'This field is required'
10126                            }
10127                        ]
10128
10129                     },
10130                     combobox
10131
10132                 ];
10133
10134             }
10135
10136         } else {
10137             
10138 //                Roo.log(" no label && no align");
10139                 cfg = combobox
10140                      
10141                 
10142         }
10143         
10144         var settings=this;
10145         ['xs','sm','md','lg'].map(function(size){
10146             if (settings[size]) {
10147                 cfg.cls += ' col-' + size + '-' + settings[size];
10148             }
10149         });
10150         
10151         return cfg;
10152         
10153     },
10154     
10155     
10156     
10157     // private
10158     onResize : function(w, h){
10159 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10160 //        if(typeof w == 'number'){
10161 //            var x = w - this.trigger.getWidth();
10162 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10163 //            this.trigger.setStyle('left', x+'px');
10164 //        }
10165     },
10166
10167     // private
10168     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10169
10170     // private
10171     getResizeEl : function(){
10172         return this.inputEl();
10173     },
10174
10175     // private
10176     getPositionEl : function(){
10177         return this.inputEl();
10178     },
10179
10180     // private
10181     alignErrorIcon : function(){
10182         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10183     },
10184
10185     // private
10186     initEvents : function(){
10187         
10188         this.createList();
10189         
10190         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10191         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10192         if(!this.multiple && this.showToggleBtn){
10193             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10194             if(this.hideTrigger){
10195                 this.trigger.setDisplayed(false);
10196             }
10197             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10198         }
10199         
10200         if(this.multiple){
10201             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10202         }
10203         
10204         if(this.removable && !this.editable && !this.tickable){
10205             var close = this.closeTriggerEl();
10206             
10207             if(close){
10208                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10209                 close.on('click', this.removeBtnClick, this, close);
10210             }
10211         }
10212         
10213         //this.trigger.addClassOnOver('x-form-trigger-over');
10214         //this.trigger.addClassOnClick('x-form-trigger-click');
10215         
10216         //if(!this.width){
10217         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10218         //}
10219     },
10220     
10221     closeTriggerEl : function()
10222     {
10223         var close = this.el.select('.roo-combo-removable-btn', true).first();
10224         return close ? close : false;
10225     },
10226     
10227     removeBtnClick : function(e, h, el)
10228     {
10229         e.preventDefault();
10230         
10231         if(this.fireEvent("remove", this) !== false){
10232             this.reset();
10233             this.fireEvent("afterremove", this)
10234         }
10235     },
10236     
10237     createList : function()
10238     {
10239         this.list = Roo.get(document.body).createChild({
10240             tag: 'ul',
10241             cls: 'typeahead typeahead-long dropdown-menu',
10242             style: 'display:none'
10243         });
10244         
10245         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10246         
10247     },
10248
10249     // private
10250     initTrigger : function(){
10251        
10252     },
10253
10254     // private
10255     onDestroy : function(){
10256         if(this.trigger){
10257             this.trigger.removeAllListeners();
10258           //  this.trigger.remove();
10259         }
10260         //if(this.wrap){
10261         //    this.wrap.remove();
10262         //}
10263         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10264     },
10265
10266     // private
10267     onFocus : function(){
10268         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10269         /*
10270         if(!this.mimicing){
10271             this.wrap.addClass('x-trigger-wrap-focus');
10272             this.mimicing = true;
10273             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10274             if(this.monitorTab){
10275                 this.el.on("keydown", this.checkTab, this);
10276             }
10277         }
10278         */
10279     },
10280
10281     // private
10282     checkTab : function(e){
10283         if(e.getKey() == e.TAB){
10284             this.triggerBlur();
10285         }
10286     },
10287
10288     // private
10289     onBlur : function(){
10290         // do nothing
10291     },
10292
10293     // private
10294     mimicBlur : function(e, t){
10295         /*
10296         if(!this.wrap.contains(t) && this.validateBlur()){
10297             this.triggerBlur();
10298         }
10299         */
10300     },
10301
10302     // private
10303     triggerBlur : function(){
10304         this.mimicing = false;
10305         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10306         if(this.monitorTab){
10307             this.el.un("keydown", this.checkTab, this);
10308         }
10309         //this.wrap.removeClass('x-trigger-wrap-focus');
10310         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10311     },
10312
10313     // private
10314     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10315     validateBlur : function(e, t){
10316         return true;
10317     },
10318
10319     // private
10320     onDisable : function(){
10321         this.inputEl().dom.disabled = true;
10322         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10323         //if(this.wrap){
10324         //    this.wrap.addClass('x-item-disabled');
10325         //}
10326     },
10327
10328     // private
10329     onEnable : function(){
10330         this.inputEl().dom.disabled = false;
10331         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10332         //if(this.wrap){
10333         //    this.el.removeClass('x-item-disabled');
10334         //}
10335     },
10336
10337     // private
10338     onShow : function(){
10339         var ae = this.getActionEl();
10340         
10341         if(ae){
10342             ae.dom.style.display = '';
10343             ae.dom.style.visibility = 'visible';
10344         }
10345     },
10346
10347     // private
10348     
10349     onHide : function(){
10350         var ae = this.getActionEl();
10351         ae.dom.style.display = 'none';
10352     },
10353
10354     /**
10355      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10356      * by an implementing function.
10357      * @method
10358      * @param {EventObject} e
10359      */
10360     onTriggerClick : Roo.emptyFn
10361 });
10362  /*
10363  * Based on:
10364  * Ext JS Library 1.1.1
10365  * Copyright(c) 2006-2007, Ext JS, LLC.
10366  *
10367  * Originally Released Under LGPL - original licence link has changed is not relivant.
10368  *
10369  * Fork - LGPL
10370  * <script type="text/javascript">
10371  */
10372
10373
10374 /**
10375  * @class Roo.data.SortTypes
10376  * @singleton
10377  * Defines the default sorting (casting?) comparison functions used when sorting data.
10378  */
10379 Roo.data.SortTypes = {
10380     /**
10381      * Default sort that does nothing
10382      * @param {Mixed} s The value being converted
10383      * @return {Mixed} The comparison value
10384      */
10385     none : function(s){
10386         return s;
10387     },
10388     
10389     /**
10390      * The regular expression used to strip tags
10391      * @type {RegExp}
10392      * @property
10393      */
10394     stripTagsRE : /<\/?[^>]+>/gi,
10395     
10396     /**
10397      * Strips all HTML tags to sort on text only
10398      * @param {Mixed} s The value being converted
10399      * @return {String} The comparison value
10400      */
10401     asText : function(s){
10402         return String(s).replace(this.stripTagsRE, "");
10403     },
10404     
10405     /**
10406      * Strips all HTML tags to sort on text only - Case insensitive
10407      * @param {Mixed} s The value being converted
10408      * @return {String} The comparison value
10409      */
10410     asUCText : function(s){
10411         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10412     },
10413     
10414     /**
10415      * Case insensitive string
10416      * @param {Mixed} s The value being converted
10417      * @return {String} The comparison value
10418      */
10419     asUCString : function(s) {
10420         return String(s).toUpperCase();
10421     },
10422     
10423     /**
10424      * Date sorting
10425      * @param {Mixed} s The value being converted
10426      * @return {Number} The comparison value
10427      */
10428     asDate : function(s) {
10429         if(!s){
10430             return 0;
10431         }
10432         if(s instanceof Date){
10433             return s.getTime();
10434         }
10435         return Date.parse(String(s));
10436     },
10437     
10438     /**
10439      * Float sorting
10440      * @param {Mixed} s The value being converted
10441      * @return {Float} The comparison value
10442      */
10443     asFloat : function(s) {
10444         var val = parseFloat(String(s).replace(/,/g, ""));
10445         if(isNaN(val)) {
10446             val = 0;
10447         }
10448         return val;
10449     },
10450     
10451     /**
10452      * Integer sorting
10453      * @param {Mixed} s The value being converted
10454      * @return {Number} The comparison value
10455      */
10456     asInt : function(s) {
10457         var val = parseInt(String(s).replace(/,/g, ""));
10458         if(isNaN(val)) {
10459             val = 0;
10460         }
10461         return val;
10462     }
10463 };/*
10464  * Based on:
10465  * Ext JS Library 1.1.1
10466  * Copyright(c) 2006-2007, Ext JS, LLC.
10467  *
10468  * Originally Released Under LGPL - original licence link has changed is not relivant.
10469  *
10470  * Fork - LGPL
10471  * <script type="text/javascript">
10472  */
10473
10474 /**
10475 * @class Roo.data.Record
10476  * Instances of this class encapsulate both record <em>definition</em> information, and record
10477  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10478  * to access Records cached in an {@link Roo.data.Store} object.<br>
10479  * <p>
10480  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10481  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10482  * objects.<br>
10483  * <p>
10484  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10485  * @constructor
10486  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10487  * {@link #create}. The parameters are the same.
10488  * @param {Array} data An associative Array of data values keyed by the field name.
10489  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10490  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10491  * not specified an integer id is generated.
10492  */
10493 Roo.data.Record = function(data, id){
10494     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10495     this.data = data;
10496 };
10497
10498 /**
10499  * Generate a constructor for a specific record layout.
10500  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10501  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10502  * Each field definition object may contain the following properties: <ul>
10503  * <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,
10504  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10505  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10506  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10507  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10508  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10509  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10510  * this may be omitted.</p></li>
10511  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10512  * <ul><li>auto (Default, implies no conversion)</li>
10513  * <li>string</li>
10514  * <li>int</li>
10515  * <li>float</li>
10516  * <li>boolean</li>
10517  * <li>date</li></ul></p></li>
10518  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10519  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10520  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10521  * by the Reader into an object that will be stored in the Record. It is passed the
10522  * following parameters:<ul>
10523  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10524  * </ul></p></li>
10525  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10526  * </ul>
10527  * <br>usage:<br><pre><code>
10528 var TopicRecord = Roo.data.Record.create(
10529     {name: 'title', mapping: 'topic_title'},
10530     {name: 'author', mapping: 'username'},
10531     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10532     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10533     {name: 'lastPoster', mapping: 'user2'},
10534     {name: 'excerpt', mapping: 'post_text'}
10535 );
10536
10537 var myNewRecord = new TopicRecord({
10538     title: 'Do my job please',
10539     author: 'noobie',
10540     totalPosts: 1,
10541     lastPost: new Date(),
10542     lastPoster: 'Animal',
10543     excerpt: 'No way dude!'
10544 });
10545 myStore.add(myNewRecord);
10546 </code></pre>
10547  * @method create
10548  * @static
10549  */
10550 Roo.data.Record.create = function(o){
10551     var f = function(){
10552         f.superclass.constructor.apply(this, arguments);
10553     };
10554     Roo.extend(f, Roo.data.Record);
10555     var p = f.prototype;
10556     p.fields = new Roo.util.MixedCollection(false, function(field){
10557         return field.name;
10558     });
10559     for(var i = 0, len = o.length; i < len; i++){
10560         p.fields.add(new Roo.data.Field(o[i]));
10561     }
10562     f.getField = function(name){
10563         return p.fields.get(name);  
10564     };
10565     return f;
10566 };
10567
10568 Roo.data.Record.AUTO_ID = 1000;
10569 Roo.data.Record.EDIT = 'edit';
10570 Roo.data.Record.REJECT = 'reject';
10571 Roo.data.Record.COMMIT = 'commit';
10572
10573 Roo.data.Record.prototype = {
10574     /**
10575      * Readonly flag - true if this record has been modified.
10576      * @type Boolean
10577      */
10578     dirty : false,
10579     editing : false,
10580     error: null,
10581     modified: null,
10582
10583     // private
10584     join : function(store){
10585         this.store = store;
10586     },
10587
10588     /**
10589      * Set the named field to the specified value.
10590      * @param {String} name The name of the field to set.
10591      * @param {Object} value The value to set the field to.
10592      */
10593     set : function(name, value){
10594         if(this.data[name] == value){
10595             return;
10596         }
10597         this.dirty = true;
10598         if(!this.modified){
10599             this.modified = {};
10600         }
10601         if(typeof this.modified[name] == 'undefined'){
10602             this.modified[name] = this.data[name];
10603         }
10604         this.data[name] = value;
10605         if(!this.editing && this.store){
10606             this.store.afterEdit(this);
10607         }       
10608     },
10609
10610     /**
10611      * Get the value of the named field.
10612      * @param {String} name The name of the field to get the value of.
10613      * @return {Object} The value of the field.
10614      */
10615     get : function(name){
10616         return this.data[name]; 
10617     },
10618
10619     // private
10620     beginEdit : function(){
10621         this.editing = true;
10622         this.modified = {}; 
10623     },
10624
10625     // private
10626     cancelEdit : function(){
10627         this.editing = false;
10628         delete this.modified;
10629     },
10630
10631     // private
10632     endEdit : function(){
10633         this.editing = false;
10634         if(this.dirty && this.store){
10635             this.store.afterEdit(this);
10636         }
10637     },
10638
10639     /**
10640      * Usually called by the {@link Roo.data.Store} which owns the Record.
10641      * Rejects all changes made to the Record since either creation, or the last commit operation.
10642      * Modified fields are reverted to their original values.
10643      * <p>
10644      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10645      * of reject operations.
10646      */
10647     reject : function(){
10648         var m = this.modified;
10649         for(var n in m){
10650             if(typeof m[n] != "function"){
10651                 this.data[n] = m[n];
10652             }
10653         }
10654         this.dirty = false;
10655         delete this.modified;
10656         this.editing = false;
10657         if(this.store){
10658             this.store.afterReject(this);
10659         }
10660     },
10661
10662     /**
10663      * Usually called by the {@link Roo.data.Store} which owns the Record.
10664      * Commits all changes made to the Record since either creation, or the last commit operation.
10665      * <p>
10666      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10667      * of commit operations.
10668      */
10669     commit : function(){
10670         this.dirty = false;
10671         delete this.modified;
10672         this.editing = false;
10673         if(this.store){
10674             this.store.afterCommit(this);
10675         }
10676     },
10677
10678     // private
10679     hasError : function(){
10680         return this.error != null;
10681     },
10682
10683     // private
10684     clearError : function(){
10685         this.error = null;
10686     },
10687
10688     /**
10689      * Creates a copy of this record.
10690      * @param {String} id (optional) A new record id if you don't want to use this record's id
10691      * @return {Record}
10692      */
10693     copy : function(newId) {
10694         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10695     }
10696 };/*
10697  * Based on:
10698  * Ext JS Library 1.1.1
10699  * Copyright(c) 2006-2007, Ext JS, LLC.
10700  *
10701  * Originally Released Under LGPL - original licence link has changed is not relivant.
10702  *
10703  * Fork - LGPL
10704  * <script type="text/javascript">
10705  */
10706
10707
10708
10709 /**
10710  * @class Roo.data.Store
10711  * @extends Roo.util.Observable
10712  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10713  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10714  * <p>
10715  * 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
10716  * has no knowledge of the format of the data returned by the Proxy.<br>
10717  * <p>
10718  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10719  * instances from the data object. These records are cached and made available through accessor functions.
10720  * @constructor
10721  * Creates a new Store.
10722  * @param {Object} config A config object containing the objects needed for the Store to access data,
10723  * and read the data into Records.
10724  */
10725 Roo.data.Store = function(config){
10726     this.data = new Roo.util.MixedCollection(false);
10727     this.data.getKey = function(o){
10728         return o.id;
10729     };
10730     this.baseParams = {};
10731     // private
10732     this.paramNames = {
10733         "start" : "start",
10734         "limit" : "limit",
10735         "sort" : "sort",
10736         "dir" : "dir",
10737         "multisort" : "_multisort"
10738     };
10739
10740     if(config && config.data){
10741         this.inlineData = config.data;
10742         delete config.data;
10743     }
10744
10745     Roo.apply(this, config);
10746     
10747     if(this.reader){ // reader passed
10748         this.reader = Roo.factory(this.reader, Roo.data);
10749         this.reader.xmodule = this.xmodule || false;
10750         if(!this.recordType){
10751             this.recordType = this.reader.recordType;
10752         }
10753         if(this.reader.onMetaChange){
10754             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10755         }
10756     }
10757
10758     if(this.recordType){
10759         this.fields = this.recordType.prototype.fields;
10760     }
10761     this.modified = [];
10762
10763     this.addEvents({
10764         /**
10765          * @event datachanged
10766          * Fires when the data cache has changed, and a widget which is using this Store
10767          * as a Record cache should refresh its view.
10768          * @param {Store} this
10769          */
10770         datachanged : true,
10771         /**
10772          * @event metachange
10773          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10774          * @param {Store} this
10775          * @param {Object} meta The JSON metadata
10776          */
10777         metachange : true,
10778         /**
10779          * @event add
10780          * Fires when Records have been added to the Store
10781          * @param {Store} this
10782          * @param {Roo.data.Record[]} records The array of Records added
10783          * @param {Number} index The index at which the record(s) were added
10784          */
10785         add : true,
10786         /**
10787          * @event remove
10788          * Fires when a Record has been removed from the Store
10789          * @param {Store} this
10790          * @param {Roo.data.Record} record The Record that was removed
10791          * @param {Number} index The index at which the record was removed
10792          */
10793         remove : true,
10794         /**
10795          * @event update
10796          * Fires when a Record has been updated
10797          * @param {Store} this
10798          * @param {Roo.data.Record} record The Record that was updated
10799          * @param {String} operation The update operation being performed.  Value may be one of:
10800          * <pre><code>
10801  Roo.data.Record.EDIT
10802  Roo.data.Record.REJECT
10803  Roo.data.Record.COMMIT
10804          * </code></pre>
10805          */
10806         update : true,
10807         /**
10808          * @event clear
10809          * Fires when the data cache has been cleared.
10810          * @param {Store} this
10811          */
10812         clear : true,
10813         /**
10814          * @event beforeload
10815          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10816          * the load action will be canceled.
10817          * @param {Store} this
10818          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10819          */
10820         beforeload : true,
10821         /**
10822          * @event beforeloadadd
10823          * Fires after a new set of Records has been loaded.
10824          * @param {Store} this
10825          * @param {Roo.data.Record[]} records The Records that were loaded
10826          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10827          */
10828         beforeloadadd : true,
10829         /**
10830          * @event load
10831          * Fires after a new set of Records has been loaded, before they are added to the store.
10832          * @param {Store} this
10833          * @param {Roo.data.Record[]} records The Records that were loaded
10834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10835          * @params {Object} return from reader
10836          */
10837         load : true,
10838         /**
10839          * @event loadexception
10840          * Fires if an exception occurs in the Proxy during loading.
10841          * Called with the signature of the Proxy's "loadexception" event.
10842          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10843          * 
10844          * @param {Proxy} 
10845          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10846          * @param {Object} load options 
10847          * @param {Object} jsonData from your request (normally this contains the Exception)
10848          */
10849         loadexception : true
10850     });
10851     
10852     if(this.proxy){
10853         this.proxy = Roo.factory(this.proxy, Roo.data);
10854         this.proxy.xmodule = this.xmodule || false;
10855         this.relayEvents(this.proxy,  ["loadexception"]);
10856     }
10857     this.sortToggle = {};
10858     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10859
10860     Roo.data.Store.superclass.constructor.call(this);
10861
10862     if(this.inlineData){
10863         this.loadData(this.inlineData);
10864         delete this.inlineData;
10865     }
10866 };
10867
10868 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10869      /**
10870     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10871     * without a remote query - used by combo/forms at present.
10872     */
10873     
10874     /**
10875     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10876     */
10877     /**
10878     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10879     */
10880     /**
10881     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10882     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10883     */
10884     /**
10885     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10886     * on any HTTP request
10887     */
10888     /**
10889     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10890     */
10891     /**
10892     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10893     */
10894     multiSort: false,
10895     /**
10896     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10897     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10898     */
10899     remoteSort : false,
10900
10901     /**
10902     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10903      * loaded or when a record is removed. (defaults to false).
10904     */
10905     pruneModifiedRecords : false,
10906
10907     // private
10908     lastOptions : null,
10909
10910     /**
10911      * Add Records to the Store and fires the add event.
10912      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10913      */
10914     add : function(records){
10915         records = [].concat(records);
10916         for(var i = 0, len = records.length; i < len; i++){
10917             records[i].join(this);
10918         }
10919         var index = this.data.length;
10920         this.data.addAll(records);
10921         this.fireEvent("add", this, records, index);
10922     },
10923
10924     /**
10925      * Remove a Record from the Store and fires the remove event.
10926      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10927      */
10928     remove : function(record){
10929         var index = this.data.indexOf(record);
10930         this.data.removeAt(index);
10931         if(this.pruneModifiedRecords){
10932             this.modified.remove(record);
10933         }
10934         this.fireEvent("remove", this, record, index);
10935     },
10936
10937     /**
10938      * Remove all Records from the Store and fires the clear event.
10939      */
10940     removeAll : function(){
10941         this.data.clear();
10942         if(this.pruneModifiedRecords){
10943             this.modified = [];
10944         }
10945         this.fireEvent("clear", this);
10946     },
10947
10948     /**
10949      * Inserts Records to the Store at the given index and fires the add event.
10950      * @param {Number} index The start index at which to insert the passed Records.
10951      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10952      */
10953     insert : function(index, records){
10954         records = [].concat(records);
10955         for(var i = 0, len = records.length; i < len; i++){
10956             this.data.insert(index, records[i]);
10957             records[i].join(this);
10958         }
10959         this.fireEvent("add", this, records, index);
10960     },
10961
10962     /**
10963      * Get the index within the cache of the passed Record.
10964      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10965      * @return {Number} The index of the passed Record. Returns -1 if not found.
10966      */
10967     indexOf : function(record){
10968         return this.data.indexOf(record);
10969     },
10970
10971     /**
10972      * Get the index within the cache of the Record with the passed id.
10973      * @param {String} id The id of the Record to find.
10974      * @return {Number} The index of the Record. Returns -1 if not found.
10975      */
10976     indexOfId : function(id){
10977         return this.data.indexOfKey(id);
10978     },
10979
10980     /**
10981      * Get the Record with the specified id.
10982      * @param {String} id The id of the Record to find.
10983      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10984      */
10985     getById : function(id){
10986         return this.data.key(id);
10987     },
10988
10989     /**
10990      * Get the Record at the specified index.
10991      * @param {Number} index The index of the Record to find.
10992      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10993      */
10994     getAt : function(index){
10995         return this.data.itemAt(index);
10996     },
10997
10998     /**
10999      * Returns a range of Records between specified indices.
11000      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11001      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11002      * @return {Roo.data.Record[]} An array of Records
11003      */
11004     getRange : function(start, end){
11005         return this.data.getRange(start, end);
11006     },
11007
11008     // private
11009     storeOptions : function(o){
11010         o = Roo.apply({}, o);
11011         delete o.callback;
11012         delete o.scope;
11013         this.lastOptions = o;
11014     },
11015
11016     /**
11017      * Loads the Record cache from the configured Proxy using the configured Reader.
11018      * <p>
11019      * If using remote paging, then the first load call must specify the <em>start</em>
11020      * and <em>limit</em> properties in the options.params property to establish the initial
11021      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11022      * <p>
11023      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11024      * and this call will return before the new data has been loaded. Perform any post-processing
11025      * in a callback function, or in a "load" event handler.</strong>
11026      * <p>
11027      * @param {Object} options An object containing properties which control loading options:<ul>
11028      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11029      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11030      * passed the following arguments:<ul>
11031      * <li>r : Roo.data.Record[]</li>
11032      * <li>options: Options object from the load call</li>
11033      * <li>success: Boolean success indicator</li></ul></li>
11034      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11035      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11036      * </ul>
11037      */
11038     load : function(options){
11039         options = options || {};
11040         if(this.fireEvent("beforeload", this, options) !== false){
11041             this.storeOptions(options);
11042             var p = Roo.apply(options.params || {}, this.baseParams);
11043             // if meta was not loaded from remote source.. try requesting it.
11044             if (!this.reader.metaFromRemote) {
11045                 p._requestMeta = 1;
11046             }
11047             if(this.sortInfo && this.remoteSort){
11048                 var pn = this.paramNames;
11049                 p[pn["sort"]] = this.sortInfo.field;
11050                 p[pn["dir"]] = this.sortInfo.direction;
11051             }
11052             if (this.multiSort) {
11053                 var pn = this.paramNames;
11054                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11055             }
11056             
11057             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11058         }
11059     },
11060
11061     /**
11062      * Reloads the Record cache from the configured Proxy using the configured Reader and
11063      * the options from the last load operation performed.
11064      * @param {Object} options (optional) An object containing properties which may override the options
11065      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11066      * the most recently used options are reused).
11067      */
11068     reload : function(options){
11069         this.load(Roo.applyIf(options||{}, this.lastOptions));
11070     },
11071
11072     // private
11073     // Called as a callback by the Reader during a load operation.
11074     loadRecords : function(o, options, success){
11075         if(!o || success === false){
11076             if(success !== false){
11077                 this.fireEvent("load", this, [], options, o);
11078             }
11079             if(options.callback){
11080                 options.callback.call(options.scope || this, [], options, false);
11081             }
11082             return;
11083         }
11084         // if data returned failure - throw an exception.
11085         if (o.success === false) {
11086             // show a message if no listener is registered.
11087             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11088                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11089             }
11090             // loadmask wil be hooked into this..
11091             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11092             return;
11093         }
11094         var r = o.records, t = o.totalRecords || r.length;
11095         
11096         this.fireEvent("beforeloadadd", this, r, options, o);
11097         
11098         if(!options || options.add !== true){
11099             if(this.pruneModifiedRecords){
11100                 this.modified = [];
11101             }
11102             for(var i = 0, len = r.length; i < len; i++){
11103                 r[i].join(this);
11104             }
11105             if(this.snapshot){
11106                 this.data = this.snapshot;
11107                 delete this.snapshot;
11108             }
11109             this.data.clear();
11110             this.data.addAll(r);
11111             this.totalLength = t;
11112             this.applySort();
11113             this.fireEvent("datachanged", this);
11114         }else{
11115             this.totalLength = Math.max(t, this.data.length+r.length);
11116             this.add(r);
11117         }
11118         
11119         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11120                 
11121             var e = new Roo.data.Record({});
11122
11123             e.set(this.parent.displayField, this.parent.emptyTitle);
11124             e.set(this.parent.valueField, '');
11125
11126             this.insert(0, e);
11127         }
11128             
11129         this.fireEvent("load", this, r, options, o);
11130         if(options.callback){
11131             options.callback.call(options.scope || this, r, options, true);
11132         }
11133     },
11134
11135
11136     /**
11137      * Loads data from a passed data block. A Reader which understands the format of the data
11138      * must have been configured in the constructor.
11139      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11140      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11141      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11142      */
11143     loadData : function(o, append){
11144         var r = this.reader.readRecords(o);
11145         this.loadRecords(r, {add: append}, true);
11146     },
11147
11148     /**
11149      * Gets the number of cached records.
11150      * <p>
11151      * <em>If using paging, this may not be the total size of the dataset. If the data object
11152      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11153      * the data set size</em>
11154      */
11155     getCount : function(){
11156         return this.data.length || 0;
11157     },
11158
11159     /**
11160      * Gets the total number of records in the dataset as returned by the server.
11161      * <p>
11162      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11163      * the dataset size</em>
11164      */
11165     getTotalCount : function(){
11166         return this.totalLength || 0;
11167     },
11168
11169     /**
11170      * Returns the sort state of the Store as an object with two properties:
11171      * <pre><code>
11172  field {String} The name of the field by which the Records are sorted
11173  direction {String} The sort order, "ASC" or "DESC"
11174      * </code></pre>
11175      */
11176     getSortState : function(){
11177         return this.sortInfo;
11178     },
11179
11180     // private
11181     applySort : function(){
11182         if(this.sortInfo && !this.remoteSort){
11183             var s = this.sortInfo, f = s.field;
11184             var st = this.fields.get(f).sortType;
11185             var fn = function(r1, r2){
11186                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11187                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11188             };
11189             this.data.sort(s.direction, fn);
11190             if(this.snapshot && this.snapshot != this.data){
11191                 this.snapshot.sort(s.direction, fn);
11192             }
11193         }
11194     },
11195
11196     /**
11197      * Sets the default sort column and order to be used by the next load operation.
11198      * @param {String} fieldName The name of the field to sort by.
11199      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11200      */
11201     setDefaultSort : function(field, dir){
11202         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11203     },
11204
11205     /**
11206      * Sort the Records.
11207      * If remote sorting is used, the sort is performed on the server, and the cache is
11208      * reloaded. If local sorting is used, the cache is sorted internally.
11209      * @param {String} fieldName The name of the field to sort by.
11210      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11211      */
11212     sort : function(fieldName, dir){
11213         var f = this.fields.get(fieldName);
11214         if(!dir){
11215             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11216             
11217             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11218                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11219             }else{
11220                 dir = f.sortDir;
11221             }
11222         }
11223         this.sortToggle[f.name] = dir;
11224         this.sortInfo = {field: f.name, direction: dir};
11225         if(!this.remoteSort){
11226             this.applySort();
11227             this.fireEvent("datachanged", this);
11228         }else{
11229             this.load(this.lastOptions);
11230         }
11231     },
11232
11233     /**
11234      * Calls the specified function for each of the Records in the cache.
11235      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11236      * Returning <em>false</em> aborts and exits the iteration.
11237      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11238      */
11239     each : function(fn, scope){
11240         this.data.each(fn, scope);
11241     },
11242
11243     /**
11244      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11245      * (e.g., during paging).
11246      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11247      */
11248     getModifiedRecords : function(){
11249         return this.modified;
11250     },
11251
11252     // private
11253     createFilterFn : function(property, value, anyMatch){
11254         if(!value.exec){ // not a regex
11255             value = String(value);
11256             if(value.length == 0){
11257                 return false;
11258             }
11259             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11260         }
11261         return function(r){
11262             return value.test(r.data[property]);
11263         };
11264     },
11265
11266     /**
11267      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11268      * @param {String} property A field on your records
11269      * @param {Number} start The record index to start at (defaults to 0)
11270      * @param {Number} end The last record index to include (defaults to length - 1)
11271      * @return {Number} The sum
11272      */
11273     sum : function(property, start, end){
11274         var rs = this.data.items, v = 0;
11275         start = start || 0;
11276         end = (end || end === 0) ? end : rs.length-1;
11277
11278         for(var i = start; i <= end; i++){
11279             v += (rs[i].data[property] || 0);
11280         }
11281         return v;
11282     },
11283
11284     /**
11285      * Filter the records by a specified property.
11286      * @param {String} field A field on your records
11287      * @param {String/RegExp} value Either a string that the field
11288      * should start with or a RegExp to test against the field
11289      * @param {Boolean} anyMatch True to match any part not just the beginning
11290      */
11291     filter : function(property, value, anyMatch){
11292         var fn = this.createFilterFn(property, value, anyMatch);
11293         return fn ? this.filterBy(fn) : this.clearFilter();
11294     },
11295
11296     /**
11297      * Filter by a function. The specified function will be called with each
11298      * record in this data source. If the function returns true the record is included,
11299      * otherwise it is filtered.
11300      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11301      * @param {Object} scope (optional) The scope of the function (defaults to this)
11302      */
11303     filterBy : function(fn, scope){
11304         this.snapshot = this.snapshot || this.data;
11305         this.data = this.queryBy(fn, scope||this);
11306         this.fireEvent("datachanged", this);
11307     },
11308
11309     /**
11310      * Query the records by a specified property.
11311      * @param {String} field A field on your records
11312      * @param {String/RegExp} value Either a string that the field
11313      * should start with or a RegExp to test against the field
11314      * @param {Boolean} anyMatch True to match any part not just the beginning
11315      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11316      */
11317     query : function(property, value, anyMatch){
11318         var fn = this.createFilterFn(property, value, anyMatch);
11319         return fn ? this.queryBy(fn) : this.data.clone();
11320     },
11321
11322     /**
11323      * Query by a function. The specified function will be called with each
11324      * record in this data source. If the function returns true the record is included
11325      * in the results.
11326      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11327      * @param {Object} scope (optional) The scope of the function (defaults to this)
11328       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11329      **/
11330     queryBy : function(fn, scope){
11331         var data = this.snapshot || this.data;
11332         return data.filterBy(fn, scope||this);
11333     },
11334
11335     /**
11336      * Collects unique values for a particular dataIndex from this store.
11337      * @param {String} dataIndex The property to collect
11338      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11339      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11340      * @return {Array} An array of the unique values
11341      **/
11342     collect : function(dataIndex, allowNull, bypassFilter){
11343         var d = (bypassFilter === true && this.snapshot) ?
11344                 this.snapshot.items : this.data.items;
11345         var v, sv, r = [], l = {};
11346         for(var i = 0, len = d.length; i < len; i++){
11347             v = d[i].data[dataIndex];
11348             sv = String(v);
11349             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11350                 l[sv] = true;
11351                 r[r.length] = v;
11352             }
11353         }
11354         return r;
11355     },
11356
11357     /**
11358      * Revert to a view of the Record cache with no filtering applied.
11359      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11360      */
11361     clearFilter : function(suppressEvent){
11362         if(this.snapshot && this.snapshot != this.data){
11363             this.data = this.snapshot;
11364             delete this.snapshot;
11365             if(suppressEvent !== true){
11366                 this.fireEvent("datachanged", this);
11367             }
11368         }
11369     },
11370
11371     // private
11372     afterEdit : function(record){
11373         if(this.modified.indexOf(record) == -1){
11374             this.modified.push(record);
11375         }
11376         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11377     },
11378     
11379     // private
11380     afterReject : function(record){
11381         this.modified.remove(record);
11382         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11383     },
11384
11385     // private
11386     afterCommit : function(record){
11387         this.modified.remove(record);
11388         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11389     },
11390
11391     /**
11392      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11393      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11394      */
11395     commitChanges : function(){
11396         var m = this.modified.slice(0);
11397         this.modified = [];
11398         for(var i = 0, len = m.length; i < len; i++){
11399             m[i].commit();
11400         }
11401     },
11402
11403     /**
11404      * Cancel outstanding changes on all changed records.
11405      */
11406     rejectChanges : function(){
11407         var m = this.modified.slice(0);
11408         this.modified = [];
11409         for(var i = 0, len = m.length; i < len; i++){
11410             m[i].reject();
11411         }
11412     },
11413
11414     onMetaChange : function(meta, rtype, o){
11415         this.recordType = rtype;
11416         this.fields = rtype.prototype.fields;
11417         delete this.snapshot;
11418         this.sortInfo = meta.sortInfo || this.sortInfo;
11419         this.modified = [];
11420         this.fireEvent('metachange', this, this.reader.meta);
11421     },
11422     
11423     moveIndex : function(data, type)
11424     {
11425         var index = this.indexOf(data);
11426         
11427         var newIndex = index + type;
11428         
11429         this.remove(data);
11430         
11431         this.insert(newIndex, data);
11432         
11433     }
11434 });/*
11435  * Based on:
11436  * Ext JS Library 1.1.1
11437  * Copyright(c) 2006-2007, Ext JS, LLC.
11438  *
11439  * Originally Released Under LGPL - original licence link has changed is not relivant.
11440  *
11441  * Fork - LGPL
11442  * <script type="text/javascript">
11443  */
11444
11445 /**
11446  * @class Roo.data.SimpleStore
11447  * @extends Roo.data.Store
11448  * Small helper class to make creating Stores from Array data easier.
11449  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11450  * @cfg {Array} fields An array of field definition objects, or field name strings.
11451  * @cfg {Array} data The multi-dimensional array of data
11452  * @constructor
11453  * @param {Object} config
11454  */
11455 Roo.data.SimpleStore = function(config){
11456     Roo.data.SimpleStore.superclass.constructor.call(this, {
11457         isLocal : true,
11458         reader: new Roo.data.ArrayReader({
11459                 id: config.id
11460             },
11461             Roo.data.Record.create(config.fields)
11462         ),
11463         proxy : new Roo.data.MemoryProxy(config.data)
11464     });
11465     this.load();
11466 };
11467 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11468  * Based on:
11469  * Ext JS Library 1.1.1
11470  * Copyright(c) 2006-2007, Ext JS, LLC.
11471  *
11472  * Originally Released Under LGPL - original licence link has changed is not relivant.
11473  *
11474  * Fork - LGPL
11475  * <script type="text/javascript">
11476  */
11477
11478 /**
11479 /**
11480  * @extends Roo.data.Store
11481  * @class Roo.data.JsonStore
11482  * Small helper class to make creating Stores for JSON data easier. <br/>
11483 <pre><code>
11484 var store = new Roo.data.JsonStore({
11485     url: 'get-images.php',
11486     root: 'images',
11487     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11488 });
11489 </code></pre>
11490  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11491  * JsonReader and HttpProxy (unless inline data is provided).</b>
11492  * @cfg {Array} fields An array of field definition objects, or field name strings.
11493  * @constructor
11494  * @param {Object} config
11495  */
11496 Roo.data.JsonStore = function(c){
11497     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11498         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11499         reader: new Roo.data.JsonReader(c, c.fields)
11500     }));
11501 };
11502 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11503  * Based on:
11504  * Ext JS Library 1.1.1
11505  * Copyright(c) 2006-2007, Ext JS, LLC.
11506  *
11507  * Originally Released Under LGPL - original licence link has changed is not relivant.
11508  *
11509  * Fork - LGPL
11510  * <script type="text/javascript">
11511  */
11512
11513  
11514 Roo.data.Field = function(config){
11515     if(typeof config == "string"){
11516         config = {name: config};
11517     }
11518     Roo.apply(this, config);
11519     
11520     if(!this.type){
11521         this.type = "auto";
11522     }
11523     
11524     var st = Roo.data.SortTypes;
11525     // named sortTypes are supported, here we look them up
11526     if(typeof this.sortType == "string"){
11527         this.sortType = st[this.sortType];
11528     }
11529     
11530     // set default sortType for strings and dates
11531     if(!this.sortType){
11532         switch(this.type){
11533             case "string":
11534                 this.sortType = st.asUCString;
11535                 break;
11536             case "date":
11537                 this.sortType = st.asDate;
11538                 break;
11539             default:
11540                 this.sortType = st.none;
11541         }
11542     }
11543
11544     // define once
11545     var stripRe = /[\$,%]/g;
11546
11547     // prebuilt conversion function for this field, instead of
11548     // switching every time we're reading a value
11549     if(!this.convert){
11550         var cv, dateFormat = this.dateFormat;
11551         switch(this.type){
11552             case "":
11553             case "auto":
11554             case undefined:
11555                 cv = function(v){ return v; };
11556                 break;
11557             case "string":
11558                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11559                 break;
11560             case "int":
11561                 cv = function(v){
11562                     return v !== undefined && v !== null && v !== '' ?
11563                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11564                     };
11565                 break;
11566             case "float":
11567                 cv = function(v){
11568                     return v !== undefined && v !== null && v !== '' ?
11569                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11570                     };
11571                 break;
11572             case "bool":
11573             case "boolean":
11574                 cv = function(v){ return v === true || v === "true" || v == 1; };
11575                 break;
11576             case "date":
11577                 cv = function(v){
11578                     if(!v){
11579                         return '';
11580                     }
11581                     if(v instanceof Date){
11582                         return v;
11583                     }
11584                     if(dateFormat){
11585                         if(dateFormat == "timestamp"){
11586                             return new Date(v*1000);
11587                         }
11588                         return Date.parseDate(v, dateFormat);
11589                     }
11590                     var parsed = Date.parse(v);
11591                     return parsed ? new Date(parsed) : null;
11592                 };
11593              break;
11594             
11595         }
11596         this.convert = cv;
11597     }
11598 };
11599
11600 Roo.data.Field.prototype = {
11601     dateFormat: null,
11602     defaultValue: "",
11603     mapping: null,
11604     sortType : null,
11605     sortDir : "ASC"
11606 };/*
11607  * Based on:
11608  * Ext JS Library 1.1.1
11609  * Copyright(c) 2006-2007, Ext JS, LLC.
11610  *
11611  * Originally Released Under LGPL - original licence link has changed is not relivant.
11612  *
11613  * Fork - LGPL
11614  * <script type="text/javascript">
11615  */
11616  
11617 // Base class for reading structured data from a data source.  This class is intended to be
11618 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11619
11620 /**
11621  * @class Roo.data.DataReader
11622  * Base class for reading structured data from a data source.  This class is intended to be
11623  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11624  */
11625
11626 Roo.data.DataReader = function(meta, recordType){
11627     
11628     this.meta = meta;
11629     
11630     this.recordType = recordType instanceof Array ? 
11631         Roo.data.Record.create(recordType) : recordType;
11632 };
11633
11634 Roo.data.DataReader.prototype = {
11635      /**
11636      * Create an empty record
11637      * @param {Object} data (optional) - overlay some values
11638      * @return {Roo.data.Record} record created.
11639      */
11640     newRow :  function(d) {
11641         var da =  {};
11642         this.recordType.prototype.fields.each(function(c) {
11643             switch( c.type) {
11644                 case 'int' : da[c.name] = 0; break;
11645                 case 'date' : da[c.name] = new Date(); break;
11646                 case 'float' : da[c.name] = 0.0; break;
11647                 case 'boolean' : da[c.name] = false; break;
11648                 default : da[c.name] = ""; break;
11649             }
11650             
11651         });
11652         return new this.recordType(Roo.apply(da, d));
11653     }
11654     
11655 };/*
11656  * Based on:
11657  * Ext JS Library 1.1.1
11658  * Copyright(c) 2006-2007, Ext JS, LLC.
11659  *
11660  * Originally Released Under LGPL - original licence link has changed is not relivant.
11661  *
11662  * Fork - LGPL
11663  * <script type="text/javascript">
11664  */
11665
11666 /**
11667  * @class Roo.data.DataProxy
11668  * @extends Roo.data.Observable
11669  * This class is an abstract base class for implementations which provide retrieval of
11670  * unformatted data objects.<br>
11671  * <p>
11672  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11673  * (of the appropriate type which knows how to parse the data object) to provide a block of
11674  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11675  * <p>
11676  * Custom implementations must implement the load method as described in
11677  * {@link Roo.data.HttpProxy#load}.
11678  */
11679 Roo.data.DataProxy = function(){
11680     this.addEvents({
11681         /**
11682          * @event beforeload
11683          * Fires before a network request is made to retrieve a data object.
11684          * @param {Object} This DataProxy object.
11685          * @param {Object} params The params parameter to the load function.
11686          */
11687         beforeload : true,
11688         /**
11689          * @event load
11690          * Fires before the load method's callback is called.
11691          * @param {Object} This DataProxy object.
11692          * @param {Object} o The data object.
11693          * @param {Object} arg The callback argument object passed to the load function.
11694          */
11695         load : true,
11696         /**
11697          * @event loadexception
11698          * Fires if an Exception occurs during data retrieval.
11699          * @param {Object} This DataProxy object.
11700          * @param {Object} o The data object.
11701          * @param {Object} arg The callback argument object passed to the load function.
11702          * @param {Object} e The Exception.
11703          */
11704         loadexception : true
11705     });
11706     Roo.data.DataProxy.superclass.constructor.call(this);
11707 };
11708
11709 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11710
11711     /**
11712      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11713      */
11714 /*
11715  * Based on:
11716  * Ext JS Library 1.1.1
11717  * Copyright(c) 2006-2007, Ext JS, LLC.
11718  *
11719  * Originally Released Under LGPL - original licence link has changed is not relivant.
11720  *
11721  * Fork - LGPL
11722  * <script type="text/javascript">
11723  */
11724 /**
11725  * @class Roo.data.MemoryProxy
11726  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11727  * to the Reader when its load method is called.
11728  * @constructor
11729  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11730  */
11731 Roo.data.MemoryProxy = function(data){
11732     if (data.data) {
11733         data = data.data;
11734     }
11735     Roo.data.MemoryProxy.superclass.constructor.call(this);
11736     this.data = data;
11737 };
11738
11739 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11740     
11741     /**
11742      * Load data from the requested source (in this case an in-memory
11743      * data object passed to the constructor), read the data object into
11744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11745      * process that block using the passed callback.
11746      * @param {Object} params This parameter is not used by the MemoryProxy class.
11747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11748      * object into a block of Roo.data.Records.
11749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11750      * The function must be passed <ul>
11751      * <li>The Record block object</li>
11752      * <li>The "arg" argument from the load function</li>
11753      * <li>A boolean success indicator</li>
11754      * </ul>
11755      * @param {Object} scope The scope in which to call the callback
11756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11757      */
11758     load : function(params, reader, callback, scope, arg){
11759         params = params || {};
11760         var result;
11761         try {
11762             result = reader.readRecords(this.data);
11763         }catch(e){
11764             this.fireEvent("loadexception", this, arg, null, e);
11765             callback.call(scope, null, arg, false);
11766             return;
11767         }
11768         callback.call(scope, result, arg, true);
11769     },
11770     
11771     // private
11772     update : function(params, records){
11773         
11774     }
11775 });/*
11776  * Based on:
11777  * Ext JS Library 1.1.1
11778  * Copyright(c) 2006-2007, Ext JS, LLC.
11779  *
11780  * Originally Released Under LGPL - original licence link has changed is not relivant.
11781  *
11782  * Fork - LGPL
11783  * <script type="text/javascript">
11784  */
11785 /**
11786  * @class Roo.data.HttpProxy
11787  * @extends Roo.data.DataProxy
11788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11789  * configured to reference a certain URL.<br><br>
11790  * <p>
11791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11792  * from which the running page was served.<br><br>
11793  * <p>
11794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11795  * <p>
11796  * Be aware that to enable the browser to parse an XML document, the server must set
11797  * the Content-Type header in the HTTP response to "text/xml".
11798  * @constructor
11799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11801  * will be used to make the request.
11802  */
11803 Roo.data.HttpProxy = function(conn){
11804     Roo.data.HttpProxy.superclass.constructor.call(this);
11805     // is conn a conn config or a real conn?
11806     this.conn = conn;
11807     this.useAjax = !conn || !conn.events;
11808   
11809 };
11810
11811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11812     // thse are take from connection...
11813     
11814     /**
11815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11816      */
11817     /**
11818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11819      * extra parameters to each request made by this object. (defaults to undefined)
11820      */
11821     /**
11822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11823      *  to each request made by this object. (defaults to undefined)
11824      */
11825     /**
11826      * @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)
11827      */
11828     /**
11829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11830      */
11831      /**
11832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11833      * @type Boolean
11834      */
11835   
11836
11837     /**
11838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11839      * @type Boolean
11840      */
11841     /**
11842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11844      * a finer-grained basis than the DataProxy events.
11845      */
11846     getConnection : function(){
11847         return this.useAjax ? Roo.Ajax : this.conn;
11848     },
11849
11850     /**
11851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11853      * process that block using the passed callback.
11854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11855      * for the request to the remote server.
11856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11857      * object into a block of Roo.data.Records.
11858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11859      * The function must be passed <ul>
11860      * <li>The Record block object</li>
11861      * <li>The "arg" argument from the load function</li>
11862      * <li>A boolean success indicator</li>
11863      * </ul>
11864      * @param {Object} scope The scope in which to call the callback
11865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11866      */
11867     load : function(params, reader, callback, scope, arg){
11868         if(this.fireEvent("beforeload", this, params) !== false){
11869             var  o = {
11870                 params : params || {},
11871                 request: {
11872                     callback : callback,
11873                     scope : scope,
11874                     arg : arg
11875                 },
11876                 reader: reader,
11877                 callback : this.loadResponse,
11878                 scope: this
11879             };
11880             if(this.useAjax){
11881                 Roo.applyIf(o, this.conn);
11882                 if(this.activeRequest){
11883                     Roo.Ajax.abort(this.activeRequest);
11884                 }
11885                 this.activeRequest = Roo.Ajax.request(o);
11886             }else{
11887                 this.conn.request(o);
11888             }
11889         }else{
11890             callback.call(scope||this, null, arg, false);
11891         }
11892     },
11893
11894     // private
11895     loadResponse : function(o, success, response){
11896         delete this.activeRequest;
11897         if(!success){
11898             this.fireEvent("loadexception", this, o, response);
11899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11900             return;
11901         }
11902         var result;
11903         try {
11904             result = o.reader.read(response);
11905         }catch(e){
11906             this.fireEvent("loadexception", this, o, response, e);
11907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11908             return;
11909         }
11910         
11911         this.fireEvent("load", this, o, o.request.arg);
11912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11913     },
11914
11915     // private
11916     update : function(dataSet){
11917
11918     },
11919
11920     // private
11921     updateResponse : function(dataSet){
11922
11923     }
11924 });/*
11925  * Based on:
11926  * Ext JS Library 1.1.1
11927  * Copyright(c) 2006-2007, Ext JS, LLC.
11928  *
11929  * Originally Released Under LGPL - original licence link has changed is not relivant.
11930  *
11931  * Fork - LGPL
11932  * <script type="text/javascript">
11933  */
11934
11935 /**
11936  * @class Roo.data.ScriptTagProxy
11937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11938  * other than the originating domain of the running page.<br><br>
11939  * <p>
11940  * <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
11941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11942  * <p>
11943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11944  * source code that is used as the source inside a &lt;script> tag.<br><br>
11945  * <p>
11946  * In order for the browser to process the returned data, the server must wrap the data object
11947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11949  * depending on whether the callback name was passed:
11950  * <p>
11951  * <pre><code>
11952 boolean scriptTag = false;
11953 String cb = request.getParameter("callback");
11954 if (cb != null) {
11955     scriptTag = true;
11956     response.setContentType("text/javascript");
11957 } else {
11958     response.setContentType("application/x-json");
11959 }
11960 Writer out = response.getWriter();
11961 if (scriptTag) {
11962     out.write(cb + "(");
11963 }
11964 out.print(dataBlock.toJsonString());
11965 if (scriptTag) {
11966     out.write(");");
11967 }
11968 </pre></code>
11969  *
11970  * @constructor
11971  * @param {Object} config A configuration object.
11972  */
11973 Roo.data.ScriptTagProxy = function(config){
11974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11975     Roo.apply(this, config);
11976     this.head = document.getElementsByTagName("head")[0];
11977 };
11978
11979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11980
11981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11982     /**
11983      * @cfg {String} url The URL from which to request the data object.
11984      */
11985     /**
11986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11987      */
11988     timeout : 30000,
11989     /**
11990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11991      * the server the name of the callback function set up by the load call to process the returned data object.
11992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11993      * javascript output which calls this named function passing the data object as its only parameter.
11994      */
11995     callbackParam : "callback",
11996     /**
11997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11998      * name to the request.
11999      */
12000     nocache : true,
12001
12002     /**
12003      * Load data from the configured URL, read the data object into
12004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12005      * process that block using the passed callback.
12006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12007      * for the request to the remote server.
12008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12009      * object into a block of Roo.data.Records.
12010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12011      * The function must be passed <ul>
12012      * <li>The Record block object</li>
12013      * <li>The "arg" argument from the load function</li>
12014      * <li>A boolean success indicator</li>
12015      * </ul>
12016      * @param {Object} scope The scope in which to call the callback
12017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12018      */
12019     load : function(params, reader, callback, scope, arg){
12020         if(this.fireEvent("beforeload", this, params) !== false){
12021
12022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12023
12024             var url = this.url;
12025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12026             if(this.nocache){
12027                 url += "&_dc=" + (new Date().getTime());
12028             }
12029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12030             var trans = {
12031                 id : transId,
12032                 cb : "stcCallback"+transId,
12033                 scriptId : "stcScript"+transId,
12034                 params : params,
12035                 arg : arg,
12036                 url : url,
12037                 callback : callback,
12038                 scope : scope,
12039                 reader : reader
12040             };
12041             var conn = this;
12042
12043             window[trans.cb] = function(o){
12044                 conn.handleResponse(o, trans);
12045             };
12046
12047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12048
12049             if(this.autoAbort !== false){
12050                 this.abort();
12051             }
12052
12053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12054
12055             var script = document.createElement("script");
12056             script.setAttribute("src", url);
12057             script.setAttribute("type", "text/javascript");
12058             script.setAttribute("id", trans.scriptId);
12059             this.head.appendChild(script);
12060
12061             this.trans = trans;
12062         }else{
12063             callback.call(scope||this, null, arg, false);
12064         }
12065     },
12066
12067     // private
12068     isLoading : function(){
12069         return this.trans ? true : false;
12070     },
12071
12072     /**
12073      * Abort the current server request.
12074      */
12075     abort : function(){
12076         if(this.isLoading()){
12077             this.destroyTrans(this.trans);
12078         }
12079     },
12080
12081     // private
12082     destroyTrans : function(trans, isLoaded){
12083         this.head.removeChild(document.getElementById(trans.scriptId));
12084         clearTimeout(trans.timeoutId);
12085         if(isLoaded){
12086             window[trans.cb] = undefined;
12087             try{
12088                 delete window[trans.cb];
12089             }catch(e){}
12090         }else{
12091             // if hasn't been loaded, wait for load to remove it to prevent script error
12092             window[trans.cb] = function(){
12093                 window[trans.cb] = undefined;
12094                 try{
12095                     delete window[trans.cb];
12096                 }catch(e){}
12097             };
12098         }
12099     },
12100
12101     // private
12102     handleResponse : function(o, trans){
12103         this.trans = false;
12104         this.destroyTrans(trans, true);
12105         var result;
12106         try {
12107             result = trans.reader.readRecords(o);
12108         }catch(e){
12109             this.fireEvent("loadexception", this, o, trans.arg, e);
12110             trans.callback.call(trans.scope||window, null, trans.arg, false);
12111             return;
12112         }
12113         this.fireEvent("load", this, o, trans.arg);
12114         trans.callback.call(trans.scope||window, result, trans.arg, true);
12115     },
12116
12117     // private
12118     handleFailure : function(trans){
12119         this.trans = false;
12120         this.destroyTrans(trans, false);
12121         this.fireEvent("loadexception", this, null, trans.arg);
12122         trans.callback.call(trans.scope||window, null, trans.arg, false);
12123     }
12124 });/*
12125  * Based on:
12126  * Ext JS Library 1.1.1
12127  * Copyright(c) 2006-2007, Ext JS, LLC.
12128  *
12129  * Originally Released Under LGPL - original licence link has changed is not relivant.
12130  *
12131  * Fork - LGPL
12132  * <script type="text/javascript">
12133  */
12134
12135 /**
12136  * @class Roo.data.JsonReader
12137  * @extends Roo.data.DataReader
12138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12139  * based on mappings in a provided Roo.data.Record constructor.
12140  * 
12141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12142  * in the reply previously. 
12143  * 
12144  * <p>
12145  * Example code:
12146  * <pre><code>
12147 var RecordDef = Roo.data.Record.create([
12148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12150 ]);
12151 var myReader = new Roo.data.JsonReader({
12152     totalProperty: "results",    // The property which contains the total dataset size (optional)
12153     root: "rows",                // The property which contains an Array of row objects
12154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12155 }, RecordDef);
12156 </code></pre>
12157  * <p>
12158  * This would consume a JSON file like this:
12159  * <pre><code>
12160 { 'results': 2, 'rows': [
12161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12163 }
12164 </code></pre>
12165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12167  * paged from the remote server.
12168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12169  * @cfg {String} root name of the property which contains the Array of row objects.
12170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12171  * @cfg {Array} fields Array of field definition objects
12172  * @constructor
12173  * Create a new JsonReader
12174  * @param {Object} meta Metadata configuration options
12175  * @param {Object} recordType Either an Array of field definition objects,
12176  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12177  */
12178 Roo.data.JsonReader = function(meta, recordType){
12179     
12180     meta = meta || {};
12181     // set some defaults:
12182     Roo.applyIf(meta, {
12183         totalProperty: 'total',
12184         successProperty : 'success',
12185         root : 'data',
12186         id : 'id'
12187     });
12188     
12189     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12190 };
12191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12192     
12193     /**
12194      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12195      * Used by Store query builder to append _requestMeta to params.
12196      * 
12197      */
12198     metaFromRemote : false,
12199     /**
12200      * This method is only used by a DataProxy which has retrieved data from a remote server.
12201      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12202      * @return {Object} data A data block which is used by an Roo.data.Store object as
12203      * a cache of Roo.data.Records.
12204      */
12205     read : function(response){
12206         var json = response.responseText;
12207        
12208         var o = /* eval:var:o */ eval("("+json+")");
12209         if(!o) {
12210             throw {message: "JsonReader.read: Json object not found"};
12211         }
12212         
12213         if(o.metaData){
12214             
12215             delete this.ef;
12216             this.metaFromRemote = true;
12217             this.meta = o.metaData;
12218             this.recordType = Roo.data.Record.create(o.metaData.fields);
12219             this.onMetaChange(this.meta, this.recordType, o);
12220         }
12221         return this.readRecords(o);
12222     },
12223
12224     // private function a store will implement
12225     onMetaChange : function(meta, recordType, o){
12226
12227     },
12228
12229     /**
12230          * @ignore
12231          */
12232     simpleAccess: function(obj, subsc) {
12233         return obj[subsc];
12234     },
12235
12236         /**
12237          * @ignore
12238          */
12239     getJsonAccessor: function(){
12240         var re = /[\[\.]/;
12241         return function(expr) {
12242             try {
12243                 return(re.test(expr))
12244                     ? new Function("obj", "return obj." + expr)
12245                     : function(obj){
12246                         return obj[expr];
12247                     };
12248             } catch(e){}
12249             return Roo.emptyFn;
12250         };
12251     }(),
12252
12253     /**
12254      * Create a data block containing Roo.data.Records from an XML document.
12255      * @param {Object} o An object which contains an Array of row objects in the property specified
12256      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12257      * which contains the total size of the dataset.
12258      * @return {Object} data A data block which is used by an Roo.data.Store object as
12259      * a cache of Roo.data.Records.
12260      */
12261     readRecords : function(o){
12262         /**
12263          * After any data loads, the raw JSON data is available for further custom processing.
12264          * @type Object
12265          */
12266         this.o = o;
12267         var s = this.meta, Record = this.recordType,
12268             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12269
12270 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12271         if (!this.ef) {
12272             if(s.totalProperty) {
12273                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12274                 }
12275                 if(s.successProperty) {
12276                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12277                 }
12278                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12279                 if (s.id) {
12280                         var g = this.getJsonAccessor(s.id);
12281                         this.getId = function(rec) {
12282                                 var r = g(rec);  
12283                                 return (r === undefined || r === "") ? null : r;
12284                         };
12285                 } else {
12286                         this.getId = function(){return null;};
12287                 }
12288             this.ef = [];
12289             for(var jj = 0; jj < fl; jj++){
12290                 f = fi[jj];
12291                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12292                 this.ef[jj] = this.getJsonAccessor(map);
12293             }
12294         }
12295
12296         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12297         if(s.totalProperty){
12298             var vt = parseInt(this.getTotal(o), 10);
12299             if(!isNaN(vt)){
12300                 totalRecords = vt;
12301             }
12302         }
12303         if(s.successProperty){
12304             var vs = this.getSuccess(o);
12305             if(vs === false || vs === 'false'){
12306                 success = false;
12307             }
12308         }
12309         var records = [];
12310         for(var i = 0; i < c; i++){
12311                 var n = root[i];
12312             var values = {};
12313             var id = this.getId(n);
12314             for(var j = 0; j < fl; j++){
12315                 f = fi[j];
12316             var v = this.ef[j](n);
12317             if (!f.convert) {
12318                 Roo.log('missing convert for ' + f.name);
12319                 Roo.log(f);
12320                 continue;
12321             }
12322             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12323             }
12324             var record = new Record(values, id);
12325             record.json = n;
12326             records[i] = record;
12327         }
12328         return {
12329             raw : o,
12330             success : success,
12331             records : records,
12332             totalRecords : totalRecords
12333         };
12334     }
12335 });/*
12336  * Based on:
12337  * Ext JS Library 1.1.1
12338  * Copyright(c) 2006-2007, Ext JS, LLC.
12339  *
12340  * Originally Released Under LGPL - original licence link has changed is not relivant.
12341  *
12342  * Fork - LGPL
12343  * <script type="text/javascript">
12344  */
12345
12346 /**
12347  * @class Roo.data.ArrayReader
12348  * @extends Roo.data.DataReader
12349  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12350  * Each element of that Array represents a row of data fields. The
12351  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12352  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12353  * <p>
12354  * Example code:.
12355  * <pre><code>
12356 var RecordDef = Roo.data.Record.create([
12357     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12358     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12359 ]);
12360 var myReader = new Roo.data.ArrayReader({
12361     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12362 }, RecordDef);
12363 </code></pre>
12364  * <p>
12365  * This would consume an Array like this:
12366  * <pre><code>
12367 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12368   </code></pre>
12369  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12370  * @constructor
12371  * Create a new JsonReader
12372  * @param {Object} meta Metadata configuration options.
12373  * @param {Object} recordType Either an Array of field definition objects
12374  * as specified to {@link Roo.data.Record#create},
12375  * or an {@link Roo.data.Record} object
12376  * created using {@link Roo.data.Record#create}.
12377  */
12378 Roo.data.ArrayReader = function(meta, recordType){
12379     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12380 };
12381
12382 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12383     /**
12384      * Create a data block containing Roo.data.Records from an XML document.
12385      * @param {Object} o An Array of row objects which represents the dataset.
12386      * @return {Object} data A data block which is used by an Roo.data.Store object as
12387      * a cache of Roo.data.Records.
12388      */
12389     readRecords : function(o){
12390         var sid = this.meta ? this.meta.id : null;
12391         var recordType = this.recordType, fields = recordType.prototype.fields;
12392         var records = [];
12393         var root = o;
12394             for(var i = 0; i < root.length; i++){
12395                     var n = root[i];
12396                 var values = {};
12397                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12399                 var f = fields.items[j];
12400                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12401                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12402                 v = f.convert(v);
12403                 values[f.name] = v;
12404             }
12405                 var record = new recordType(values, id);
12406                 record.json = n;
12407                 records[records.length] = record;
12408             }
12409             return {
12410                 records : records,
12411                 totalRecords : records.length
12412             };
12413     }
12414 });/*
12415  * - LGPL
12416  * * 
12417  */
12418
12419 /**
12420  * @class Roo.bootstrap.ComboBox
12421  * @extends Roo.bootstrap.TriggerField
12422  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12423  * @cfg {Boolean} append (true|false) default false
12424  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12425  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12426  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12427  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12428  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12429  * @cfg {Boolean} animate default true
12430  * @cfg {Boolean} emptyResultText only for touch device
12431  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12432  * @cfg {String} emptyTitle default ''
12433  * @constructor
12434  * Create a new ComboBox.
12435  * @param {Object} config Configuration options
12436  */
12437 Roo.bootstrap.ComboBox = function(config){
12438     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12439     this.addEvents({
12440         /**
12441          * @event expand
12442          * Fires when the dropdown list is expanded
12443         * @param {Roo.bootstrap.ComboBox} combo This combo box
12444         */
12445         'expand' : true,
12446         /**
12447          * @event collapse
12448          * Fires when the dropdown list is collapsed
12449         * @param {Roo.bootstrap.ComboBox} combo This combo box
12450         */
12451         'collapse' : true,
12452         /**
12453          * @event beforeselect
12454          * Fires before a list item is selected. Return false to cancel the selection.
12455         * @param {Roo.bootstrap.ComboBox} combo This combo box
12456         * @param {Roo.data.Record} record The data record returned from the underlying store
12457         * @param {Number} index The index of the selected item in the dropdown list
12458         */
12459         'beforeselect' : true,
12460         /**
12461          * @event select
12462          * Fires when a list item is selected
12463         * @param {Roo.bootstrap.ComboBox} combo This combo box
12464         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12465         * @param {Number} index The index of the selected item in the dropdown list
12466         */
12467         'select' : true,
12468         /**
12469          * @event beforequery
12470          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12471          * The event object passed has these properties:
12472         * @param {Roo.bootstrap.ComboBox} combo This combo box
12473         * @param {String} query The query
12474         * @param {Boolean} forceAll true to force "all" query
12475         * @param {Boolean} cancel true to cancel the query
12476         * @param {Object} e The query event object
12477         */
12478         'beforequery': true,
12479          /**
12480          * @event add
12481          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12482         * @param {Roo.bootstrap.ComboBox} combo This combo box
12483         */
12484         'add' : true,
12485         /**
12486          * @event edit
12487          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12490         */
12491         'edit' : true,
12492         /**
12493          * @event remove
12494          * Fires when the remove value from the combobox array
12495         * @param {Roo.bootstrap.ComboBox} combo This combo box
12496         */
12497         'remove' : true,
12498         /**
12499          * @event afterremove
12500          * Fires when the remove value from the combobox array
12501         * @param {Roo.bootstrap.ComboBox} combo This combo box
12502         */
12503         'afterremove' : true,
12504         /**
12505          * @event specialfilter
12506          * Fires when specialfilter
12507             * @param {Roo.bootstrap.ComboBox} combo This combo box
12508             */
12509         'specialfilter' : true,
12510         /**
12511          * @event tick
12512          * Fires when tick the element
12513             * @param {Roo.bootstrap.ComboBox} combo This combo box
12514             */
12515         'tick' : true,
12516         /**
12517          * @event touchviewdisplay
12518          * Fires when touch view require special display (default is using displayField)
12519             * @param {Roo.bootstrap.ComboBox} combo This combo box
12520             * @param {Object} cfg set html .
12521             */
12522         'touchviewdisplay' : true
12523         
12524     });
12525     
12526     this.item = [];
12527     this.tickItems = [];
12528     
12529     this.selectedIndex = -1;
12530     if(this.mode == 'local'){
12531         if(config.queryDelay === undefined){
12532             this.queryDelay = 10;
12533         }
12534         if(config.minChars === undefined){
12535             this.minChars = 0;
12536         }
12537     }
12538 };
12539
12540 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12541      
12542     /**
12543      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12544      * rendering into an Roo.Editor, defaults to false)
12545      */
12546     /**
12547      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12548      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12549      */
12550     /**
12551      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12552      */
12553     /**
12554      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12555      * the dropdown list (defaults to undefined, with no header element)
12556      */
12557
12558      /**
12559      * @cfg {String/Roo.Template} tpl The template to use to render the output
12560      */
12561      
12562      /**
12563      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12564      */
12565     listWidth: undefined,
12566     /**
12567      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12568      * mode = 'remote' or 'text' if mode = 'local')
12569      */
12570     displayField: undefined,
12571     
12572     /**
12573      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12574      * mode = 'remote' or 'value' if mode = 'local'). 
12575      * Note: use of a valueField requires the user make a selection
12576      * in order for a value to be mapped.
12577      */
12578     valueField: undefined,
12579     /**
12580      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12581      */
12582     modalTitle : '',
12583     
12584     /**
12585      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12586      * field's data value (defaults to the underlying DOM element's name)
12587      */
12588     hiddenName: undefined,
12589     /**
12590      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12591      */
12592     listClass: '',
12593     /**
12594      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12595      */
12596     selectedClass: 'active',
12597     
12598     /**
12599      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12600      */
12601     shadow:'sides',
12602     /**
12603      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12604      * anchor positions (defaults to 'tl-bl')
12605      */
12606     listAlign: 'tl-bl?',
12607     /**
12608      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12609      */
12610     maxHeight: 300,
12611     /**
12612      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12613      * query specified by the allQuery config option (defaults to 'query')
12614      */
12615     triggerAction: 'query',
12616     /**
12617      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12618      * (defaults to 4, does not apply if editable = false)
12619      */
12620     minChars : 4,
12621     /**
12622      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12623      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12624      */
12625     typeAhead: false,
12626     /**
12627      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12628      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12629      */
12630     queryDelay: 500,
12631     /**
12632      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12633      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12634      */
12635     pageSize: 0,
12636     /**
12637      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12638      * when editable = true (defaults to false)
12639      */
12640     selectOnFocus:false,
12641     /**
12642      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12643      */
12644     queryParam: 'query',
12645     /**
12646      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12647      * when mode = 'remote' (defaults to 'Loading...')
12648      */
12649     loadingText: 'Loading...',
12650     /**
12651      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12652      */
12653     resizable: false,
12654     /**
12655      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12656      */
12657     handleHeight : 8,
12658     /**
12659      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12660      * traditional select (defaults to true)
12661      */
12662     editable: true,
12663     /**
12664      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12665      */
12666     allQuery: '',
12667     /**
12668      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12669      */
12670     mode: 'remote',
12671     /**
12672      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12673      * listWidth has a higher value)
12674      */
12675     minListWidth : 70,
12676     /**
12677      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12678      * allow the user to set arbitrary text into the field (defaults to false)
12679      */
12680     forceSelection:false,
12681     /**
12682      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12683      * if typeAhead = true (defaults to 250)
12684      */
12685     typeAheadDelay : 250,
12686     /**
12687      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12688      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12689      */
12690     valueNotFoundText : undefined,
12691     /**
12692      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12693      */
12694     blockFocus : false,
12695     
12696     /**
12697      * @cfg {Boolean} disableClear Disable showing of clear button.
12698      */
12699     disableClear : false,
12700     /**
12701      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12702      */
12703     alwaysQuery : false,
12704     
12705     /**
12706      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12707      */
12708     multiple : false,
12709     
12710     /**
12711      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12712      */
12713     invalidClass : "has-warning",
12714     
12715     /**
12716      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12717      */
12718     validClass : "has-success",
12719     
12720     /**
12721      * @cfg {Boolean} specialFilter (true|false) special filter default false
12722      */
12723     specialFilter : false,
12724     
12725     /**
12726      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12727      */
12728     mobileTouchView : true,
12729     
12730     /**
12731      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12732      */
12733     useNativeIOS : false,
12734     
12735     ios_options : false,
12736     
12737     //private
12738     addicon : false,
12739     editicon: false,
12740     
12741     page: 0,
12742     hasQuery: false,
12743     append: false,
12744     loadNext: false,
12745     autoFocus : true,
12746     tickable : false,
12747     btnPosition : 'right',
12748     triggerList : true,
12749     showToggleBtn : true,
12750     animate : true,
12751     emptyResultText: 'Empty',
12752     triggerText : 'Select',
12753     emptyTitle : '',
12754     
12755     // element that contains real text value.. (when hidden is used..)
12756     
12757     getAutoCreate : function()
12758     {   
12759         var cfg = false;
12760         //render
12761         /*
12762          * Render classic select for iso
12763          */
12764         
12765         if(Roo.isIOS && this.useNativeIOS){
12766             cfg = this.getAutoCreateNativeIOS();
12767             return cfg;
12768         }
12769         
12770         /*
12771          * Touch Devices
12772          */
12773         
12774         if(Roo.isTouch && this.mobileTouchView){
12775             cfg = this.getAutoCreateTouchView();
12776             return cfg;;
12777         }
12778         
12779         /*
12780          *  Normal ComboBox
12781          */
12782         if(!this.tickable){
12783             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12784             return cfg;
12785         }
12786         
12787         /*
12788          *  ComboBox with tickable selections
12789          */
12790              
12791         var align = this.labelAlign || this.parentLabelAlign();
12792         
12793         cfg = {
12794             cls : 'form-group roo-combobox-tickable' //input-group
12795         };
12796         
12797         var btn_text_select = '';
12798         var btn_text_done = '';
12799         var btn_text_cancel = '';
12800         
12801         if (this.btn_text_show) {
12802             btn_text_select = 'Select';
12803             btn_text_done = 'Done';
12804             btn_text_cancel = 'Cancel'; 
12805         }
12806         
12807         var buttons = {
12808             tag : 'div',
12809             cls : 'tickable-buttons',
12810             cn : [
12811                 {
12812                     tag : 'button',
12813                     type : 'button',
12814                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12815                     //html : this.triggerText
12816                     html: btn_text_select
12817                 },
12818                 {
12819                     tag : 'button',
12820                     type : 'button',
12821                     name : 'ok',
12822                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12823                     //html : 'Done'
12824                     html: btn_text_done
12825                 },
12826                 {
12827                     tag : 'button',
12828                     type : 'button',
12829                     name : 'cancel',
12830                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12831                     //html : 'Cancel'
12832                     html: btn_text_cancel
12833                 }
12834             ]
12835         };
12836         
12837         if(this.editable){
12838             buttons.cn.unshift({
12839                 tag: 'input',
12840                 cls: 'roo-select2-search-field-input'
12841             });
12842         }
12843         
12844         var _this = this;
12845         
12846         Roo.each(buttons.cn, function(c){
12847             if (_this.size) {
12848                 c.cls += ' btn-' + _this.size;
12849             }
12850
12851             if (_this.disabled) {
12852                 c.disabled = true;
12853             }
12854         });
12855         
12856         var box = {
12857             tag: 'div',
12858             cn: [
12859                 {
12860                     tag: 'input',
12861                     type : 'hidden',
12862                     cls: 'form-hidden-field'
12863                 },
12864                 {
12865                     tag: 'ul',
12866                     cls: 'roo-select2-choices',
12867                     cn:[
12868                         {
12869                             tag: 'li',
12870                             cls: 'roo-select2-search-field',
12871                             cn: [
12872                                 buttons
12873                             ]
12874                         }
12875                     ]
12876                 }
12877             ]
12878         };
12879         
12880         var combobox = {
12881             cls: 'roo-select2-container input-group roo-select2-container-multi',
12882             cn: [
12883                 box
12884 //                {
12885 //                    tag: 'ul',
12886 //                    cls: 'typeahead typeahead-long dropdown-menu',
12887 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12888 //                }
12889             ]
12890         };
12891         
12892         if(this.hasFeedback && !this.allowBlank){
12893             
12894             var feedback = {
12895                 tag: 'span',
12896                 cls: 'glyphicon form-control-feedback'
12897             };
12898
12899             combobox.cn.push(feedback);
12900         }
12901         
12902         
12903         if (align ==='left' && this.fieldLabel.length) {
12904             
12905             cfg.cls += ' roo-form-group-label-left';
12906             
12907             cfg.cn = [
12908                 {
12909                     tag : 'i',
12910                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12911                     tooltip : 'This field is required'
12912                 },
12913                 {
12914                     tag: 'label',
12915                     'for' :  id,
12916                     cls : 'control-label',
12917                     html : this.fieldLabel
12918
12919                 },
12920                 {
12921                     cls : "", 
12922                     cn: [
12923                         combobox
12924                     ]
12925                 }
12926
12927             ];
12928             
12929             var labelCfg = cfg.cn[1];
12930             var contentCfg = cfg.cn[2];
12931             
12932
12933             if(this.indicatorpos == 'right'){
12934                 
12935                 cfg.cn = [
12936                     {
12937                         tag: 'label',
12938                         'for' :  id,
12939                         cls : 'control-label',
12940                         cn : [
12941                             {
12942                                 tag : 'span',
12943                                 html : this.fieldLabel
12944                             },
12945                             {
12946                                 tag : 'i',
12947                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12948                                 tooltip : 'This field is required'
12949                             }
12950                         ]
12951                     },
12952                     {
12953                         cls : "",
12954                         cn: [
12955                             combobox
12956                         ]
12957                     }
12958
12959                 ];
12960                 
12961                 
12962                 
12963                 labelCfg = cfg.cn[0];
12964                 contentCfg = cfg.cn[1];
12965             
12966             }
12967             
12968             if(this.labelWidth > 12){
12969                 labelCfg.style = "width: " + this.labelWidth + 'px';
12970             }
12971             
12972             if(this.labelWidth < 13 && this.labelmd == 0){
12973                 this.labelmd = this.labelWidth;
12974             }
12975             
12976             if(this.labellg > 0){
12977                 labelCfg.cls += ' col-lg-' + this.labellg;
12978                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12979             }
12980             
12981             if(this.labelmd > 0){
12982                 labelCfg.cls += ' col-md-' + this.labelmd;
12983                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12984             }
12985             
12986             if(this.labelsm > 0){
12987                 labelCfg.cls += ' col-sm-' + this.labelsm;
12988                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12989             }
12990             
12991             if(this.labelxs > 0){
12992                 labelCfg.cls += ' col-xs-' + this.labelxs;
12993                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12994             }
12995                 
12996                 
12997         } else if ( this.fieldLabel.length) {
12998 //                Roo.log(" label");
12999                  cfg.cn = [
13000                     {
13001                         tag : 'i',
13002                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13003                         tooltip : 'This field is required'
13004                     },
13005                     {
13006                         tag: 'label',
13007                         //cls : 'input-group-addon',
13008                         html : this.fieldLabel
13009                     },
13010                     combobox
13011                 ];
13012                 
13013                 if(this.indicatorpos == 'right'){
13014                     cfg.cn = [
13015                         {
13016                             tag: 'label',
13017                             //cls : 'input-group-addon',
13018                             html : this.fieldLabel
13019                         },
13020                         {
13021                             tag : 'i',
13022                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13023                             tooltip : 'This field is required'
13024                         },
13025                         combobox
13026                     ];
13027                     
13028                 }
13029
13030         } else {
13031             
13032 //                Roo.log(" no label && no align");
13033                 cfg = combobox
13034                      
13035                 
13036         }
13037          
13038         var settings=this;
13039         ['xs','sm','md','lg'].map(function(size){
13040             if (settings[size]) {
13041                 cfg.cls += ' col-' + size + '-' + settings[size];
13042             }
13043         });
13044         
13045         return cfg;
13046         
13047     },
13048     
13049     _initEventsCalled : false,
13050     
13051     // private
13052     initEvents: function()
13053     {   
13054         if (this._initEventsCalled) { // as we call render... prevent looping...
13055             return;
13056         }
13057         this._initEventsCalled = true;
13058         
13059         if (!this.store) {
13060             throw "can not find store for combo";
13061         }
13062         
13063         this.indicator = this.indicatorEl();
13064         
13065         this.store = Roo.factory(this.store, Roo.data);
13066         this.store.parent = this;
13067         
13068         // if we are building from html. then this element is so complex, that we can not really
13069         // use the rendered HTML.
13070         // so we have to trash and replace the previous code.
13071         if (Roo.XComponent.build_from_html) {
13072             // remove this element....
13073             var e = this.el.dom, k=0;
13074             while (e ) { e = e.previousSibling;  ++k;}
13075
13076             this.el.remove();
13077             
13078             this.el=false;
13079             this.rendered = false;
13080             
13081             this.render(this.parent().getChildContainer(true), k);
13082         }
13083         
13084         if(Roo.isIOS && this.useNativeIOS){
13085             this.initIOSView();
13086             return;
13087         }
13088         
13089         /*
13090          * Touch Devices
13091          */
13092         
13093         if(Roo.isTouch && this.mobileTouchView){
13094             this.initTouchView();
13095             return;
13096         }
13097         
13098         if(this.tickable){
13099             this.initTickableEvents();
13100             return;
13101         }
13102         
13103         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13104         
13105         if(this.hiddenName){
13106             
13107             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13108             
13109             this.hiddenField.dom.value =
13110                 this.hiddenValue !== undefined ? this.hiddenValue :
13111                 this.value !== undefined ? this.value : '';
13112
13113             // prevent input submission
13114             this.el.dom.removeAttribute('name');
13115             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13116              
13117              
13118         }
13119         //if(Roo.isGecko){
13120         //    this.el.dom.setAttribute('autocomplete', 'off');
13121         //}
13122         
13123         var cls = 'x-combo-list';
13124         
13125         //this.list = new Roo.Layer({
13126         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13127         //});
13128         
13129         var _this = this;
13130         
13131         (function(){
13132             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13133             _this.list.setWidth(lw);
13134         }).defer(100);
13135         
13136         this.list.on('mouseover', this.onViewOver, this);
13137         this.list.on('mousemove', this.onViewMove, this);
13138         this.list.on('scroll', this.onViewScroll, this);
13139         
13140         /*
13141         this.list.swallowEvent('mousewheel');
13142         this.assetHeight = 0;
13143
13144         if(this.title){
13145             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13146             this.assetHeight += this.header.getHeight();
13147         }
13148
13149         this.innerList = this.list.createChild({cls:cls+'-inner'});
13150         this.innerList.on('mouseover', this.onViewOver, this);
13151         this.innerList.on('mousemove', this.onViewMove, this);
13152         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13153         
13154         if(this.allowBlank && !this.pageSize && !this.disableClear){
13155             this.footer = this.list.createChild({cls:cls+'-ft'});
13156             this.pageTb = new Roo.Toolbar(this.footer);
13157            
13158         }
13159         if(this.pageSize){
13160             this.footer = this.list.createChild({cls:cls+'-ft'});
13161             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13162                     {pageSize: this.pageSize});
13163             
13164         }
13165         
13166         if (this.pageTb && this.allowBlank && !this.disableClear) {
13167             var _this = this;
13168             this.pageTb.add(new Roo.Toolbar.Fill(), {
13169                 cls: 'x-btn-icon x-btn-clear',
13170                 text: '&#160;',
13171                 handler: function()
13172                 {
13173                     _this.collapse();
13174                     _this.clearValue();
13175                     _this.onSelect(false, -1);
13176                 }
13177             });
13178         }
13179         if (this.footer) {
13180             this.assetHeight += this.footer.getHeight();
13181         }
13182         */
13183             
13184         if(!this.tpl){
13185             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13186         }
13187
13188         this.view = new Roo.View(this.list, this.tpl, {
13189             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13190         });
13191         //this.view.wrapEl.setDisplayed(false);
13192         this.view.on('click', this.onViewClick, this);
13193         
13194         
13195         this.store.on('beforeload', this.onBeforeLoad, this);
13196         this.store.on('load', this.onLoad, this);
13197         this.store.on('loadexception', this.onLoadException, this);
13198         /*
13199         if(this.resizable){
13200             this.resizer = new Roo.Resizable(this.list,  {
13201                pinned:true, handles:'se'
13202             });
13203             this.resizer.on('resize', function(r, w, h){
13204                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13205                 this.listWidth = w;
13206                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13207                 this.restrictHeight();
13208             }, this);
13209             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13210         }
13211         */
13212         if(!this.editable){
13213             this.editable = true;
13214             this.setEditable(false);
13215         }
13216         
13217         /*
13218         
13219         if (typeof(this.events.add.listeners) != 'undefined') {
13220             
13221             this.addicon = this.wrap.createChild(
13222                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13223        
13224             this.addicon.on('click', function(e) {
13225                 this.fireEvent('add', this);
13226             }, this);
13227         }
13228         if (typeof(this.events.edit.listeners) != 'undefined') {
13229             
13230             this.editicon = this.wrap.createChild(
13231                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13232             if (this.addicon) {
13233                 this.editicon.setStyle('margin-left', '40px');
13234             }
13235             this.editicon.on('click', function(e) {
13236                 
13237                 // we fire even  if inothing is selected..
13238                 this.fireEvent('edit', this, this.lastData );
13239                 
13240             }, this);
13241         }
13242         */
13243         
13244         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13245             "up" : function(e){
13246                 this.inKeyMode = true;
13247                 this.selectPrev();
13248             },
13249
13250             "down" : function(e){
13251                 if(!this.isExpanded()){
13252                     this.onTriggerClick();
13253                 }else{
13254                     this.inKeyMode = true;
13255                     this.selectNext();
13256                 }
13257             },
13258
13259             "enter" : function(e){
13260 //                this.onViewClick();
13261                 //return true;
13262                 this.collapse();
13263                 
13264                 if(this.fireEvent("specialkey", this, e)){
13265                     this.onViewClick(false);
13266                 }
13267                 
13268                 return true;
13269             },
13270
13271             "esc" : function(e){
13272                 this.collapse();
13273             },
13274
13275             "tab" : function(e){
13276                 this.collapse();
13277                 
13278                 if(this.fireEvent("specialkey", this, e)){
13279                     this.onViewClick(false);
13280                 }
13281                 
13282                 return true;
13283             },
13284
13285             scope : this,
13286
13287             doRelay : function(foo, bar, hname){
13288                 if(hname == 'down' || this.scope.isExpanded()){
13289                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13290                 }
13291                 return true;
13292             },
13293
13294             forceKeyDown: true
13295         });
13296         
13297         
13298         this.queryDelay = Math.max(this.queryDelay || 10,
13299                 this.mode == 'local' ? 10 : 250);
13300         
13301         
13302         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13303         
13304         if(this.typeAhead){
13305             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13306         }
13307         if(this.editable !== false){
13308             this.inputEl().on("keyup", this.onKeyUp, this);
13309         }
13310         if(this.forceSelection){
13311             this.inputEl().on('blur', this.doForce, this);
13312         }
13313         
13314         if(this.multiple){
13315             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13316             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13317         }
13318     },
13319     
13320     initTickableEvents: function()
13321     {   
13322         this.createList();
13323         
13324         if(this.hiddenName){
13325             
13326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13327             
13328             this.hiddenField.dom.value =
13329                 this.hiddenValue !== undefined ? this.hiddenValue :
13330                 this.value !== undefined ? this.value : '';
13331
13332             // prevent input submission
13333             this.el.dom.removeAttribute('name');
13334             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13335              
13336              
13337         }
13338         
13339 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13340         
13341         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13342         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13343         if(this.triggerList){
13344             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13345         }
13346          
13347         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13348         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13349         
13350         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13351         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13352         
13353         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13354         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13355         
13356         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13357         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13358         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13359         
13360         this.okBtn.hide();
13361         this.cancelBtn.hide();
13362         
13363         var _this = this;
13364         
13365         (function(){
13366             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13367             _this.list.setWidth(lw);
13368         }).defer(100);
13369         
13370         this.list.on('mouseover', this.onViewOver, this);
13371         this.list.on('mousemove', this.onViewMove, this);
13372         
13373         this.list.on('scroll', this.onViewScroll, this);
13374         
13375         if(!this.tpl){
13376             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>';
13377         }
13378
13379         this.view = new Roo.View(this.list, this.tpl, {
13380             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13381         });
13382         
13383         //this.view.wrapEl.setDisplayed(false);
13384         this.view.on('click', this.onViewClick, this);
13385         
13386         
13387         
13388         this.store.on('beforeload', this.onBeforeLoad, this);
13389         this.store.on('load', this.onLoad, this);
13390         this.store.on('loadexception', this.onLoadException, this);
13391         
13392         if(this.editable){
13393             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13394                 "up" : function(e){
13395                     this.inKeyMode = true;
13396                     this.selectPrev();
13397                 },
13398
13399                 "down" : function(e){
13400                     this.inKeyMode = true;
13401                     this.selectNext();
13402                 },
13403
13404                 "enter" : function(e){
13405                     if(this.fireEvent("specialkey", this, e)){
13406                         this.onViewClick(false);
13407                     }
13408                     
13409                     return true;
13410                 },
13411
13412                 "esc" : function(e){
13413                     this.onTickableFooterButtonClick(e, false, false);
13414                 },
13415
13416                 "tab" : function(e){
13417                     this.fireEvent("specialkey", this, e);
13418                     
13419                     this.onTickableFooterButtonClick(e, false, false);
13420                     
13421                     return true;
13422                 },
13423
13424                 scope : this,
13425
13426                 doRelay : function(e, fn, key){
13427                     if(this.scope.isExpanded()){
13428                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13429                     }
13430                     return true;
13431                 },
13432
13433                 forceKeyDown: true
13434             });
13435         }
13436         
13437         this.queryDelay = Math.max(this.queryDelay || 10,
13438                 this.mode == 'local' ? 10 : 250);
13439         
13440         
13441         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13442         
13443         if(this.typeAhead){
13444             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13445         }
13446         
13447         if(this.editable !== false){
13448             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13449         }
13450         
13451         this.indicator = this.indicatorEl();
13452         
13453         if(this.indicator){
13454             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13455             this.indicator.hide();
13456         }
13457         
13458     },
13459
13460     onDestroy : function(){
13461         if(this.view){
13462             this.view.setStore(null);
13463             this.view.el.removeAllListeners();
13464             this.view.el.remove();
13465             this.view.purgeListeners();
13466         }
13467         if(this.list){
13468             this.list.dom.innerHTML  = '';
13469         }
13470         
13471         if(this.store){
13472             this.store.un('beforeload', this.onBeforeLoad, this);
13473             this.store.un('load', this.onLoad, this);
13474             this.store.un('loadexception', this.onLoadException, this);
13475         }
13476         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13477     },
13478
13479     // private
13480     fireKey : function(e){
13481         if(e.isNavKeyPress() && !this.list.isVisible()){
13482             this.fireEvent("specialkey", this, e);
13483         }
13484     },
13485
13486     // private
13487     onResize: function(w, h){
13488 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13489 //        
13490 //        if(typeof w != 'number'){
13491 //            // we do not handle it!?!?
13492 //            return;
13493 //        }
13494 //        var tw = this.trigger.getWidth();
13495 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13496 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13497 //        var x = w - tw;
13498 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13499 //            
13500 //        //this.trigger.setStyle('left', x+'px');
13501 //        
13502 //        if(this.list && this.listWidth === undefined){
13503 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13504 //            this.list.setWidth(lw);
13505 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13506 //        }
13507         
13508     
13509         
13510     },
13511
13512     /**
13513      * Allow or prevent the user from directly editing the field text.  If false is passed,
13514      * the user will only be able to select from the items defined in the dropdown list.  This method
13515      * is the runtime equivalent of setting the 'editable' config option at config time.
13516      * @param {Boolean} value True to allow the user to directly edit the field text
13517      */
13518     setEditable : function(value){
13519         if(value == this.editable){
13520             return;
13521         }
13522         this.editable = value;
13523         if(!value){
13524             this.inputEl().dom.setAttribute('readOnly', true);
13525             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13526             this.inputEl().addClass('x-combo-noedit');
13527         }else{
13528             this.inputEl().dom.setAttribute('readOnly', false);
13529             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13530             this.inputEl().removeClass('x-combo-noedit');
13531         }
13532     },
13533
13534     // private
13535     
13536     onBeforeLoad : function(combo,opts){
13537         if(!this.hasFocus){
13538             return;
13539         }
13540          if (!opts.add) {
13541             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13542          }
13543         this.restrictHeight();
13544         this.selectedIndex = -1;
13545     },
13546
13547     // private
13548     onLoad : function(){
13549         
13550         this.hasQuery = false;
13551         
13552         if(!this.hasFocus){
13553             return;
13554         }
13555         
13556         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13557             this.loading.hide();
13558         }
13559         
13560         if(this.store.getCount() > 0){
13561             
13562             this.expand();
13563             this.restrictHeight();
13564             if(this.lastQuery == this.allQuery){
13565                 if(this.editable && !this.tickable){
13566                     this.inputEl().dom.select();
13567                 }
13568                 
13569                 if(
13570                     !this.selectByValue(this.value, true) &&
13571                     this.autoFocus && 
13572                     (
13573                         !this.store.lastOptions ||
13574                         typeof(this.store.lastOptions.add) == 'undefined' || 
13575                         this.store.lastOptions.add != true
13576                     )
13577                 ){
13578                     this.select(0, true);
13579                 }
13580             }else{
13581                 if(this.autoFocus){
13582                     this.selectNext();
13583                 }
13584                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13585                     this.taTask.delay(this.typeAheadDelay);
13586                 }
13587             }
13588         }else{
13589             this.onEmptyResults();
13590         }
13591         
13592         //this.el.focus();
13593     },
13594     // private
13595     onLoadException : function()
13596     {
13597         this.hasQuery = false;
13598         
13599         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13600             this.loading.hide();
13601         }
13602         
13603         if(this.tickable && this.editable){
13604             return;
13605         }
13606         
13607         this.collapse();
13608         // only causes errors at present
13609         //Roo.log(this.store.reader.jsonData);
13610         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13611             // fixme
13612             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13613         //}
13614         
13615         
13616     },
13617     // private
13618     onTypeAhead : function(){
13619         if(this.store.getCount() > 0){
13620             var r = this.store.getAt(0);
13621             var newValue = r.data[this.displayField];
13622             var len = newValue.length;
13623             var selStart = this.getRawValue().length;
13624             
13625             if(selStart != len){
13626                 this.setRawValue(newValue);
13627                 this.selectText(selStart, newValue.length);
13628             }
13629         }
13630     },
13631
13632     // private
13633     onSelect : function(record, index){
13634         
13635         if(this.fireEvent('beforeselect', this, record, index) !== false){
13636         
13637             this.setFromData(index > -1 ? record.data : false);
13638             
13639             this.collapse();
13640             this.fireEvent('select', this, record, index);
13641         }
13642     },
13643
13644     /**
13645      * Returns the currently selected field value or empty string if no value is set.
13646      * @return {String} value The selected value
13647      */
13648     getValue : function()
13649     {
13650         if(Roo.isIOS && this.useNativeIOS){
13651             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13652         }
13653         
13654         if(this.multiple){
13655             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13656         }
13657         
13658         if(this.valueField){
13659             return typeof this.value != 'undefined' ? this.value : '';
13660         }else{
13661             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13662         }
13663     },
13664     
13665     getRawValue : function()
13666     {
13667         if(Roo.isIOS && this.useNativeIOS){
13668             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13669         }
13670         
13671         var v = this.inputEl().getValue();
13672         
13673         return v;
13674     },
13675
13676     /**
13677      * Clears any text/value currently set in the field
13678      */
13679     clearValue : function(){
13680         
13681         if(this.hiddenField){
13682             this.hiddenField.dom.value = '';
13683         }
13684         this.value = '';
13685         this.setRawValue('');
13686         this.lastSelectionText = '';
13687         this.lastData = false;
13688         
13689         var close = this.closeTriggerEl();
13690         
13691         if(close){
13692             close.hide();
13693         }
13694         
13695         this.validate();
13696         
13697     },
13698
13699     /**
13700      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13701      * will be displayed in the field.  If the value does not match the data value of an existing item,
13702      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13703      * Otherwise the field will be blank (although the value will still be set).
13704      * @param {String} value The value to match
13705      */
13706     setValue : function(v)
13707     {
13708         if(Roo.isIOS && this.useNativeIOS){
13709             this.setIOSValue(v);
13710             return;
13711         }
13712         
13713         if(this.multiple){
13714             this.syncValue();
13715             return;
13716         }
13717         
13718         var text = v;
13719         if(this.valueField){
13720             var r = this.findRecord(this.valueField, v);
13721             if(r){
13722                 text = r.data[this.displayField];
13723             }else if(this.valueNotFoundText !== undefined){
13724                 text = this.valueNotFoundText;
13725             }
13726         }
13727         this.lastSelectionText = text;
13728         if(this.hiddenField){
13729             this.hiddenField.dom.value = v;
13730         }
13731         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13732         this.value = v;
13733         
13734         var close = this.closeTriggerEl();
13735         
13736         if(close){
13737             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13738         }
13739         
13740         this.validate();
13741     },
13742     /**
13743      * @property {Object} the last set data for the element
13744      */
13745     
13746     lastData : false,
13747     /**
13748      * Sets the value of the field based on a object which is related to the record format for the store.
13749      * @param {Object} value the value to set as. or false on reset?
13750      */
13751     setFromData : function(o){
13752         
13753         if(this.multiple){
13754             this.addItem(o);
13755             return;
13756         }
13757             
13758         var dv = ''; // display value
13759         var vv = ''; // value value..
13760         this.lastData = o;
13761         if (this.displayField) {
13762             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13763         } else {
13764             // this is an error condition!!!
13765             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13766         }
13767         
13768         if(this.valueField){
13769             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13770         }
13771         
13772         var close = this.closeTriggerEl();
13773         
13774         if(close){
13775             if(dv.length || vv * 1 > 0){
13776                 close.show() ;
13777                 this.blockFocus=true;
13778             } else {
13779                 close.hide();
13780             }             
13781         }
13782         
13783         if(this.hiddenField){
13784             this.hiddenField.dom.value = vv;
13785             
13786             this.lastSelectionText = dv;
13787             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13788             this.value = vv;
13789             return;
13790         }
13791         // no hidden field.. - we store the value in 'value', but still display
13792         // display field!!!!
13793         this.lastSelectionText = dv;
13794         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13795         this.value = vv;
13796         
13797         
13798         
13799     },
13800     // private
13801     reset : function(){
13802         // overridden so that last data is reset..
13803         
13804         if(this.multiple){
13805             this.clearItem();
13806             return;
13807         }
13808         
13809         this.setValue(this.originalValue);
13810         //this.clearInvalid();
13811         this.lastData = false;
13812         if (this.view) {
13813             this.view.clearSelections();
13814         }
13815         
13816         this.validate();
13817     },
13818     // private
13819     findRecord : function(prop, value){
13820         var record;
13821         if(this.store.getCount() > 0){
13822             this.store.each(function(r){
13823                 if(r.data[prop] == value){
13824                     record = r;
13825                     return false;
13826                 }
13827                 return true;
13828             });
13829         }
13830         return record;
13831     },
13832     
13833     getName: function()
13834     {
13835         // returns hidden if it's set..
13836         if (!this.rendered) {return ''};
13837         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13838         
13839     },
13840     // private
13841     onViewMove : function(e, t){
13842         this.inKeyMode = false;
13843     },
13844
13845     // private
13846     onViewOver : function(e, t){
13847         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13848             return;
13849         }
13850         var item = this.view.findItemFromChild(t);
13851         
13852         if(item){
13853             var index = this.view.indexOf(item);
13854             this.select(index, false);
13855         }
13856     },
13857
13858     // private
13859     onViewClick : function(view, doFocus, el, e)
13860     {
13861         var index = this.view.getSelectedIndexes()[0];
13862         
13863         var r = this.store.getAt(index);
13864         
13865         if(this.tickable){
13866             
13867             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13868                 return;
13869             }
13870             
13871             var rm = false;
13872             var _this = this;
13873             
13874             Roo.each(this.tickItems, function(v,k){
13875                 
13876                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13877                     Roo.log(v);
13878                     _this.tickItems.splice(k, 1);
13879                     
13880                     if(typeof(e) == 'undefined' && view == false){
13881                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13882                     }
13883                     
13884                     rm = true;
13885                     return;
13886                 }
13887             });
13888             
13889             if(rm){
13890                 return;
13891             }
13892             
13893             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13894                 this.tickItems.push(r.data);
13895             }
13896             
13897             if(typeof(e) == 'undefined' && view == false){
13898                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13899             }
13900                     
13901             return;
13902         }
13903         
13904         if(r){
13905             this.onSelect(r, index);
13906         }
13907         if(doFocus !== false && !this.blockFocus){
13908             this.inputEl().focus();
13909         }
13910     },
13911
13912     // private
13913     restrictHeight : function(){
13914         //this.innerList.dom.style.height = '';
13915         //var inner = this.innerList.dom;
13916         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13917         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13918         //this.list.beginUpdate();
13919         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13920         this.list.alignTo(this.inputEl(), this.listAlign);
13921         this.list.alignTo(this.inputEl(), this.listAlign);
13922         //this.list.endUpdate();
13923     },
13924
13925     // private
13926     onEmptyResults : function(){
13927         
13928         if(this.tickable && this.editable){
13929             this.restrictHeight();
13930             return;
13931         }
13932         
13933         this.collapse();
13934     },
13935
13936     /**
13937      * Returns true if the dropdown list is expanded, else false.
13938      */
13939     isExpanded : function(){
13940         return this.list.isVisible();
13941     },
13942
13943     /**
13944      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13946      * @param {String} value The data value of the item to select
13947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13948      * selected item if it is not currently in view (defaults to true)
13949      * @return {Boolean} True if the value matched an item in the list, else false
13950      */
13951     selectByValue : function(v, scrollIntoView){
13952         if(v !== undefined && v !== null){
13953             var r = this.findRecord(this.valueField || this.displayField, v);
13954             if(r){
13955                 this.select(this.store.indexOf(r), scrollIntoView);
13956                 return true;
13957             }
13958         }
13959         return false;
13960     },
13961
13962     /**
13963      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13964      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13965      * @param {Number} index The zero-based index of the list item to select
13966      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13967      * selected item if it is not currently in view (defaults to true)
13968      */
13969     select : function(index, scrollIntoView){
13970         this.selectedIndex = index;
13971         this.view.select(index);
13972         if(scrollIntoView !== false){
13973             var el = this.view.getNode(index);
13974             /*
13975              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13976              */
13977             if(el){
13978                 this.list.scrollChildIntoView(el, false);
13979             }
13980         }
13981     },
13982
13983     // private
13984     selectNext : function(){
13985         var ct = this.store.getCount();
13986         if(ct > 0){
13987             if(this.selectedIndex == -1){
13988                 this.select(0);
13989             }else if(this.selectedIndex < ct-1){
13990                 this.select(this.selectedIndex+1);
13991             }
13992         }
13993     },
13994
13995     // private
13996     selectPrev : function(){
13997         var ct = this.store.getCount();
13998         if(ct > 0){
13999             if(this.selectedIndex == -1){
14000                 this.select(0);
14001             }else if(this.selectedIndex != 0){
14002                 this.select(this.selectedIndex-1);
14003             }
14004         }
14005     },
14006
14007     // private
14008     onKeyUp : function(e){
14009         if(this.editable !== false && !e.isSpecialKey()){
14010             this.lastKey = e.getKey();
14011             this.dqTask.delay(this.queryDelay);
14012         }
14013     },
14014
14015     // private
14016     validateBlur : function(){
14017         return !this.list || !this.list.isVisible();   
14018     },
14019
14020     // private
14021     initQuery : function(){
14022         
14023         var v = this.getRawValue();
14024         
14025         if(this.tickable && this.editable){
14026             v = this.tickableInputEl().getValue();
14027         }
14028         
14029         this.doQuery(v);
14030     },
14031
14032     // private
14033     doForce : function(){
14034         if(this.inputEl().dom.value.length > 0){
14035             this.inputEl().dom.value =
14036                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14037              
14038         }
14039     },
14040
14041     /**
14042      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14043      * query allowing the query action to be canceled if needed.
14044      * @param {String} query The SQL query to execute
14045      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14046      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14047      * saved in the current store (defaults to false)
14048      */
14049     doQuery : function(q, forceAll){
14050         
14051         if(q === undefined || q === null){
14052             q = '';
14053         }
14054         var qe = {
14055             query: q,
14056             forceAll: forceAll,
14057             combo: this,
14058             cancel:false
14059         };
14060         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14061             return false;
14062         }
14063         q = qe.query;
14064         
14065         forceAll = qe.forceAll;
14066         if(forceAll === true || (q.length >= this.minChars)){
14067             
14068             this.hasQuery = true;
14069             
14070             if(this.lastQuery != q || this.alwaysQuery){
14071                 this.lastQuery = q;
14072                 if(this.mode == 'local'){
14073                     this.selectedIndex = -1;
14074                     if(forceAll){
14075                         this.store.clearFilter();
14076                     }else{
14077                         
14078                         if(this.specialFilter){
14079                             this.fireEvent('specialfilter', this);
14080                             this.onLoad();
14081                             return;
14082                         }
14083                         
14084                         this.store.filter(this.displayField, q);
14085                     }
14086                     
14087                     this.store.fireEvent("datachanged", this.store);
14088                     
14089                     this.onLoad();
14090                     
14091                     
14092                 }else{
14093                     
14094                     this.store.baseParams[this.queryParam] = q;
14095                     
14096                     var options = {params : this.getParams(q)};
14097                     
14098                     if(this.loadNext){
14099                         options.add = true;
14100                         options.params.start = this.page * this.pageSize;
14101                     }
14102                     
14103                     this.store.load(options);
14104                     
14105                     /*
14106                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14107                      *  we should expand the list on onLoad
14108                      *  so command out it
14109                      */
14110 //                    this.expand();
14111                 }
14112             }else{
14113                 this.selectedIndex = -1;
14114                 this.onLoad();   
14115             }
14116         }
14117         
14118         this.loadNext = false;
14119     },
14120     
14121     // private
14122     getParams : function(q){
14123         var p = {};
14124         //p[this.queryParam] = q;
14125         
14126         if(this.pageSize){
14127             p.start = 0;
14128             p.limit = this.pageSize;
14129         }
14130         return p;
14131     },
14132
14133     /**
14134      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14135      */
14136     collapse : function(){
14137         if(!this.isExpanded()){
14138             return;
14139         }
14140         
14141         this.list.hide();
14142         
14143         this.hasFocus = false;
14144         
14145         if(this.tickable){
14146             this.okBtn.hide();
14147             this.cancelBtn.hide();
14148             this.trigger.show();
14149             
14150             if(this.editable){
14151                 this.tickableInputEl().dom.value = '';
14152                 this.tickableInputEl().blur();
14153             }
14154             
14155         }
14156         
14157         Roo.get(document).un('mousedown', this.collapseIf, this);
14158         Roo.get(document).un('mousewheel', this.collapseIf, this);
14159         if (!this.editable) {
14160             Roo.get(document).un('keydown', this.listKeyPress, this);
14161         }
14162         this.fireEvent('collapse', this);
14163         
14164         this.validate();
14165     },
14166
14167     // private
14168     collapseIf : function(e){
14169         var in_combo  = e.within(this.el);
14170         var in_list =  e.within(this.list);
14171         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14172         
14173         if (in_combo || in_list || is_list) {
14174             //e.stopPropagation();
14175             return;
14176         }
14177         
14178         if(this.tickable){
14179             this.onTickableFooterButtonClick(e, false, false);
14180         }
14181
14182         this.collapse();
14183         
14184     },
14185
14186     /**
14187      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14188      */
14189     expand : function(){
14190        
14191         if(this.isExpanded() || !this.hasFocus){
14192             return;
14193         }
14194         
14195         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14196         this.list.setWidth(lw);
14197         
14198         Roo.log('expand');
14199         
14200         this.list.show();
14201         
14202         this.restrictHeight();
14203         
14204         if(this.tickable){
14205             
14206             this.tickItems = Roo.apply([], this.item);
14207             
14208             this.okBtn.show();
14209             this.cancelBtn.show();
14210             this.trigger.hide();
14211             
14212             if(this.editable){
14213                 this.tickableInputEl().focus();
14214             }
14215             
14216         }
14217         
14218         Roo.get(document).on('mousedown', this.collapseIf, this);
14219         Roo.get(document).on('mousewheel', this.collapseIf, this);
14220         if (!this.editable) {
14221             Roo.get(document).on('keydown', this.listKeyPress, this);
14222         }
14223         
14224         this.fireEvent('expand', this);
14225     },
14226
14227     // private
14228     // Implements the default empty TriggerField.onTriggerClick function
14229     onTriggerClick : function(e)
14230     {
14231         Roo.log('trigger click');
14232         
14233         if(this.disabled || !this.triggerList){
14234             return;
14235         }
14236         
14237         this.page = 0;
14238         this.loadNext = false;
14239         
14240         if(this.isExpanded()){
14241             this.collapse();
14242             if (!this.blockFocus) {
14243                 this.inputEl().focus();
14244             }
14245             
14246         }else {
14247             this.hasFocus = true;
14248             if(this.triggerAction == 'all') {
14249                 this.doQuery(this.allQuery, true);
14250             } else {
14251                 this.doQuery(this.getRawValue());
14252             }
14253             if (!this.blockFocus) {
14254                 this.inputEl().focus();
14255             }
14256         }
14257     },
14258     
14259     onTickableTriggerClick : function(e)
14260     {
14261         if(this.disabled){
14262             return;
14263         }
14264         
14265         this.page = 0;
14266         this.loadNext = false;
14267         this.hasFocus = true;
14268         
14269         if(this.triggerAction == 'all') {
14270             this.doQuery(this.allQuery, true);
14271         } else {
14272             this.doQuery(this.getRawValue());
14273         }
14274     },
14275     
14276     onSearchFieldClick : function(e)
14277     {
14278         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14279             this.onTickableFooterButtonClick(e, false, false);
14280             return;
14281         }
14282         
14283         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14284             return;
14285         }
14286         
14287         this.page = 0;
14288         this.loadNext = false;
14289         this.hasFocus = true;
14290         
14291         if(this.triggerAction == 'all') {
14292             this.doQuery(this.allQuery, true);
14293         } else {
14294             this.doQuery(this.getRawValue());
14295         }
14296     },
14297     
14298     listKeyPress : function(e)
14299     {
14300         //Roo.log('listkeypress');
14301         // scroll to first matching element based on key pres..
14302         if (e.isSpecialKey()) {
14303             return false;
14304         }
14305         var k = String.fromCharCode(e.getKey()).toUpperCase();
14306         //Roo.log(k);
14307         var match  = false;
14308         var csel = this.view.getSelectedNodes();
14309         var cselitem = false;
14310         if (csel.length) {
14311             var ix = this.view.indexOf(csel[0]);
14312             cselitem  = this.store.getAt(ix);
14313             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14314                 cselitem = false;
14315             }
14316             
14317         }
14318         
14319         this.store.each(function(v) { 
14320             if (cselitem) {
14321                 // start at existing selection.
14322                 if (cselitem.id == v.id) {
14323                     cselitem = false;
14324                 }
14325                 return true;
14326             }
14327                 
14328             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14329                 match = this.store.indexOf(v);
14330                 return false;
14331             }
14332             return true;
14333         }, this);
14334         
14335         if (match === false) {
14336             return true; // no more action?
14337         }
14338         // scroll to?
14339         this.view.select(match);
14340         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14341         sn.scrollIntoView(sn.dom.parentNode, false);
14342     },
14343     
14344     onViewScroll : function(e, t){
14345         
14346         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){
14347             return;
14348         }
14349         
14350         this.hasQuery = true;
14351         
14352         this.loading = this.list.select('.loading', true).first();
14353         
14354         if(this.loading === null){
14355             this.list.createChild({
14356                 tag: 'div',
14357                 cls: 'loading roo-select2-more-results roo-select2-active',
14358                 html: 'Loading more results...'
14359             });
14360             
14361             this.loading = this.list.select('.loading', true).first();
14362             
14363             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14364             
14365             this.loading.hide();
14366         }
14367         
14368         this.loading.show();
14369         
14370         var _combo = this;
14371         
14372         this.page++;
14373         this.loadNext = true;
14374         
14375         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14376         
14377         return;
14378     },
14379     
14380     addItem : function(o)
14381     {   
14382         var dv = ''; // display value
14383         
14384         if (this.displayField) {
14385             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14386         } else {
14387             // this is an error condition!!!
14388             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14389         }
14390         
14391         if(!dv.length){
14392             return;
14393         }
14394         
14395         var choice = this.choices.createChild({
14396             tag: 'li',
14397             cls: 'roo-select2-search-choice',
14398             cn: [
14399                 {
14400                     tag: 'div',
14401                     html: dv
14402                 },
14403                 {
14404                     tag: 'a',
14405                     href: '#',
14406                     cls: 'roo-select2-search-choice-close fa fa-times',
14407                     tabindex: '-1'
14408                 }
14409             ]
14410             
14411         }, this.searchField);
14412         
14413         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14414         
14415         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14416         
14417         this.item.push(o);
14418         
14419         this.lastData = o;
14420         
14421         this.syncValue();
14422         
14423         this.inputEl().dom.value = '';
14424         
14425         this.validate();
14426     },
14427     
14428     onRemoveItem : function(e, _self, o)
14429     {
14430         e.preventDefault();
14431         
14432         this.lastItem = Roo.apply([], this.item);
14433         
14434         var index = this.item.indexOf(o.data) * 1;
14435         
14436         if( index < 0){
14437             Roo.log('not this item?!');
14438             return;
14439         }
14440         
14441         this.item.splice(index, 1);
14442         o.item.remove();
14443         
14444         this.syncValue();
14445         
14446         this.fireEvent('remove', this, e);
14447         
14448         this.validate();
14449         
14450     },
14451     
14452     syncValue : function()
14453     {
14454         if(!this.item.length){
14455             this.clearValue();
14456             return;
14457         }
14458             
14459         var value = [];
14460         var _this = this;
14461         Roo.each(this.item, function(i){
14462             if(_this.valueField){
14463                 value.push(i[_this.valueField]);
14464                 return;
14465             }
14466
14467             value.push(i);
14468         });
14469
14470         this.value = value.join(',');
14471
14472         if(this.hiddenField){
14473             this.hiddenField.dom.value = this.value;
14474         }
14475         
14476         this.store.fireEvent("datachanged", this.store);
14477         
14478         this.validate();
14479     },
14480     
14481     clearItem : function()
14482     {
14483         if(!this.multiple){
14484             return;
14485         }
14486         
14487         this.item = [];
14488         
14489         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14490            c.remove();
14491         });
14492         
14493         this.syncValue();
14494         
14495         this.validate();
14496         
14497         if(this.tickable && !Roo.isTouch){
14498             this.view.refresh();
14499         }
14500     },
14501     
14502     inputEl: function ()
14503     {
14504         if(Roo.isIOS && this.useNativeIOS){
14505             return this.el.select('select.roo-ios-select', true).first();
14506         }
14507         
14508         if(Roo.isTouch && this.mobileTouchView){
14509             return this.el.select('input.form-control',true).first();
14510         }
14511         
14512         if(this.tickable){
14513             return this.searchField;
14514         }
14515         
14516         return this.el.select('input.form-control',true).first();
14517     },
14518     
14519     onTickableFooterButtonClick : function(e, btn, el)
14520     {
14521         e.preventDefault();
14522         
14523         this.lastItem = Roo.apply([], this.item);
14524         
14525         if(btn && btn.name == 'cancel'){
14526             this.tickItems = Roo.apply([], this.item);
14527             this.collapse();
14528             return;
14529         }
14530         
14531         this.clearItem();
14532         
14533         var _this = this;
14534         
14535         Roo.each(this.tickItems, function(o){
14536             _this.addItem(o);
14537         });
14538         
14539         this.collapse();
14540         
14541     },
14542     
14543     validate : function()
14544     {
14545         var v = this.getRawValue();
14546         
14547         if(this.multiple){
14548             v = this.getValue();
14549         }
14550         
14551         if(this.disabled || this.allowBlank || v.length){
14552             this.markValid();
14553             return true;
14554         }
14555         
14556         this.markInvalid();
14557         return false;
14558     },
14559     
14560     tickableInputEl : function()
14561     {
14562         if(!this.tickable || !this.editable){
14563             return this.inputEl();
14564         }
14565         
14566         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14567     },
14568     
14569     
14570     getAutoCreateTouchView : function()
14571     {
14572         var id = Roo.id();
14573         
14574         var cfg = {
14575             cls: 'form-group' //input-group
14576         };
14577         
14578         var input =  {
14579             tag: 'input',
14580             id : id,
14581             type : this.inputType,
14582             cls : 'form-control x-combo-noedit',
14583             autocomplete: 'new-password',
14584             placeholder : this.placeholder || '',
14585             readonly : true
14586         };
14587         
14588         if (this.name) {
14589             input.name = this.name;
14590         }
14591         
14592         if (this.size) {
14593             input.cls += ' input-' + this.size;
14594         }
14595         
14596         if (this.disabled) {
14597             input.disabled = true;
14598         }
14599         
14600         var inputblock = {
14601             cls : '',
14602             cn : [
14603                 input
14604             ]
14605         };
14606         
14607         if(this.before){
14608             inputblock.cls += ' input-group';
14609             
14610             inputblock.cn.unshift({
14611                 tag :'span',
14612                 cls : 'input-group-addon',
14613                 html : this.before
14614             });
14615         }
14616         
14617         if(this.removable && !this.multiple){
14618             inputblock.cls += ' roo-removable';
14619             
14620             inputblock.cn.push({
14621                 tag: 'button',
14622                 html : 'x',
14623                 cls : 'roo-combo-removable-btn close'
14624             });
14625         }
14626
14627         if(this.hasFeedback && !this.allowBlank){
14628             
14629             inputblock.cls += ' has-feedback';
14630             
14631             inputblock.cn.push({
14632                 tag: 'span',
14633                 cls: 'glyphicon form-control-feedback'
14634             });
14635             
14636         }
14637         
14638         if (this.after) {
14639             
14640             inputblock.cls += (this.before) ? '' : ' input-group';
14641             
14642             inputblock.cn.push({
14643                 tag :'span',
14644                 cls : 'input-group-addon',
14645                 html : this.after
14646             });
14647         }
14648
14649         var box = {
14650             tag: 'div',
14651             cn: [
14652                 {
14653                     tag: 'input',
14654                     type : 'hidden',
14655                     cls: 'form-hidden-field'
14656                 },
14657                 inputblock
14658             ]
14659             
14660         };
14661         
14662         if(this.multiple){
14663             box = {
14664                 tag: 'div',
14665                 cn: [
14666                     {
14667                         tag: 'input',
14668                         type : 'hidden',
14669                         cls: 'form-hidden-field'
14670                     },
14671                     {
14672                         tag: 'ul',
14673                         cls: 'roo-select2-choices',
14674                         cn:[
14675                             {
14676                                 tag: 'li',
14677                                 cls: 'roo-select2-search-field',
14678                                 cn: [
14679
14680                                     inputblock
14681                                 ]
14682                             }
14683                         ]
14684                     }
14685                 ]
14686             }
14687         };
14688         
14689         var combobox = {
14690             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14691             cn: [
14692                 box
14693             ]
14694         };
14695         
14696         if(!this.multiple && this.showToggleBtn){
14697             
14698             var caret = {
14699                         tag: 'span',
14700                         cls: 'caret'
14701             };
14702             
14703             if (this.caret != false) {
14704                 caret = {
14705                      tag: 'i',
14706                      cls: 'fa fa-' + this.caret
14707                 };
14708                 
14709             }
14710             
14711             combobox.cn.push({
14712                 tag :'span',
14713                 cls : 'input-group-addon btn dropdown-toggle',
14714                 cn : [
14715                     caret,
14716                     {
14717                         tag: 'span',
14718                         cls: 'combobox-clear',
14719                         cn  : [
14720                             {
14721                                 tag : 'i',
14722                                 cls: 'icon-remove'
14723                             }
14724                         ]
14725                     }
14726                 ]
14727
14728             })
14729         }
14730         
14731         if(this.multiple){
14732             combobox.cls += ' roo-select2-container-multi';
14733         }
14734         
14735         var align = this.labelAlign || this.parentLabelAlign();
14736         
14737         if (align ==='left' && this.fieldLabel.length) {
14738
14739             cfg.cn = [
14740                 {
14741                    tag : 'i',
14742                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14743                    tooltip : 'This field is required'
14744                 },
14745                 {
14746                     tag: 'label',
14747                     cls : 'control-label',
14748                     html : this.fieldLabel
14749
14750                 },
14751                 {
14752                     cls : '', 
14753                     cn: [
14754                         combobox
14755                     ]
14756                 }
14757             ];
14758             
14759             var labelCfg = cfg.cn[1];
14760             var contentCfg = cfg.cn[2];
14761             
14762
14763             if(this.indicatorpos == 'right'){
14764                 cfg.cn = [
14765                     {
14766                         tag: 'label',
14767                         'for' :  id,
14768                         cls : 'control-label',
14769                         cn : [
14770                             {
14771                                 tag : 'span',
14772                                 html : this.fieldLabel
14773                             },
14774                             {
14775                                 tag : 'i',
14776                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14777                                 tooltip : 'This field is required'
14778                             }
14779                         ]
14780                     },
14781                     {
14782                         cls : "",
14783                         cn: [
14784                             combobox
14785                         ]
14786                     }
14787
14788                 ];
14789                 
14790                 labelCfg = cfg.cn[0];
14791                 contentCfg = cfg.cn[1];
14792             }
14793             
14794            
14795             
14796             if(this.labelWidth > 12){
14797                 labelCfg.style = "width: " + this.labelWidth + 'px';
14798             }
14799             
14800             if(this.labelWidth < 13 && this.labelmd == 0){
14801                 this.labelmd = this.labelWidth;
14802             }
14803             
14804             if(this.labellg > 0){
14805                 labelCfg.cls += ' col-lg-' + this.labellg;
14806                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14807             }
14808             
14809             if(this.labelmd > 0){
14810                 labelCfg.cls += ' col-md-' + this.labelmd;
14811                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14812             }
14813             
14814             if(this.labelsm > 0){
14815                 labelCfg.cls += ' col-sm-' + this.labelsm;
14816                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14817             }
14818             
14819             if(this.labelxs > 0){
14820                 labelCfg.cls += ' col-xs-' + this.labelxs;
14821                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14822             }
14823                 
14824                 
14825         } else if ( this.fieldLabel.length) {
14826             cfg.cn = [
14827                 {
14828                    tag : 'i',
14829                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14830                    tooltip : 'This field is required'
14831                 },
14832                 {
14833                     tag: 'label',
14834                     cls : 'control-label',
14835                     html : this.fieldLabel
14836
14837                 },
14838                 {
14839                     cls : '', 
14840                     cn: [
14841                         combobox
14842                     ]
14843                 }
14844             ];
14845             
14846             if(this.indicatorpos == 'right'){
14847                 cfg.cn = [
14848                     {
14849                         tag: 'label',
14850                         cls : 'control-label',
14851                         html : this.fieldLabel,
14852                         cn : [
14853                             {
14854                                tag : 'i',
14855                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14856                                tooltip : 'This field is required'
14857                             }
14858                         ]
14859                     },
14860                     {
14861                         cls : '', 
14862                         cn: [
14863                             combobox
14864                         ]
14865                     }
14866                 ];
14867             }
14868         } else {
14869             cfg.cn = combobox;    
14870         }
14871         
14872         
14873         var settings = this;
14874         
14875         ['xs','sm','md','lg'].map(function(size){
14876             if (settings[size]) {
14877                 cfg.cls += ' col-' + size + '-' + settings[size];
14878             }
14879         });
14880         
14881         return cfg;
14882     },
14883     
14884     initTouchView : function()
14885     {
14886         this.renderTouchView();
14887         
14888         this.touchViewEl.on('scroll', function(){
14889             this.el.dom.scrollTop = 0;
14890         }, this);
14891         
14892         this.originalValue = this.getValue();
14893         
14894         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14895         
14896         this.inputEl().on("click", this.showTouchView, this);
14897         if (this.triggerEl) {
14898             this.triggerEl.on("click", this.showTouchView, this);
14899         }
14900         
14901         
14902         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14903         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14904         
14905         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14906         
14907         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14908         this.store.on('load', this.onTouchViewLoad, this);
14909         this.store.on('loadexception', this.onTouchViewLoadException, this);
14910         
14911         if(this.hiddenName){
14912             
14913             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14914             
14915             this.hiddenField.dom.value =
14916                 this.hiddenValue !== undefined ? this.hiddenValue :
14917                 this.value !== undefined ? this.value : '';
14918         
14919             this.el.dom.removeAttribute('name');
14920             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14921         }
14922         
14923         if(this.multiple){
14924             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14925             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14926         }
14927         
14928         if(this.removable && !this.multiple){
14929             var close = this.closeTriggerEl();
14930             if(close){
14931                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14932                 close.on('click', this.removeBtnClick, this, close);
14933             }
14934         }
14935         /*
14936          * fix the bug in Safari iOS8
14937          */
14938         this.inputEl().on("focus", function(e){
14939             document.activeElement.blur();
14940         }, this);
14941         
14942         return;
14943         
14944         
14945     },
14946     
14947     renderTouchView : function()
14948     {
14949         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14950         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14951         
14952         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14953         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14954         
14955         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14956         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14957         this.touchViewBodyEl.setStyle('overflow', 'auto');
14958         
14959         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14960         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14961         
14962         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14963         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14964         
14965     },
14966     
14967     showTouchView : function()
14968     {
14969         if(this.disabled){
14970             return;
14971         }
14972         
14973         this.touchViewHeaderEl.hide();
14974
14975         if(this.modalTitle.length){
14976             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14977             this.touchViewHeaderEl.show();
14978         }
14979
14980         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14981         this.touchViewEl.show();
14982
14983         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14984         
14985         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14986         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14987
14988         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14989
14990         if(this.modalTitle.length){
14991             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14992         }
14993         
14994         this.touchViewBodyEl.setHeight(bodyHeight);
14995
14996         if(this.animate){
14997             var _this = this;
14998             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14999         }else{
15000             this.touchViewEl.addClass('in');
15001         }
15002
15003         this.doTouchViewQuery();
15004         
15005     },
15006     
15007     hideTouchView : function()
15008     {
15009         this.touchViewEl.removeClass('in');
15010
15011         if(this.animate){
15012             var _this = this;
15013             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15014         }else{
15015             this.touchViewEl.setStyle('display', 'none');
15016         }
15017         
15018     },
15019     
15020     setTouchViewValue : function()
15021     {
15022         if(this.multiple){
15023             this.clearItem();
15024         
15025             var _this = this;
15026
15027             Roo.each(this.tickItems, function(o){
15028                 this.addItem(o);
15029             }, this);
15030         }
15031         
15032         this.hideTouchView();
15033     },
15034     
15035     doTouchViewQuery : function()
15036     {
15037         var qe = {
15038             query: '',
15039             forceAll: true,
15040             combo: this,
15041             cancel:false
15042         };
15043         
15044         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15045             return false;
15046         }
15047         
15048         if(!this.alwaysQuery || this.mode == 'local'){
15049             this.onTouchViewLoad();
15050             return;
15051         }
15052         
15053         this.store.load();
15054     },
15055     
15056     onTouchViewBeforeLoad : function(combo,opts)
15057     {
15058         return;
15059     },
15060
15061     // private
15062     onTouchViewLoad : function()
15063     {
15064         if(this.store.getCount() < 1){
15065             this.onTouchViewEmptyResults();
15066             return;
15067         }
15068         
15069         this.clearTouchView();
15070         
15071         var rawValue = this.getRawValue();
15072         
15073         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15074         
15075         this.tickItems = [];
15076         
15077         this.store.data.each(function(d, rowIndex){
15078             var row = this.touchViewListGroup.createChild(template);
15079             
15080             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15081                 row.addClass(d.data.cls);
15082             }
15083             
15084             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15085                 var cfg = {
15086                     data : d.data,
15087                     html : d.data[this.displayField]
15088                 };
15089                 
15090                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15091                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15092                 }
15093             }
15094             row.removeClass('selected');
15095             if(!this.multiple && this.valueField &&
15096                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15097             {
15098                 // radio buttons..
15099                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15100                 row.addClass('selected');
15101             }
15102             
15103             if(this.multiple && this.valueField &&
15104                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15105             {
15106                 
15107                 // checkboxes...
15108                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15109                 this.tickItems.push(d.data);
15110             }
15111             
15112             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15113             
15114         }, this);
15115         
15116         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15117         
15118         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15119
15120         if(this.modalTitle.length){
15121             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15122         }
15123
15124         var listHeight = this.touchViewListGroup.getHeight();
15125         
15126         var _this = this;
15127         
15128         if(firstChecked && listHeight > bodyHeight){
15129             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15130         }
15131         
15132     },
15133     
15134     onTouchViewLoadException : function()
15135     {
15136         this.hideTouchView();
15137     },
15138     
15139     onTouchViewEmptyResults : function()
15140     {
15141         this.clearTouchView();
15142         
15143         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15144         
15145         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15146         
15147     },
15148     
15149     clearTouchView : function()
15150     {
15151         this.touchViewListGroup.dom.innerHTML = '';
15152     },
15153     
15154     onTouchViewClick : function(e, el, o)
15155     {
15156         e.preventDefault();
15157         
15158         var row = o.row;
15159         var rowIndex = o.rowIndex;
15160         
15161         var r = this.store.getAt(rowIndex);
15162         
15163         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15164             
15165             if(!this.multiple){
15166                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15167                     c.dom.removeAttribute('checked');
15168                 }, this);
15169
15170                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15171
15172                 this.setFromData(r.data);
15173
15174                 var close = this.closeTriggerEl();
15175
15176                 if(close){
15177                     close.show();
15178                 }
15179
15180                 this.hideTouchView();
15181
15182                 this.fireEvent('select', this, r, rowIndex);
15183
15184                 return;
15185             }
15186
15187             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15188                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15189                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15190                 return;
15191             }
15192
15193             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15194             this.addItem(r.data);
15195             this.tickItems.push(r.data);
15196         }
15197     },
15198     
15199     getAutoCreateNativeIOS : function()
15200     {
15201         var cfg = {
15202             cls: 'form-group' //input-group,
15203         };
15204         
15205         var combobox =  {
15206             tag: 'select',
15207             cls : 'roo-ios-select'
15208         };
15209         
15210         if (this.name) {
15211             combobox.name = this.name;
15212         }
15213         
15214         if (this.disabled) {
15215             combobox.disabled = true;
15216         }
15217         
15218         var settings = this;
15219         
15220         ['xs','sm','md','lg'].map(function(size){
15221             if (settings[size]) {
15222                 cfg.cls += ' col-' + size + '-' + settings[size];
15223             }
15224         });
15225         
15226         cfg.cn = combobox;
15227         
15228         return cfg;
15229         
15230     },
15231     
15232     initIOSView : function()
15233     {
15234         this.store.on('load', this.onIOSViewLoad, this);
15235         
15236         return;
15237     },
15238     
15239     onIOSViewLoad : function()
15240     {
15241         if(this.store.getCount() < 1){
15242             return;
15243         }
15244         
15245         this.clearIOSView();
15246         
15247         if(this.allowBlank) {
15248             
15249             var default_text = '-- SELECT --';
15250             
15251             if(this.placeholder.length){
15252                 default_text = this.placeholder;
15253             }
15254             
15255             if(this.emptyTitle.length){
15256                 default_text += ' - ' + this.emptyTitle + ' -';
15257             }
15258             
15259             var opt = this.inputEl().createChild({
15260                 tag: 'option',
15261                 value : 0,
15262                 html : default_text
15263             });
15264             
15265             var o = {};
15266             o[this.valueField] = 0;
15267             o[this.displayField] = default_text;
15268             
15269             this.ios_options.push({
15270                 data : o,
15271                 el : opt
15272             });
15273             
15274         }
15275         
15276         this.store.data.each(function(d, rowIndex){
15277             
15278             var html = '';
15279             
15280             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15281                 html = d.data[this.displayField];
15282             }
15283             
15284             var value = '';
15285             
15286             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15287                 value = d.data[this.valueField];
15288             }
15289             
15290             var option = {
15291                 tag: 'option',
15292                 value : value,
15293                 html : html
15294             };
15295             
15296             if(this.value == d.data[this.valueField]){
15297                 option['selected'] = true;
15298             }
15299             
15300             var opt = this.inputEl().createChild(option);
15301             
15302             this.ios_options.push({
15303                 data : d.data,
15304                 el : opt
15305             });
15306             
15307         }, this);
15308         
15309         this.inputEl().on('change', function(){
15310            this.fireEvent('select', this);
15311         }, this);
15312         
15313     },
15314     
15315     clearIOSView: function()
15316     {
15317         this.inputEl().dom.innerHTML = '';
15318         
15319         this.ios_options = [];
15320     },
15321     
15322     setIOSValue: function(v)
15323     {
15324         this.value = v;
15325         
15326         if(!this.ios_options){
15327             return;
15328         }
15329         
15330         Roo.each(this.ios_options, function(opts){
15331            
15332            opts.el.dom.removeAttribute('selected');
15333            
15334            if(opts.data[this.valueField] != v){
15335                return;
15336            }
15337            
15338            opts.el.dom.setAttribute('selected', true);
15339            
15340         }, this);
15341     }
15342
15343     /** 
15344     * @cfg {Boolean} grow 
15345     * @hide 
15346     */
15347     /** 
15348     * @cfg {Number} growMin 
15349     * @hide 
15350     */
15351     /** 
15352     * @cfg {Number} growMax 
15353     * @hide 
15354     */
15355     /**
15356      * @hide
15357      * @method autoSize
15358      */
15359 });
15360
15361 Roo.apply(Roo.bootstrap.ComboBox,  {
15362     
15363     header : {
15364         tag: 'div',
15365         cls: 'modal-header',
15366         cn: [
15367             {
15368                 tag: 'h4',
15369                 cls: 'modal-title'
15370             }
15371         ]
15372     },
15373     
15374     body : {
15375         tag: 'div',
15376         cls: 'modal-body',
15377         cn: [
15378             {
15379                 tag: 'ul',
15380                 cls: 'list-group'
15381             }
15382         ]
15383     },
15384     
15385     listItemRadio : {
15386         tag: 'li',
15387         cls: 'list-group-item',
15388         cn: [
15389             {
15390                 tag: 'span',
15391                 cls: 'roo-combobox-list-group-item-value'
15392             },
15393             {
15394                 tag: 'div',
15395                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15396                 cn: [
15397                     {
15398                         tag: 'input',
15399                         type: 'radio'
15400                     },
15401                     {
15402                         tag: 'label'
15403                     }
15404                 ]
15405             }
15406         ]
15407     },
15408     
15409     listItemCheckbox : {
15410         tag: 'li',
15411         cls: 'list-group-item',
15412         cn: [
15413             {
15414                 tag: 'span',
15415                 cls: 'roo-combobox-list-group-item-value'
15416             },
15417             {
15418                 tag: 'div',
15419                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15420                 cn: [
15421                     {
15422                         tag: 'input',
15423                         type: 'checkbox'
15424                     },
15425                     {
15426                         tag: 'label'
15427                     }
15428                 ]
15429             }
15430         ]
15431     },
15432     
15433     emptyResult : {
15434         tag: 'div',
15435         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15436     },
15437     
15438     footer : {
15439         tag: 'div',
15440         cls: 'modal-footer',
15441         cn: [
15442             {
15443                 tag: 'div',
15444                 cls: 'row',
15445                 cn: [
15446                     {
15447                         tag: 'div',
15448                         cls: 'col-xs-6 text-left',
15449                         cn: {
15450                             tag: 'button',
15451                             cls: 'btn btn-danger roo-touch-view-cancel',
15452                             html: 'Cancel'
15453                         }
15454                     },
15455                     {
15456                         tag: 'div',
15457                         cls: 'col-xs-6 text-right',
15458                         cn: {
15459                             tag: 'button',
15460                             cls: 'btn btn-success roo-touch-view-ok',
15461                             html: 'OK'
15462                         }
15463                     }
15464                 ]
15465             }
15466         ]
15467         
15468     }
15469 });
15470
15471 Roo.apply(Roo.bootstrap.ComboBox,  {
15472     
15473     touchViewTemplate : {
15474         tag: 'div',
15475         cls: 'modal fade roo-combobox-touch-view',
15476         cn: [
15477             {
15478                 tag: 'div',
15479                 cls: 'modal-dialog',
15480                 style : 'position:fixed', // we have to fix position....
15481                 cn: [
15482                     {
15483                         tag: 'div',
15484                         cls: 'modal-content',
15485                         cn: [
15486                             Roo.bootstrap.ComboBox.header,
15487                             Roo.bootstrap.ComboBox.body,
15488                             Roo.bootstrap.ComboBox.footer
15489                         ]
15490                     }
15491                 ]
15492             }
15493         ]
15494     }
15495 });/*
15496  * Based on:
15497  * Ext JS Library 1.1.1
15498  * Copyright(c) 2006-2007, Ext JS, LLC.
15499  *
15500  * Originally Released Under LGPL - original licence link has changed is not relivant.
15501  *
15502  * Fork - LGPL
15503  * <script type="text/javascript">
15504  */
15505
15506 /**
15507  * @class Roo.View
15508  * @extends Roo.util.Observable
15509  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15510  * This class also supports single and multi selection modes. <br>
15511  * Create a data model bound view:
15512  <pre><code>
15513  var store = new Roo.data.Store(...);
15514
15515  var view = new Roo.View({
15516     el : "my-element",
15517     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15518  
15519     singleSelect: true,
15520     selectedClass: "ydataview-selected",
15521     store: store
15522  });
15523
15524  // listen for node click?
15525  view.on("click", function(vw, index, node, e){
15526  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15527  });
15528
15529  // load XML data
15530  dataModel.load("foobar.xml");
15531  </code></pre>
15532  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15533  * <br><br>
15534  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15535  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15536  * 
15537  * Note: old style constructor is still suported (container, template, config)
15538  * 
15539  * @constructor
15540  * Create a new View
15541  * @param {Object} config The config object
15542  * 
15543  */
15544 Roo.View = function(config, depreciated_tpl, depreciated_config){
15545     
15546     this.parent = false;
15547     
15548     if (typeof(depreciated_tpl) == 'undefined') {
15549         // new way.. - universal constructor.
15550         Roo.apply(this, config);
15551         this.el  = Roo.get(this.el);
15552     } else {
15553         // old format..
15554         this.el  = Roo.get(config);
15555         this.tpl = depreciated_tpl;
15556         Roo.apply(this, depreciated_config);
15557     }
15558     this.wrapEl  = this.el.wrap().wrap();
15559     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15560     
15561     
15562     if(typeof(this.tpl) == "string"){
15563         this.tpl = new Roo.Template(this.tpl);
15564     } else {
15565         // support xtype ctors..
15566         this.tpl = new Roo.factory(this.tpl, Roo);
15567     }
15568     
15569     
15570     this.tpl.compile();
15571     
15572     /** @private */
15573     this.addEvents({
15574         /**
15575          * @event beforeclick
15576          * Fires before a click is processed. Returns false to cancel the default action.
15577          * @param {Roo.View} this
15578          * @param {Number} index The index of the target node
15579          * @param {HTMLElement} node The target node
15580          * @param {Roo.EventObject} e The raw event object
15581          */
15582             "beforeclick" : true,
15583         /**
15584          * @event click
15585          * Fires when a template node is clicked.
15586          * @param {Roo.View} this
15587          * @param {Number} index The index of the target node
15588          * @param {HTMLElement} node The target node
15589          * @param {Roo.EventObject} e The raw event object
15590          */
15591             "click" : true,
15592         /**
15593          * @event dblclick
15594          * Fires when a template node is double clicked.
15595          * @param {Roo.View} this
15596          * @param {Number} index The index of the target node
15597          * @param {HTMLElement} node The target node
15598          * @param {Roo.EventObject} e The raw event object
15599          */
15600             "dblclick" : true,
15601         /**
15602          * @event contextmenu
15603          * Fires when a template node is right clicked.
15604          * @param {Roo.View} this
15605          * @param {Number} index The index of the target node
15606          * @param {HTMLElement} node The target node
15607          * @param {Roo.EventObject} e The raw event object
15608          */
15609             "contextmenu" : true,
15610         /**
15611          * @event selectionchange
15612          * Fires when the selected nodes change.
15613          * @param {Roo.View} this
15614          * @param {Array} selections Array of the selected nodes
15615          */
15616             "selectionchange" : true,
15617     
15618         /**
15619          * @event beforeselect
15620          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15621          * @param {Roo.View} this
15622          * @param {HTMLElement} node The node to be selected
15623          * @param {Array} selections Array of currently selected nodes
15624          */
15625             "beforeselect" : true,
15626         /**
15627          * @event preparedata
15628          * Fires on every row to render, to allow you to change the data.
15629          * @param {Roo.View} this
15630          * @param {Object} data to be rendered (change this)
15631          */
15632           "preparedata" : true
15633           
15634           
15635         });
15636
15637
15638
15639     this.el.on({
15640         "click": this.onClick,
15641         "dblclick": this.onDblClick,
15642         "contextmenu": this.onContextMenu,
15643         scope:this
15644     });
15645
15646     this.selections = [];
15647     this.nodes = [];
15648     this.cmp = new Roo.CompositeElementLite([]);
15649     if(this.store){
15650         this.store = Roo.factory(this.store, Roo.data);
15651         this.setStore(this.store, true);
15652     }
15653     
15654     if ( this.footer && this.footer.xtype) {
15655            
15656          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15657         
15658         this.footer.dataSource = this.store;
15659         this.footer.container = fctr;
15660         this.footer = Roo.factory(this.footer, Roo);
15661         fctr.insertFirst(this.el);
15662         
15663         // this is a bit insane - as the paging toolbar seems to detach the el..
15664 //        dom.parentNode.parentNode.parentNode
15665          // they get detached?
15666     }
15667     
15668     
15669     Roo.View.superclass.constructor.call(this);
15670     
15671     
15672 };
15673
15674 Roo.extend(Roo.View, Roo.util.Observable, {
15675     
15676      /**
15677      * @cfg {Roo.data.Store} store Data store to load data from.
15678      */
15679     store : false,
15680     
15681     /**
15682      * @cfg {String|Roo.Element} el The container element.
15683      */
15684     el : '',
15685     
15686     /**
15687      * @cfg {String|Roo.Template} tpl The template used by this View 
15688      */
15689     tpl : false,
15690     /**
15691      * @cfg {String} dataName the named area of the template to use as the data area
15692      *                          Works with domtemplates roo-name="name"
15693      */
15694     dataName: false,
15695     /**
15696      * @cfg {String} selectedClass The css class to add to selected nodes
15697      */
15698     selectedClass : "x-view-selected",
15699      /**
15700      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15701      */
15702     emptyText : "",
15703     
15704     /**
15705      * @cfg {String} text to display on mask (default Loading)
15706      */
15707     mask : false,
15708     /**
15709      * @cfg {Boolean} multiSelect Allow multiple selection
15710      */
15711     multiSelect : false,
15712     /**
15713      * @cfg {Boolean} singleSelect Allow single selection
15714      */
15715     singleSelect:  false,
15716     
15717     /**
15718      * @cfg {Boolean} toggleSelect - selecting 
15719      */
15720     toggleSelect : false,
15721     
15722     /**
15723      * @cfg {Boolean} tickable - selecting 
15724      */
15725     tickable : false,
15726     
15727     /**
15728      * Returns the element this view is bound to.
15729      * @return {Roo.Element}
15730      */
15731     getEl : function(){
15732         return this.wrapEl;
15733     },
15734     
15735     
15736
15737     /**
15738      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15739      */
15740     refresh : function(){
15741         //Roo.log('refresh');
15742         var t = this.tpl;
15743         
15744         // if we are using something like 'domtemplate', then
15745         // the what gets used is:
15746         // t.applySubtemplate(NAME, data, wrapping data..)
15747         // the outer template then get' applied with
15748         //     the store 'extra data'
15749         // and the body get's added to the
15750         //      roo-name="data" node?
15751         //      <span class='roo-tpl-{name}'></span> ?????
15752         
15753         
15754         
15755         this.clearSelections();
15756         this.el.update("");
15757         var html = [];
15758         var records = this.store.getRange();
15759         if(records.length < 1) {
15760             
15761             // is this valid??  = should it render a template??
15762             
15763             this.el.update(this.emptyText);
15764             return;
15765         }
15766         var el = this.el;
15767         if (this.dataName) {
15768             this.el.update(t.apply(this.store.meta)); //????
15769             el = this.el.child('.roo-tpl-' + this.dataName);
15770         }
15771         
15772         for(var i = 0, len = records.length; i < len; i++){
15773             var data = this.prepareData(records[i].data, i, records[i]);
15774             this.fireEvent("preparedata", this, data, i, records[i]);
15775             
15776             var d = Roo.apply({}, data);
15777             
15778             if(this.tickable){
15779                 Roo.apply(d, {'roo-id' : Roo.id()});
15780                 
15781                 var _this = this;
15782             
15783                 Roo.each(this.parent.item, function(item){
15784                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15785                         return;
15786                     }
15787                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15788                 });
15789             }
15790             
15791             html[html.length] = Roo.util.Format.trim(
15792                 this.dataName ?
15793                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15794                     t.apply(d)
15795             );
15796         }
15797         
15798         
15799         
15800         el.update(html.join(""));
15801         this.nodes = el.dom.childNodes;
15802         this.updateIndexes(0);
15803     },
15804     
15805
15806     /**
15807      * Function to override to reformat the data that is sent to
15808      * the template for each node.
15809      * DEPRICATED - use the preparedata event handler.
15810      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15811      * a JSON object for an UpdateManager bound view).
15812      */
15813     prepareData : function(data, index, record)
15814     {
15815         this.fireEvent("preparedata", this, data, index, record);
15816         return data;
15817     },
15818
15819     onUpdate : function(ds, record){
15820         // Roo.log('on update');   
15821         this.clearSelections();
15822         var index = this.store.indexOf(record);
15823         var n = this.nodes[index];
15824         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15825         n.parentNode.removeChild(n);
15826         this.updateIndexes(index, index);
15827     },
15828
15829     
15830     
15831 // --------- FIXME     
15832     onAdd : function(ds, records, index)
15833     {
15834         //Roo.log(['on Add', ds, records, index] );        
15835         this.clearSelections();
15836         if(this.nodes.length == 0){
15837             this.refresh();
15838             return;
15839         }
15840         var n = this.nodes[index];
15841         for(var i = 0, len = records.length; i < len; i++){
15842             var d = this.prepareData(records[i].data, i, records[i]);
15843             if(n){
15844                 this.tpl.insertBefore(n, d);
15845             }else{
15846                 
15847                 this.tpl.append(this.el, d);
15848             }
15849         }
15850         this.updateIndexes(index);
15851     },
15852
15853     onRemove : function(ds, record, index){
15854        // Roo.log('onRemove');
15855         this.clearSelections();
15856         var el = this.dataName  ?
15857             this.el.child('.roo-tpl-' + this.dataName) :
15858             this.el; 
15859         
15860         el.dom.removeChild(this.nodes[index]);
15861         this.updateIndexes(index);
15862     },
15863
15864     /**
15865      * Refresh an individual node.
15866      * @param {Number} index
15867      */
15868     refreshNode : function(index){
15869         this.onUpdate(this.store, this.store.getAt(index));
15870     },
15871
15872     updateIndexes : function(startIndex, endIndex){
15873         var ns = this.nodes;
15874         startIndex = startIndex || 0;
15875         endIndex = endIndex || ns.length - 1;
15876         for(var i = startIndex; i <= endIndex; i++){
15877             ns[i].nodeIndex = i;
15878         }
15879     },
15880
15881     /**
15882      * Changes the data store this view uses and refresh the view.
15883      * @param {Store} store
15884      */
15885     setStore : function(store, initial){
15886         if(!initial && this.store){
15887             this.store.un("datachanged", this.refresh);
15888             this.store.un("add", this.onAdd);
15889             this.store.un("remove", this.onRemove);
15890             this.store.un("update", this.onUpdate);
15891             this.store.un("clear", this.refresh);
15892             this.store.un("beforeload", this.onBeforeLoad);
15893             this.store.un("load", this.onLoad);
15894             this.store.un("loadexception", this.onLoad);
15895         }
15896         if(store){
15897           
15898             store.on("datachanged", this.refresh, this);
15899             store.on("add", this.onAdd, this);
15900             store.on("remove", this.onRemove, this);
15901             store.on("update", this.onUpdate, this);
15902             store.on("clear", this.refresh, this);
15903             store.on("beforeload", this.onBeforeLoad, this);
15904             store.on("load", this.onLoad, this);
15905             store.on("loadexception", this.onLoad, this);
15906         }
15907         
15908         if(store){
15909             this.refresh();
15910         }
15911     },
15912     /**
15913      * onbeforeLoad - masks the loading area.
15914      *
15915      */
15916     onBeforeLoad : function(store,opts)
15917     {
15918          //Roo.log('onBeforeLoad');   
15919         if (!opts.add) {
15920             this.el.update("");
15921         }
15922         this.el.mask(this.mask ? this.mask : "Loading" ); 
15923     },
15924     onLoad : function ()
15925     {
15926         this.el.unmask();
15927     },
15928     
15929
15930     /**
15931      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15932      * @param {HTMLElement} node
15933      * @return {HTMLElement} The template node
15934      */
15935     findItemFromChild : function(node){
15936         var el = this.dataName  ?
15937             this.el.child('.roo-tpl-' + this.dataName,true) :
15938             this.el.dom; 
15939         
15940         if(!node || node.parentNode == el){
15941                     return node;
15942             }
15943             var p = node.parentNode;
15944             while(p && p != el){
15945             if(p.parentNode == el){
15946                 return p;
15947             }
15948             p = p.parentNode;
15949         }
15950             return null;
15951     },
15952
15953     /** @ignore */
15954     onClick : function(e){
15955         var item = this.findItemFromChild(e.getTarget());
15956         if(item){
15957             var index = this.indexOf(item);
15958             if(this.onItemClick(item, index, e) !== false){
15959                 this.fireEvent("click", this, index, item, e);
15960             }
15961         }else{
15962             this.clearSelections();
15963         }
15964     },
15965
15966     /** @ignore */
15967     onContextMenu : function(e){
15968         var item = this.findItemFromChild(e.getTarget());
15969         if(item){
15970             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15971         }
15972     },
15973
15974     /** @ignore */
15975     onDblClick : function(e){
15976         var item = this.findItemFromChild(e.getTarget());
15977         if(item){
15978             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15979         }
15980     },
15981
15982     onItemClick : function(item, index, e)
15983     {
15984         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15985             return false;
15986         }
15987         if (this.toggleSelect) {
15988             var m = this.isSelected(item) ? 'unselect' : 'select';
15989             //Roo.log(m);
15990             var _t = this;
15991             _t[m](item, true, false);
15992             return true;
15993         }
15994         if(this.multiSelect || this.singleSelect){
15995             if(this.multiSelect && e.shiftKey && this.lastSelection){
15996                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15997             }else{
15998                 this.select(item, this.multiSelect && e.ctrlKey);
15999                 this.lastSelection = item;
16000             }
16001             
16002             if(!this.tickable){
16003                 e.preventDefault();
16004             }
16005             
16006         }
16007         return true;
16008     },
16009
16010     /**
16011      * Get the number of selected nodes.
16012      * @return {Number}
16013      */
16014     getSelectionCount : function(){
16015         return this.selections.length;
16016     },
16017
16018     /**
16019      * Get the currently selected nodes.
16020      * @return {Array} An array of HTMLElements
16021      */
16022     getSelectedNodes : function(){
16023         return this.selections;
16024     },
16025
16026     /**
16027      * Get the indexes of the selected nodes.
16028      * @return {Array}
16029      */
16030     getSelectedIndexes : function(){
16031         var indexes = [], s = this.selections;
16032         for(var i = 0, len = s.length; i < len; i++){
16033             indexes.push(s[i].nodeIndex);
16034         }
16035         return indexes;
16036     },
16037
16038     /**
16039      * Clear all selections
16040      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16041      */
16042     clearSelections : function(suppressEvent){
16043         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16044             this.cmp.elements = this.selections;
16045             this.cmp.removeClass(this.selectedClass);
16046             this.selections = [];
16047             if(!suppressEvent){
16048                 this.fireEvent("selectionchange", this, this.selections);
16049             }
16050         }
16051     },
16052
16053     /**
16054      * Returns true if the passed node is selected
16055      * @param {HTMLElement/Number} node The node or node index
16056      * @return {Boolean}
16057      */
16058     isSelected : function(node){
16059         var s = this.selections;
16060         if(s.length < 1){
16061             return false;
16062         }
16063         node = this.getNode(node);
16064         return s.indexOf(node) !== -1;
16065     },
16066
16067     /**
16068      * Selects nodes.
16069      * @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
16070      * @param {Boolean} keepExisting (optional) true to keep existing selections
16071      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16072      */
16073     select : function(nodeInfo, keepExisting, suppressEvent){
16074         if(nodeInfo instanceof Array){
16075             if(!keepExisting){
16076                 this.clearSelections(true);
16077             }
16078             for(var i = 0, len = nodeInfo.length; i < len; i++){
16079                 this.select(nodeInfo[i], true, true);
16080             }
16081             return;
16082         } 
16083         var node = this.getNode(nodeInfo);
16084         if(!node || this.isSelected(node)){
16085             return; // already selected.
16086         }
16087         if(!keepExisting){
16088             this.clearSelections(true);
16089         }
16090         
16091         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16092             Roo.fly(node).addClass(this.selectedClass);
16093             this.selections.push(node);
16094             if(!suppressEvent){
16095                 this.fireEvent("selectionchange", this, this.selections);
16096             }
16097         }
16098         
16099         
16100     },
16101       /**
16102      * Unselects nodes.
16103      * @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
16104      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16105      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16106      */
16107     unselect : function(nodeInfo, keepExisting, suppressEvent)
16108     {
16109         if(nodeInfo instanceof Array){
16110             Roo.each(this.selections, function(s) {
16111                 this.unselect(s, nodeInfo);
16112             }, this);
16113             return;
16114         }
16115         var node = this.getNode(nodeInfo);
16116         if(!node || !this.isSelected(node)){
16117             //Roo.log("not selected");
16118             return; // not selected.
16119         }
16120         // fireevent???
16121         var ns = [];
16122         Roo.each(this.selections, function(s) {
16123             if (s == node ) {
16124                 Roo.fly(node).removeClass(this.selectedClass);
16125
16126                 return;
16127             }
16128             ns.push(s);
16129         },this);
16130         
16131         this.selections= ns;
16132         this.fireEvent("selectionchange", this, this.selections);
16133     },
16134
16135     /**
16136      * Gets a template node.
16137      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16138      * @return {HTMLElement} The node or null if it wasn't found
16139      */
16140     getNode : function(nodeInfo){
16141         if(typeof nodeInfo == "string"){
16142             return document.getElementById(nodeInfo);
16143         }else if(typeof nodeInfo == "number"){
16144             return this.nodes[nodeInfo];
16145         }
16146         return nodeInfo;
16147     },
16148
16149     /**
16150      * Gets a range template nodes.
16151      * @param {Number} startIndex
16152      * @param {Number} endIndex
16153      * @return {Array} An array of nodes
16154      */
16155     getNodes : function(start, end){
16156         var ns = this.nodes;
16157         start = start || 0;
16158         end = typeof end == "undefined" ? ns.length - 1 : end;
16159         var nodes = [];
16160         if(start <= end){
16161             for(var i = start; i <= end; i++){
16162                 nodes.push(ns[i]);
16163             }
16164         } else{
16165             for(var i = start; i >= end; i--){
16166                 nodes.push(ns[i]);
16167             }
16168         }
16169         return nodes;
16170     },
16171
16172     /**
16173      * Finds the index of the passed node
16174      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16175      * @return {Number} The index of the node or -1
16176      */
16177     indexOf : function(node){
16178         node = this.getNode(node);
16179         if(typeof node.nodeIndex == "number"){
16180             return node.nodeIndex;
16181         }
16182         var ns = this.nodes;
16183         for(var i = 0, len = ns.length; i < len; i++){
16184             if(ns[i] == node){
16185                 return i;
16186             }
16187         }
16188         return -1;
16189     }
16190 });
16191 /*
16192  * - LGPL
16193  *
16194  * based on jquery fullcalendar
16195  * 
16196  */
16197
16198 Roo.bootstrap = Roo.bootstrap || {};
16199 /**
16200  * @class Roo.bootstrap.Calendar
16201  * @extends Roo.bootstrap.Component
16202  * Bootstrap Calendar class
16203  * @cfg {Boolean} loadMask (true|false) default false
16204  * @cfg {Object} header generate the user specific header of the calendar, default false
16205
16206  * @constructor
16207  * Create a new Container
16208  * @param {Object} config The config object
16209  */
16210
16211
16212
16213 Roo.bootstrap.Calendar = function(config){
16214     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16215      this.addEvents({
16216         /**
16217              * @event select
16218              * Fires when a date is selected
16219              * @param {DatePicker} this
16220              * @param {Date} date The selected date
16221              */
16222         'select': true,
16223         /**
16224              * @event monthchange
16225              * Fires when the displayed month changes 
16226              * @param {DatePicker} this
16227              * @param {Date} date The selected month
16228              */
16229         'monthchange': true,
16230         /**
16231              * @event evententer
16232              * Fires when mouse over an event
16233              * @param {Calendar} this
16234              * @param {event} Event
16235              */
16236         'evententer': true,
16237         /**
16238              * @event eventleave
16239              * Fires when the mouse leaves an
16240              * @param {Calendar} this
16241              * @param {event}
16242              */
16243         'eventleave': true,
16244         /**
16245              * @event eventclick
16246              * Fires when the mouse click an
16247              * @param {Calendar} this
16248              * @param {event}
16249              */
16250         'eventclick': true
16251         
16252     });
16253
16254 };
16255
16256 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16257     
16258      /**
16259      * @cfg {Number} startDay
16260      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16261      */
16262     startDay : 0,
16263     
16264     loadMask : false,
16265     
16266     header : false,
16267       
16268     getAutoCreate : function(){
16269         
16270         
16271         var fc_button = function(name, corner, style, content ) {
16272             return Roo.apply({},{
16273                 tag : 'span',
16274                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16275                          (corner.length ?
16276                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16277                             ''
16278                         ),
16279                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16280                 unselectable: 'on'
16281             });
16282         };
16283         
16284         var header = {};
16285         
16286         if(!this.header){
16287             header = {
16288                 tag : 'table',
16289                 cls : 'fc-header',
16290                 style : 'width:100%',
16291                 cn : [
16292                     {
16293                         tag: 'tr',
16294                         cn : [
16295                             {
16296                                 tag : 'td',
16297                                 cls : 'fc-header-left',
16298                                 cn : [
16299                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16300                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16301                                     { tag: 'span', cls: 'fc-header-space' },
16302                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16303
16304
16305                                 ]
16306                             },
16307
16308                             {
16309                                 tag : 'td',
16310                                 cls : 'fc-header-center',
16311                                 cn : [
16312                                     {
16313                                         tag: 'span',
16314                                         cls: 'fc-header-title',
16315                                         cn : {
16316                                             tag: 'H2',
16317                                             html : 'month / year'
16318                                         }
16319                                     }
16320
16321                                 ]
16322                             },
16323                             {
16324                                 tag : 'td',
16325                                 cls : 'fc-header-right',
16326                                 cn : [
16327                               /*      fc_button('month', 'left', '', 'month' ),
16328                                     fc_button('week', '', '', 'week' ),
16329                                     fc_button('day', 'right', '', 'day' )
16330                                 */    
16331
16332                                 ]
16333                             }
16334
16335                         ]
16336                     }
16337                 ]
16338             };
16339         }
16340         
16341         header = this.header;
16342         
16343        
16344         var cal_heads = function() {
16345             var ret = [];
16346             // fixme - handle this.
16347             
16348             for (var i =0; i < Date.dayNames.length; i++) {
16349                 var d = Date.dayNames[i];
16350                 ret.push({
16351                     tag: 'th',
16352                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16353                     html : d.substring(0,3)
16354                 });
16355                 
16356             }
16357             ret[0].cls += ' fc-first';
16358             ret[6].cls += ' fc-last';
16359             return ret;
16360         };
16361         var cal_cell = function(n) {
16362             return  {
16363                 tag: 'td',
16364                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16365                 cn : [
16366                     {
16367                         cn : [
16368                             {
16369                                 cls: 'fc-day-number',
16370                                 html: 'D'
16371                             },
16372                             {
16373                                 cls: 'fc-day-content',
16374                              
16375                                 cn : [
16376                                      {
16377                                         style: 'position: relative;' // height: 17px;
16378                                     }
16379                                 ]
16380                             }
16381                             
16382                             
16383                         ]
16384                     }
16385                 ]
16386                 
16387             }
16388         };
16389         var cal_rows = function() {
16390             
16391             var ret = [];
16392             for (var r = 0; r < 6; r++) {
16393                 var row= {
16394                     tag : 'tr',
16395                     cls : 'fc-week',
16396                     cn : []
16397                 };
16398                 
16399                 for (var i =0; i < Date.dayNames.length; i++) {
16400                     var d = Date.dayNames[i];
16401                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16402
16403                 }
16404                 row.cn[0].cls+=' fc-first';
16405                 row.cn[0].cn[0].style = 'min-height:90px';
16406                 row.cn[6].cls+=' fc-last';
16407                 ret.push(row);
16408                 
16409             }
16410             ret[0].cls += ' fc-first';
16411             ret[4].cls += ' fc-prev-last';
16412             ret[5].cls += ' fc-last';
16413             return ret;
16414             
16415         };
16416         
16417         var cal_table = {
16418             tag: 'table',
16419             cls: 'fc-border-separate',
16420             style : 'width:100%',
16421             cellspacing  : 0,
16422             cn : [
16423                 { 
16424                     tag: 'thead',
16425                     cn : [
16426                         { 
16427                             tag: 'tr',
16428                             cls : 'fc-first fc-last',
16429                             cn : cal_heads()
16430                         }
16431                     ]
16432                 },
16433                 { 
16434                     tag: 'tbody',
16435                     cn : cal_rows()
16436                 }
16437                   
16438             ]
16439         };
16440          
16441          var cfg = {
16442             cls : 'fc fc-ltr',
16443             cn : [
16444                 header,
16445                 {
16446                     cls : 'fc-content',
16447                     style : "position: relative;",
16448                     cn : [
16449                         {
16450                             cls : 'fc-view fc-view-month fc-grid',
16451                             style : 'position: relative',
16452                             unselectable : 'on',
16453                             cn : [
16454                                 {
16455                                     cls : 'fc-event-container',
16456                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16457                                 },
16458                                 cal_table
16459                             ]
16460                         }
16461                     ]
16462     
16463                 }
16464            ] 
16465             
16466         };
16467         
16468          
16469         
16470         return cfg;
16471     },
16472     
16473     
16474     initEvents : function()
16475     {
16476         if(!this.store){
16477             throw "can not find store for calendar";
16478         }
16479         
16480         var mark = {
16481             tag: "div",
16482             cls:"x-dlg-mask",
16483             style: "text-align:center",
16484             cn: [
16485                 {
16486                     tag: "div",
16487                     style: "background-color:white;width:50%;margin:250 auto",
16488                     cn: [
16489                         {
16490                             tag: "img",
16491                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16492                         },
16493                         {
16494                             tag: "span",
16495                             html: "Loading"
16496                         }
16497                         
16498                     ]
16499                 }
16500             ]
16501         };
16502         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16503         
16504         var size = this.el.select('.fc-content', true).first().getSize();
16505         this.maskEl.setSize(size.width, size.height);
16506         this.maskEl.enableDisplayMode("block");
16507         if(!this.loadMask){
16508             this.maskEl.hide();
16509         }
16510         
16511         this.store = Roo.factory(this.store, Roo.data);
16512         this.store.on('load', this.onLoad, this);
16513         this.store.on('beforeload', this.onBeforeLoad, this);
16514         
16515         this.resize();
16516         
16517         this.cells = this.el.select('.fc-day',true);
16518         //Roo.log(this.cells);
16519         this.textNodes = this.el.query('.fc-day-number');
16520         this.cells.addClassOnOver('fc-state-hover');
16521         
16522         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16523         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16524         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16525         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16526         
16527         this.on('monthchange', this.onMonthChange, this);
16528         
16529         this.update(new Date().clearTime());
16530     },
16531     
16532     resize : function() {
16533         var sz  = this.el.getSize();
16534         
16535         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16536         this.el.select('.fc-day-content div',true).setHeight(34);
16537     },
16538     
16539     
16540     // private
16541     showPrevMonth : function(e){
16542         this.update(this.activeDate.add("mo", -1));
16543     },
16544     showToday : function(e){
16545         this.update(new Date().clearTime());
16546     },
16547     // private
16548     showNextMonth : function(e){
16549         this.update(this.activeDate.add("mo", 1));
16550     },
16551
16552     // private
16553     showPrevYear : function(){
16554         this.update(this.activeDate.add("y", -1));
16555     },
16556
16557     // private
16558     showNextYear : function(){
16559         this.update(this.activeDate.add("y", 1));
16560     },
16561
16562     
16563    // private
16564     update : function(date)
16565     {
16566         var vd = this.activeDate;
16567         this.activeDate = date;
16568 //        if(vd && this.el){
16569 //            var t = date.getTime();
16570 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16571 //                Roo.log('using add remove');
16572 //                
16573 //                this.fireEvent('monthchange', this, date);
16574 //                
16575 //                this.cells.removeClass("fc-state-highlight");
16576 //                this.cells.each(function(c){
16577 //                   if(c.dateValue == t){
16578 //                       c.addClass("fc-state-highlight");
16579 //                       setTimeout(function(){
16580 //                            try{c.dom.firstChild.focus();}catch(e){}
16581 //                       }, 50);
16582 //                       return false;
16583 //                   }
16584 //                   return true;
16585 //                });
16586 //                return;
16587 //            }
16588 //        }
16589         
16590         var days = date.getDaysInMonth();
16591         
16592         var firstOfMonth = date.getFirstDateOfMonth();
16593         var startingPos = firstOfMonth.getDay()-this.startDay;
16594         
16595         if(startingPos < this.startDay){
16596             startingPos += 7;
16597         }
16598         
16599         var pm = date.add(Date.MONTH, -1);
16600         var prevStart = pm.getDaysInMonth()-startingPos;
16601 //        
16602         this.cells = this.el.select('.fc-day',true);
16603         this.textNodes = this.el.query('.fc-day-number');
16604         this.cells.addClassOnOver('fc-state-hover');
16605         
16606         var cells = this.cells.elements;
16607         var textEls = this.textNodes;
16608         
16609         Roo.each(cells, function(cell){
16610             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16611         });
16612         
16613         days += startingPos;
16614
16615         // convert everything to numbers so it's fast
16616         var day = 86400000;
16617         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16618         //Roo.log(d);
16619         //Roo.log(pm);
16620         //Roo.log(prevStart);
16621         
16622         var today = new Date().clearTime().getTime();
16623         var sel = date.clearTime().getTime();
16624         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16625         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16626         var ddMatch = this.disabledDatesRE;
16627         var ddText = this.disabledDatesText;
16628         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16629         var ddaysText = this.disabledDaysText;
16630         var format = this.format;
16631         
16632         var setCellClass = function(cal, cell){
16633             cell.row = 0;
16634             cell.events = [];
16635             cell.more = [];
16636             //Roo.log('set Cell Class');
16637             cell.title = "";
16638             var t = d.getTime();
16639             
16640             //Roo.log(d);
16641             
16642             cell.dateValue = t;
16643             if(t == today){
16644                 cell.className += " fc-today";
16645                 cell.className += " fc-state-highlight";
16646                 cell.title = cal.todayText;
16647             }
16648             if(t == sel){
16649                 // disable highlight in other month..
16650                 //cell.className += " fc-state-highlight";
16651                 
16652             }
16653             // disabling
16654             if(t < min) {
16655                 cell.className = " fc-state-disabled";
16656                 cell.title = cal.minText;
16657                 return;
16658             }
16659             if(t > max) {
16660                 cell.className = " fc-state-disabled";
16661                 cell.title = cal.maxText;
16662                 return;
16663             }
16664             if(ddays){
16665                 if(ddays.indexOf(d.getDay()) != -1){
16666                     cell.title = ddaysText;
16667                     cell.className = " fc-state-disabled";
16668                 }
16669             }
16670             if(ddMatch && format){
16671                 var fvalue = d.dateFormat(format);
16672                 if(ddMatch.test(fvalue)){
16673                     cell.title = ddText.replace("%0", fvalue);
16674                     cell.className = " fc-state-disabled";
16675                 }
16676             }
16677             
16678             if (!cell.initialClassName) {
16679                 cell.initialClassName = cell.dom.className;
16680             }
16681             
16682             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16683         };
16684
16685         var i = 0;
16686         
16687         for(; i < startingPos; i++) {
16688             textEls[i].innerHTML = (++prevStart);
16689             d.setDate(d.getDate()+1);
16690             
16691             cells[i].className = "fc-past fc-other-month";
16692             setCellClass(this, cells[i]);
16693         }
16694         
16695         var intDay = 0;
16696         
16697         for(; i < days; i++){
16698             intDay = i - startingPos + 1;
16699             textEls[i].innerHTML = (intDay);
16700             d.setDate(d.getDate()+1);
16701             
16702             cells[i].className = ''; // "x-date-active";
16703             setCellClass(this, cells[i]);
16704         }
16705         var extraDays = 0;
16706         
16707         for(; i < 42; i++) {
16708             textEls[i].innerHTML = (++extraDays);
16709             d.setDate(d.getDate()+1);
16710             
16711             cells[i].className = "fc-future fc-other-month";
16712             setCellClass(this, cells[i]);
16713         }
16714         
16715         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16716         
16717         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16718         
16719         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16720         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16721         
16722         if(totalRows != 6){
16723             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16724             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16725         }
16726         
16727         this.fireEvent('monthchange', this, date);
16728         
16729         
16730         /*
16731         if(!this.internalRender){
16732             var main = this.el.dom.firstChild;
16733             var w = main.offsetWidth;
16734             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16735             Roo.fly(main).setWidth(w);
16736             this.internalRender = true;
16737             // opera does not respect the auto grow header center column
16738             // then, after it gets a width opera refuses to recalculate
16739             // without a second pass
16740             if(Roo.isOpera && !this.secondPass){
16741                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16742                 this.secondPass = true;
16743                 this.update.defer(10, this, [date]);
16744             }
16745         }
16746         */
16747         
16748     },
16749     
16750     findCell : function(dt) {
16751         dt = dt.clearTime().getTime();
16752         var ret = false;
16753         this.cells.each(function(c){
16754             //Roo.log("check " +c.dateValue + '?=' + dt);
16755             if(c.dateValue == dt){
16756                 ret = c;
16757                 return false;
16758             }
16759             return true;
16760         });
16761         
16762         return ret;
16763     },
16764     
16765     findCells : function(ev) {
16766         var s = ev.start.clone().clearTime().getTime();
16767        // Roo.log(s);
16768         var e= ev.end.clone().clearTime().getTime();
16769        // Roo.log(e);
16770         var ret = [];
16771         this.cells.each(function(c){
16772              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16773             
16774             if(c.dateValue > e){
16775                 return ;
16776             }
16777             if(c.dateValue < s){
16778                 return ;
16779             }
16780             ret.push(c);
16781         });
16782         
16783         return ret;    
16784     },
16785     
16786 //    findBestRow: function(cells)
16787 //    {
16788 //        var ret = 0;
16789 //        
16790 //        for (var i =0 ; i < cells.length;i++) {
16791 //            ret  = Math.max(cells[i].rows || 0,ret);
16792 //        }
16793 //        return ret;
16794 //        
16795 //    },
16796     
16797     
16798     addItem : function(ev)
16799     {
16800         // look for vertical location slot in
16801         var cells = this.findCells(ev);
16802         
16803 //        ev.row = this.findBestRow(cells);
16804         
16805         // work out the location.
16806         
16807         var crow = false;
16808         var rows = [];
16809         for(var i =0; i < cells.length; i++) {
16810             
16811             cells[i].row = cells[0].row;
16812             
16813             if(i == 0){
16814                 cells[i].row = cells[i].row + 1;
16815             }
16816             
16817             if (!crow) {
16818                 crow = {
16819                     start : cells[i],
16820                     end :  cells[i]
16821                 };
16822                 continue;
16823             }
16824             if (crow.start.getY() == cells[i].getY()) {
16825                 // on same row.
16826                 crow.end = cells[i];
16827                 continue;
16828             }
16829             // different row.
16830             rows.push(crow);
16831             crow = {
16832                 start: cells[i],
16833                 end : cells[i]
16834             };
16835             
16836         }
16837         
16838         rows.push(crow);
16839         ev.els = [];
16840         ev.rows = rows;
16841         ev.cells = cells;
16842         
16843         cells[0].events.push(ev);
16844         
16845         this.calevents.push(ev);
16846     },
16847     
16848     clearEvents: function() {
16849         
16850         if(!this.calevents){
16851             return;
16852         }
16853         
16854         Roo.each(this.cells.elements, function(c){
16855             c.row = 0;
16856             c.events = [];
16857             c.more = [];
16858         });
16859         
16860         Roo.each(this.calevents, function(e) {
16861             Roo.each(e.els, function(el) {
16862                 el.un('mouseenter' ,this.onEventEnter, this);
16863                 el.un('mouseleave' ,this.onEventLeave, this);
16864                 el.remove();
16865             },this);
16866         },this);
16867         
16868         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16869             e.remove();
16870         });
16871         
16872     },
16873     
16874     renderEvents: function()
16875     {   
16876         var _this = this;
16877         
16878         this.cells.each(function(c) {
16879             
16880             if(c.row < 5){
16881                 return;
16882             }
16883             
16884             var ev = c.events;
16885             
16886             var r = 4;
16887             if(c.row != c.events.length){
16888                 r = 4 - (4 - (c.row - c.events.length));
16889             }
16890             
16891             c.events = ev.slice(0, r);
16892             c.more = ev.slice(r);
16893             
16894             if(c.more.length && c.more.length == 1){
16895                 c.events.push(c.more.pop());
16896             }
16897             
16898             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16899             
16900         });
16901             
16902         this.cells.each(function(c) {
16903             
16904             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16905             
16906             
16907             for (var e = 0; e < c.events.length; e++){
16908                 var ev = c.events[e];
16909                 var rows = ev.rows;
16910                 
16911                 for(var i = 0; i < rows.length; i++) {
16912                 
16913                     // how many rows should it span..
16914
16915                     var  cfg = {
16916                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16917                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16918
16919                         unselectable : "on",
16920                         cn : [
16921                             {
16922                                 cls: 'fc-event-inner',
16923                                 cn : [
16924     //                                {
16925     //                                  tag:'span',
16926     //                                  cls: 'fc-event-time',
16927     //                                  html : cells.length > 1 ? '' : ev.time
16928     //                                },
16929                                     {
16930                                       tag:'span',
16931                                       cls: 'fc-event-title',
16932                                       html : String.format('{0}', ev.title)
16933                                     }
16934
16935
16936                                 ]
16937                             },
16938                             {
16939                                 cls: 'ui-resizable-handle ui-resizable-e',
16940                                 html : '&nbsp;&nbsp;&nbsp'
16941                             }
16942
16943                         ]
16944                     };
16945
16946                     if (i == 0) {
16947                         cfg.cls += ' fc-event-start';
16948                     }
16949                     if ((i+1) == rows.length) {
16950                         cfg.cls += ' fc-event-end';
16951                     }
16952
16953                     var ctr = _this.el.select('.fc-event-container',true).first();
16954                     var cg = ctr.createChild(cfg);
16955
16956                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16957                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16958
16959                     var r = (c.more.length) ? 1 : 0;
16960                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16961                     cg.setWidth(ebox.right - sbox.x -2);
16962
16963                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16964                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16965                     cg.on('click', _this.onEventClick, _this, ev);
16966
16967                     ev.els.push(cg);
16968                     
16969                 }
16970                 
16971             }
16972             
16973             
16974             if(c.more.length){
16975                 var  cfg = {
16976                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16977                     style : 'position: absolute',
16978                     unselectable : "on",
16979                     cn : [
16980                         {
16981                             cls: 'fc-event-inner',
16982                             cn : [
16983                                 {
16984                                   tag:'span',
16985                                   cls: 'fc-event-title',
16986                                   html : 'More'
16987                                 }
16988
16989
16990                             ]
16991                         },
16992                         {
16993                             cls: 'ui-resizable-handle ui-resizable-e',
16994                             html : '&nbsp;&nbsp;&nbsp'
16995                         }
16996
16997                     ]
16998                 };
16999
17000                 var ctr = _this.el.select('.fc-event-container',true).first();
17001                 var cg = ctr.createChild(cfg);
17002
17003                 var sbox = c.select('.fc-day-content',true).first().getBox();
17004                 var ebox = c.select('.fc-day-content',true).first().getBox();
17005                 //Roo.log(cg);
17006                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17007                 cg.setWidth(ebox.right - sbox.x -2);
17008
17009                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17010                 
17011             }
17012             
17013         });
17014         
17015         
17016         
17017     },
17018     
17019     onEventEnter: function (e, el,event,d) {
17020         this.fireEvent('evententer', this, el, event);
17021     },
17022     
17023     onEventLeave: function (e, el,event,d) {
17024         this.fireEvent('eventleave', this, el, event);
17025     },
17026     
17027     onEventClick: function (e, el,event,d) {
17028         this.fireEvent('eventclick', this, el, event);
17029     },
17030     
17031     onMonthChange: function () {
17032         this.store.load();
17033     },
17034     
17035     onMoreEventClick: function(e, el, more)
17036     {
17037         var _this = this;
17038         
17039         this.calpopover.placement = 'right';
17040         this.calpopover.setTitle('More');
17041         
17042         this.calpopover.setContent('');
17043         
17044         var ctr = this.calpopover.el.select('.popover-content', true).first();
17045         
17046         Roo.each(more, function(m){
17047             var cfg = {
17048                 cls : 'fc-event-hori fc-event-draggable',
17049                 html : m.title
17050             };
17051             var cg = ctr.createChild(cfg);
17052             
17053             cg.on('click', _this.onEventClick, _this, m);
17054         });
17055         
17056         this.calpopover.show(el);
17057         
17058         
17059     },
17060     
17061     onLoad: function () 
17062     {   
17063         this.calevents = [];
17064         var cal = this;
17065         
17066         if(this.store.getCount() > 0){
17067             this.store.data.each(function(d){
17068                cal.addItem({
17069                     id : d.data.id,
17070                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17071                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17072                     time : d.data.start_time,
17073                     title : d.data.title,
17074                     description : d.data.description,
17075                     venue : d.data.venue
17076                 });
17077             });
17078         }
17079         
17080         this.renderEvents();
17081         
17082         if(this.calevents.length && this.loadMask){
17083             this.maskEl.hide();
17084         }
17085     },
17086     
17087     onBeforeLoad: function()
17088     {
17089         this.clearEvents();
17090         if(this.loadMask){
17091             this.maskEl.show();
17092         }
17093     }
17094 });
17095
17096  
17097  /*
17098  * - LGPL
17099  *
17100  * element
17101  * 
17102  */
17103
17104 /**
17105  * @class Roo.bootstrap.Popover
17106  * @extends Roo.bootstrap.Component
17107  * Bootstrap Popover class
17108  * @cfg {String} html contents of the popover   (or false to use children..)
17109  * @cfg {String} title of popover (or false to hide)
17110  * @cfg {String} placement how it is placed
17111  * @cfg {String} trigger click || hover (or false to trigger manually)
17112  * @cfg {String} over what (parent or false to trigger manually.)
17113  * @cfg {Number} delay - delay before showing
17114  
17115  * @constructor
17116  * Create a new Popover
17117  * @param {Object} config The config object
17118  */
17119
17120 Roo.bootstrap.Popover = function(config){
17121     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17122     
17123     this.addEvents({
17124         // raw events
17125          /**
17126          * @event show
17127          * After the popover show
17128          * 
17129          * @param {Roo.bootstrap.Popover} this
17130          */
17131         "show" : true,
17132         /**
17133          * @event hide
17134          * After the popover hide
17135          * 
17136          * @param {Roo.bootstrap.Popover} this
17137          */
17138         "hide" : true
17139     });
17140 };
17141
17142 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17143     
17144     title: 'Fill in a title',
17145     html: false,
17146     
17147     placement : 'right',
17148     trigger : 'hover', // hover
17149     
17150     delay : 0,
17151     
17152     over: 'parent',
17153     
17154     can_build_overlaid : false,
17155     
17156     getChildContainer : function()
17157     {
17158         return this.el.select('.popover-content',true).first();
17159     },
17160     
17161     getAutoCreate : function(){
17162          
17163         var cfg = {
17164            cls : 'popover roo-dynamic',
17165            style: 'display:block',
17166            cn : [
17167                 {
17168                     cls : 'arrow'
17169                 },
17170                 {
17171                     cls : 'popover-inner',
17172                     cn : [
17173                         {
17174                             tag: 'h3',
17175                             cls: 'popover-title',
17176                             html : this.title
17177                         },
17178                         {
17179                             cls : 'popover-content',
17180                             html : this.html
17181                         }
17182                     ]
17183                     
17184                 }
17185            ]
17186         };
17187         
17188         return cfg;
17189     },
17190     setTitle: function(str)
17191     {
17192         this.title = str;
17193         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17194     },
17195     setContent: function(str)
17196     {
17197         this.html = str;
17198         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17199     },
17200     // as it get's added to the bottom of the page.
17201     onRender : function(ct, position)
17202     {
17203         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17204         if(!this.el){
17205             var cfg = Roo.apply({},  this.getAutoCreate());
17206             cfg.id = Roo.id();
17207             
17208             if (this.cls) {
17209                 cfg.cls += ' ' + this.cls;
17210             }
17211             if (this.style) {
17212                 cfg.style = this.style;
17213             }
17214             //Roo.log("adding to ");
17215             this.el = Roo.get(document.body).createChild(cfg, position);
17216 //            Roo.log(this.el);
17217         }
17218         this.initEvents();
17219     },
17220     
17221     initEvents : function()
17222     {
17223         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17224         this.el.enableDisplayMode('block');
17225         this.el.hide();
17226         if (this.over === false) {
17227             return; 
17228         }
17229         if (this.triggers === false) {
17230             return;
17231         }
17232         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17233         var triggers = this.trigger ? this.trigger.split(' ') : [];
17234         Roo.each(triggers, function(trigger) {
17235         
17236             if (trigger == 'click') {
17237                 on_el.on('click', this.toggle, this);
17238             } else if (trigger != 'manual') {
17239                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17240                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17241       
17242                 on_el.on(eventIn  ,this.enter, this);
17243                 on_el.on(eventOut, this.leave, this);
17244             }
17245         }, this);
17246         
17247     },
17248     
17249     
17250     // private
17251     timeout : null,
17252     hoverState : null,
17253     
17254     toggle : function () {
17255         this.hoverState == 'in' ? this.leave() : this.enter();
17256     },
17257     
17258     enter : function () {
17259         
17260         clearTimeout(this.timeout);
17261     
17262         this.hoverState = 'in';
17263     
17264         if (!this.delay || !this.delay.show) {
17265             this.show();
17266             return;
17267         }
17268         var _t = this;
17269         this.timeout = setTimeout(function () {
17270             if (_t.hoverState == 'in') {
17271                 _t.show();
17272             }
17273         }, this.delay.show)
17274     },
17275     
17276     leave : function() {
17277         clearTimeout(this.timeout);
17278     
17279         this.hoverState = 'out';
17280     
17281         if (!this.delay || !this.delay.hide) {
17282             this.hide();
17283             return;
17284         }
17285         var _t = this;
17286         this.timeout = setTimeout(function () {
17287             if (_t.hoverState == 'out') {
17288                 _t.hide();
17289             }
17290         }, this.delay.hide)
17291     },
17292     
17293     show : function (on_el)
17294     {
17295         if (!on_el) {
17296             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17297         }
17298         
17299         // set content.
17300         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17301         if (this.html !== false) {
17302             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17303         }
17304         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17305         if (!this.title.length) {
17306             this.el.select('.popover-title',true).hide();
17307         }
17308         
17309         var placement = typeof this.placement == 'function' ?
17310             this.placement.call(this, this.el, on_el) :
17311             this.placement;
17312             
17313         var autoToken = /\s?auto?\s?/i;
17314         var autoPlace = autoToken.test(placement);
17315         if (autoPlace) {
17316             placement = placement.replace(autoToken, '') || 'top';
17317         }
17318         
17319         //this.el.detach()
17320         //this.el.setXY([0,0]);
17321         this.el.show();
17322         this.el.dom.style.display='block';
17323         this.el.addClass(placement);
17324         
17325         //this.el.appendTo(on_el);
17326         
17327         var p = this.getPosition();
17328         var box = this.el.getBox();
17329         
17330         if (autoPlace) {
17331             // fixme..
17332         }
17333         var align = Roo.bootstrap.Popover.alignment[placement];
17334         
17335 //        Roo.log(align);
17336         this.el.alignTo(on_el, align[0],align[1]);
17337         //var arrow = this.el.select('.arrow',true).first();
17338         //arrow.set(align[2], 
17339         
17340         this.el.addClass('in');
17341         
17342         
17343         if (this.el.hasClass('fade')) {
17344             // fade it?
17345         }
17346         
17347         this.hoverState = 'in';
17348         
17349         this.fireEvent('show', this);
17350         
17351     },
17352     hide : function()
17353     {
17354         this.el.setXY([0,0]);
17355         this.el.removeClass('in');
17356         this.el.hide();
17357         this.hoverState = null;
17358         
17359         this.fireEvent('hide', this);
17360     }
17361     
17362 });
17363
17364 Roo.bootstrap.Popover.alignment = {
17365     'left' : ['r-l', [-10,0], 'right'],
17366     'right' : ['l-r', [10,0], 'left'],
17367     'bottom' : ['t-b', [0,10], 'top'],
17368     'top' : [ 'b-t', [0,-10], 'bottom']
17369 };
17370
17371  /*
17372  * - LGPL
17373  *
17374  * Progress
17375  * 
17376  */
17377
17378 /**
17379  * @class Roo.bootstrap.Progress
17380  * @extends Roo.bootstrap.Component
17381  * Bootstrap Progress class
17382  * @cfg {Boolean} striped striped of the progress bar
17383  * @cfg {Boolean} active animated of the progress bar
17384  * 
17385  * 
17386  * @constructor
17387  * Create a new Progress
17388  * @param {Object} config The config object
17389  */
17390
17391 Roo.bootstrap.Progress = function(config){
17392     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17393 };
17394
17395 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17396     
17397     striped : false,
17398     active: false,
17399     
17400     getAutoCreate : function(){
17401         var cfg = {
17402             tag: 'div',
17403             cls: 'progress'
17404         };
17405         
17406         
17407         if(this.striped){
17408             cfg.cls += ' progress-striped';
17409         }
17410       
17411         if(this.active){
17412             cfg.cls += ' active';
17413         }
17414         
17415         
17416         return cfg;
17417     }
17418    
17419 });
17420
17421  
17422
17423  /*
17424  * - LGPL
17425  *
17426  * ProgressBar
17427  * 
17428  */
17429
17430 /**
17431  * @class Roo.bootstrap.ProgressBar
17432  * @extends Roo.bootstrap.Component
17433  * Bootstrap ProgressBar class
17434  * @cfg {Number} aria_valuenow aria-value now
17435  * @cfg {Number} aria_valuemin aria-value min
17436  * @cfg {Number} aria_valuemax aria-value max
17437  * @cfg {String} label label for the progress bar
17438  * @cfg {String} panel (success | info | warning | danger )
17439  * @cfg {String} role role of the progress bar
17440  * @cfg {String} sr_only text
17441  * 
17442  * 
17443  * @constructor
17444  * Create a new ProgressBar
17445  * @param {Object} config The config object
17446  */
17447
17448 Roo.bootstrap.ProgressBar = function(config){
17449     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17450 };
17451
17452 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17453     
17454     aria_valuenow : 0,
17455     aria_valuemin : 0,
17456     aria_valuemax : 100,
17457     label : false,
17458     panel : false,
17459     role : false,
17460     sr_only: false,
17461     
17462     getAutoCreate : function()
17463     {
17464         
17465         var cfg = {
17466             tag: 'div',
17467             cls: 'progress-bar',
17468             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17469         };
17470         
17471         if(this.sr_only){
17472             cfg.cn = {
17473                 tag: 'span',
17474                 cls: 'sr-only',
17475                 html: this.sr_only
17476             }
17477         }
17478         
17479         if(this.role){
17480             cfg.role = this.role;
17481         }
17482         
17483         if(this.aria_valuenow){
17484             cfg['aria-valuenow'] = this.aria_valuenow;
17485         }
17486         
17487         if(this.aria_valuemin){
17488             cfg['aria-valuemin'] = this.aria_valuemin;
17489         }
17490         
17491         if(this.aria_valuemax){
17492             cfg['aria-valuemax'] = this.aria_valuemax;
17493         }
17494         
17495         if(this.label && !this.sr_only){
17496             cfg.html = this.label;
17497         }
17498         
17499         if(this.panel){
17500             cfg.cls += ' progress-bar-' + this.panel;
17501         }
17502         
17503         return cfg;
17504     },
17505     
17506     update : function(aria_valuenow)
17507     {
17508         this.aria_valuenow = aria_valuenow;
17509         
17510         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17511     }
17512    
17513 });
17514
17515  
17516
17517  /*
17518  * - LGPL
17519  *
17520  * column
17521  * 
17522  */
17523
17524 /**
17525  * @class Roo.bootstrap.TabGroup
17526  * @extends Roo.bootstrap.Column
17527  * Bootstrap Column class
17528  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17529  * @cfg {Boolean} carousel true to make the group behave like a carousel
17530  * @cfg {Boolean} bullets show bullets for the panels
17531  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17532  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17533  * @cfg {Boolean} showarrow (true|false) show arrow default true
17534  * 
17535  * @constructor
17536  * Create a new TabGroup
17537  * @param {Object} config The config object
17538  */
17539
17540 Roo.bootstrap.TabGroup = function(config){
17541     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17542     if (!this.navId) {
17543         this.navId = Roo.id();
17544     }
17545     this.tabs = [];
17546     Roo.bootstrap.TabGroup.register(this);
17547     
17548 };
17549
17550 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17551     
17552     carousel : false,
17553     transition : false,
17554     bullets : 0,
17555     timer : 0,
17556     autoslide : false,
17557     slideFn : false,
17558     slideOnTouch : false,
17559     showarrow : true,
17560     
17561     getAutoCreate : function()
17562     {
17563         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17564         
17565         cfg.cls += ' tab-content';
17566         
17567         if (this.carousel) {
17568             cfg.cls += ' carousel slide';
17569             
17570             cfg.cn = [{
17571                cls : 'carousel-inner',
17572                cn : []
17573             }];
17574         
17575             if(this.bullets  && !Roo.isTouch){
17576                 
17577                 var bullets = {
17578                     cls : 'carousel-bullets',
17579                     cn : []
17580                 };
17581                
17582                 if(this.bullets_cls){
17583                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17584                 }
17585                 
17586                 bullets.cn.push({
17587                     cls : 'clear'
17588                 });
17589                 
17590                 cfg.cn[0].cn.push(bullets);
17591             }
17592             
17593             if(this.showarrow){
17594                 cfg.cn[0].cn.push({
17595                     tag : 'div',
17596                     class : 'carousel-arrow',
17597                     cn : [
17598                         {
17599                             tag : 'div',
17600                             class : 'carousel-prev',
17601                             cn : [
17602                                 {
17603                                     tag : 'i',
17604                                     class : 'fa fa-chevron-left'
17605                                 }
17606                             ]
17607                         },
17608                         {
17609                             tag : 'div',
17610                             class : 'carousel-next',
17611                             cn : [
17612                                 {
17613                                     tag : 'i',
17614                                     class : 'fa fa-chevron-right'
17615                                 }
17616                             ]
17617                         }
17618                     ]
17619                 });
17620             }
17621             
17622         }
17623         
17624         return cfg;
17625     },
17626     
17627     initEvents:  function()
17628     {
17629 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17630 //            this.el.on("touchstart", this.onTouchStart, this);
17631 //        }
17632         
17633         if(this.autoslide){
17634             var _this = this;
17635             
17636             this.slideFn = window.setInterval(function() {
17637                 _this.showPanelNext();
17638             }, this.timer);
17639         }
17640         
17641         if(this.showarrow){
17642             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17643             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17644         }
17645         
17646         
17647     },
17648     
17649 //    onTouchStart : function(e, el, o)
17650 //    {
17651 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17652 //            return;
17653 //        }
17654 //        
17655 //        this.showPanelNext();
17656 //    },
17657     
17658     
17659     getChildContainer : function()
17660     {
17661         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17662     },
17663     
17664     /**
17665     * register a Navigation item
17666     * @param {Roo.bootstrap.NavItem} the navitem to add
17667     */
17668     register : function(item)
17669     {
17670         this.tabs.push( item);
17671         item.navId = this.navId; // not really needed..
17672         this.addBullet();
17673     
17674     },
17675     
17676     getActivePanel : function()
17677     {
17678         var r = false;
17679         Roo.each(this.tabs, function(t) {
17680             if (t.active) {
17681                 r = t;
17682                 return false;
17683             }
17684             return null;
17685         });
17686         return r;
17687         
17688     },
17689     getPanelByName : function(n)
17690     {
17691         var r = false;
17692         Roo.each(this.tabs, function(t) {
17693             if (t.tabId == n) {
17694                 r = t;
17695                 return false;
17696             }
17697             return null;
17698         });
17699         return r;
17700     },
17701     indexOfPanel : function(p)
17702     {
17703         var r = false;
17704         Roo.each(this.tabs, function(t,i) {
17705             if (t.tabId == p.tabId) {
17706                 r = i;
17707                 return false;
17708             }
17709             return null;
17710         });
17711         return r;
17712     },
17713     /**
17714      * show a specific panel
17715      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17716      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17717      */
17718     showPanel : function (pan)
17719     {
17720         if(this.transition || typeof(pan) == 'undefined'){
17721             Roo.log("waiting for the transitionend");
17722             return;
17723         }
17724         
17725         if (typeof(pan) == 'number') {
17726             pan = this.tabs[pan];
17727         }
17728         
17729         if (typeof(pan) == 'string') {
17730             pan = this.getPanelByName(pan);
17731         }
17732         
17733         var cur = this.getActivePanel();
17734         
17735         if(!pan || !cur){
17736             Roo.log('pan or acitve pan is undefined');
17737             return false;
17738         }
17739         
17740         if (pan.tabId == this.getActivePanel().tabId) {
17741             return true;
17742         }
17743         
17744         if (false === cur.fireEvent('beforedeactivate')) {
17745             return false;
17746         }
17747         
17748         if(this.bullets > 0 && !Roo.isTouch){
17749             this.setActiveBullet(this.indexOfPanel(pan));
17750         }
17751         
17752         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17753             
17754             this.transition = true;
17755             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17756             var lr = dir == 'next' ? 'left' : 'right';
17757             pan.el.addClass(dir); // or prev
17758             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17759             cur.el.addClass(lr); // or right
17760             pan.el.addClass(lr);
17761             
17762             var _this = this;
17763             cur.el.on('transitionend', function() {
17764                 Roo.log("trans end?");
17765                 
17766                 pan.el.removeClass([lr,dir]);
17767                 pan.setActive(true);
17768                 
17769                 cur.el.removeClass([lr]);
17770                 cur.setActive(false);
17771                 
17772                 _this.transition = false;
17773                 
17774             }, this, { single:  true } );
17775             
17776             return true;
17777         }
17778         
17779         cur.setActive(false);
17780         pan.setActive(true);
17781         
17782         return true;
17783         
17784     },
17785     showPanelNext : function()
17786     {
17787         var i = this.indexOfPanel(this.getActivePanel());
17788         
17789         if (i >= this.tabs.length - 1 && !this.autoslide) {
17790             return;
17791         }
17792         
17793         if (i >= this.tabs.length - 1 && this.autoslide) {
17794             i = -1;
17795         }
17796         
17797         this.showPanel(this.tabs[i+1]);
17798     },
17799     
17800     showPanelPrev : function()
17801     {
17802         var i = this.indexOfPanel(this.getActivePanel());
17803         
17804         if (i  < 1 && !this.autoslide) {
17805             return;
17806         }
17807         
17808         if (i < 1 && this.autoslide) {
17809             i = this.tabs.length;
17810         }
17811         
17812         this.showPanel(this.tabs[i-1]);
17813     },
17814     
17815     
17816     addBullet: function()
17817     {
17818         if(!this.bullets || Roo.isTouch){
17819             return;
17820         }
17821         var ctr = this.el.select('.carousel-bullets',true).first();
17822         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17823         var bullet = ctr.createChild({
17824             cls : 'bullet bullet-' + i
17825         },ctr.dom.lastChild);
17826         
17827         
17828         var _this = this;
17829         
17830         bullet.on('click', (function(e, el, o, ii, t){
17831
17832             e.preventDefault();
17833
17834             this.showPanel(ii);
17835
17836             if(this.autoslide && this.slideFn){
17837                 clearInterval(this.slideFn);
17838                 this.slideFn = window.setInterval(function() {
17839                     _this.showPanelNext();
17840                 }, this.timer);
17841             }
17842
17843         }).createDelegate(this, [i, bullet], true));
17844                 
17845         
17846     },
17847      
17848     setActiveBullet : function(i)
17849     {
17850         if(Roo.isTouch){
17851             return;
17852         }
17853         
17854         Roo.each(this.el.select('.bullet', true).elements, function(el){
17855             el.removeClass('selected');
17856         });
17857
17858         var bullet = this.el.select('.bullet-' + i, true).first();
17859         
17860         if(!bullet){
17861             return;
17862         }
17863         
17864         bullet.addClass('selected');
17865     }
17866     
17867     
17868   
17869 });
17870
17871  
17872
17873  
17874  
17875 Roo.apply(Roo.bootstrap.TabGroup, {
17876     
17877     groups: {},
17878      /**
17879     * register a Navigation Group
17880     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17881     */
17882     register : function(navgrp)
17883     {
17884         this.groups[navgrp.navId] = navgrp;
17885         
17886     },
17887     /**
17888     * fetch a Navigation Group based on the navigation ID
17889     * if one does not exist , it will get created.
17890     * @param {string} the navgroup to add
17891     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17892     */
17893     get: function(navId) {
17894         if (typeof(this.groups[navId]) == 'undefined') {
17895             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17896         }
17897         return this.groups[navId] ;
17898     }
17899     
17900     
17901     
17902 });
17903
17904  /*
17905  * - LGPL
17906  *
17907  * TabPanel
17908  * 
17909  */
17910
17911 /**
17912  * @class Roo.bootstrap.TabPanel
17913  * @extends Roo.bootstrap.Component
17914  * Bootstrap TabPanel class
17915  * @cfg {Boolean} active panel active
17916  * @cfg {String} html panel content
17917  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17918  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17919  * @cfg {String} href click to link..
17920  * 
17921  * 
17922  * @constructor
17923  * Create a new TabPanel
17924  * @param {Object} config The config object
17925  */
17926
17927 Roo.bootstrap.TabPanel = function(config){
17928     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17929     this.addEvents({
17930         /**
17931              * @event changed
17932              * Fires when the active status changes
17933              * @param {Roo.bootstrap.TabPanel} this
17934              * @param {Boolean} state the new state
17935             
17936          */
17937         'changed': true,
17938         /**
17939              * @event beforedeactivate
17940              * Fires before a tab is de-activated - can be used to do validation on a form.
17941              * @param {Roo.bootstrap.TabPanel} this
17942              * @return {Boolean} false if there is an error
17943             
17944          */
17945         'beforedeactivate': true
17946      });
17947     
17948     this.tabId = this.tabId || Roo.id();
17949   
17950 };
17951
17952 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17953     
17954     active: false,
17955     html: false,
17956     tabId: false,
17957     navId : false,
17958     href : '',
17959     
17960     getAutoCreate : function(){
17961         var cfg = {
17962             tag: 'div',
17963             // item is needed for carousel - not sure if it has any effect otherwise
17964             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17965             html: this.html || ''
17966         };
17967         
17968         if(this.active){
17969             cfg.cls += ' active';
17970         }
17971         
17972         if(this.tabId){
17973             cfg.tabId = this.tabId;
17974         }
17975         
17976         
17977         return cfg;
17978     },
17979     
17980     initEvents:  function()
17981     {
17982         var p = this.parent();
17983         
17984         this.navId = this.navId || p.navId;
17985         
17986         if (typeof(this.navId) != 'undefined') {
17987             // not really needed.. but just in case.. parent should be a NavGroup.
17988             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17989             
17990             tg.register(this);
17991             
17992             var i = tg.tabs.length - 1;
17993             
17994             if(this.active && tg.bullets > 0 && i < tg.bullets){
17995                 tg.setActiveBullet(i);
17996             }
17997         }
17998         
17999         this.el.on('click', this.onClick, this);
18000         
18001         if(Roo.isTouch){
18002             this.el.on("touchstart", this.onTouchStart, this);
18003             this.el.on("touchmove", this.onTouchMove, this);
18004             this.el.on("touchend", this.onTouchEnd, this);
18005         }
18006         
18007     },
18008     
18009     onRender : function(ct, position)
18010     {
18011         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18012     },
18013     
18014     setActive : function(state)
18015     {
18016         Roo.log("panel - set active " + this.tabId + "=" + state);
18017         
18018         this.active = state;
18019         if (!state) {
18020             this.el.removeClass('active');
18021             
18022         } else  if (!this.el.hasClass('active')) {
18023             this.el.addClass('active');
18024         }
18025         
18026         this.fireEvent('changed', this, state);
18027     },
18028     
18029     onClick : function(e)
18030     {
18031         e.preventDefault();
18032         
18033         if(!this.href.length){
18034             return;
18035         }
18036         
18037         window.location.href = this.href;
18038     },
18039     
18040     startX : 0,
18041     startY : 0,
18042     endX : 0,
18043     endY : 0,
18044     swiping : false,
18045     
18046     onTouchStart : function(e)
18047     {
18048         this.swiping = false;
18049         
18050         this.startX = e.browserEvent.touches[0].clientX;
18051         this.startY = e.browserEvent.touches[0].clientY;
18052     },
18053     
18054     onTouchMove : function(e)
18055     {
18056         this.swiping = true;
18057         
18058         this.endX = e.browserEvent.touches[0].clientX;
18059         this.endY = e.browserEvent.touches[0].clientY;
18060     },
18061     
18062     onTouchEnd : function(e)
18063     {
18064         if(!this.swiping){
18065             this.onClick(e);
18066             return;
18067         }
18068         
18069         var tabGroup = this.parent();
18070         
18071         if(this.endX > this.startX){ // swiping right
18072             tabGroup.showPanelPrev();
18073             return;
18074         }
18075         
18076         if(this.startX > this.endX){ // swiping left
18077             tabGroup.showPanelNext();
18078             return;
18079         }
18080     }
18081     
18082     
18083 });
18084  
18085
18086  
18087
18088  /*
18089  * - LGPL
18090  *
18091  * DateField
18092  * 
18093  */
18094
18095 /**
18096  * @class Roo.bootstrap.DateField
18097  * @extends Roo.bootstrap.Input
18098  * Bootstrap DateField class
18099  * @cfg {Number} weekStart default 0
18100  * @cfg {String} viewMode default empty, (months|years)
18101  * @cfg {String} minViewMode default empty, (months|years)
18102  * @cfg {Number} startDate default -Infinity
18103  * @cfg {Number} endDate default Infinity
18104  * @cfg {Boolean} todayHighlight default false
18105  * @cfg {Boolean} todayBtn default false
18106  * @cfg {Boolean} calendarWeeks default false
18107  * @cfg {Object} daysOfWeekDisabled default empty
18108  * @cfg {Boolean} singleMode default false (true | false)
18109  * 
18110  * @cfg {Boolean} keyboardNavigation default true
18111  * @cfg {String} language default en
18112  * 
18113  * @constructor
18114  * Create a new DateField
18115  * @param {Object} config The config object
18116  */
18117
18118 Roo.bootstrap.DateField = function(config){
18119     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18120      this.addEvents({
18121             /**
18122              * @event show
18123              * Fires when this field show.
18124              * @param {Roo.bootstrap.DateField} this
18125              * @param {Mixed} date The date value
18126              */
18127             show : true,
18128             /**
18129              * @event show
18130              * Fires when this field hide.
18131              * @param {Roo.bootstrap.DateField} this
18132              * @param {Mixed} date The date value
18133              */
18134             hide : true,
18135             /**
18136              * @event select
18137              * Fires when select a date.
18138              * @param {Roo.bootstrap.DateField} this
18139              * @param {Mixed} date The date value
18140              */
18141             select : true,
18142             /**
18143              * @event beforeselect
18144              * Fires when before select a date.
18145              * @param {Roo.bootstrap.DateField} this
18146              * @param {Mixed} date The date value
18147              */
18148             beforeselect : true
18149         });
18150 };
18151
18152 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18153     
18154     /**
18155      * @cfg {String} format
18156      * The default date format string which can be overriden for localization support.  The format must be
18157      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18158      */
18159     format : "m/d/y",
18160     /**
18161      * @cfg {String} altFormats
18162      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18163      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18164      */
18165     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18166     
18167     weekStart : 0,
18168     
18169     viewMode : '',
18170     
18171     minViewMode : '',
18172     
18173     todayHighlight : false,
18174     
18175     todayBtn: false,
18176     
18177     language: 'en',
18178     
18179     keyboardNavigation: true,
18180     
18181     calendarWeeks: false,
18182     
18183     startDate: -Infinity,
18184     
18185     endDate: Infinity,
18186     
18187     daysOfWeekDisabled: [],
18188     
18189     _events: [],
18190     
18191     singleMode : false,
18192     
18193     UTCDate: function()
18194     {
18195         return new Date(Date.UTC.apply(Date, arguments));
18196     },
18197     
18198     UTCToday: function()
18199     {
18200         var today = new Date();
18201         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18202     },
18203     
18204     getDate: function() {
18205             var d = this.getUTCDate();
18206             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18207     },
18208     
18209     getUTCDate: function() {
18210             return this.date;
18211     },
18212     
18213     setDate: function(d) {
18214             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18215     },
18216     
18217     setUTCDate: function(d) {
18218             this.date = d;
18219             this.setValue(this.formatDate(this.date));
18220     },
18221         
18222     onRender: function(ct, position)
18223     {
18224         
18225         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18226         
18227         this.language = this.language || 'en';
18228         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18229         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18230         
18231         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18232         this.format = this.format || 'm/d/y';
18233         this.isInline = false;
18234         this.isInput = true;
18235         this.component = this.el.select('.add-on', true).first() || false;
18236         this.component = (this.component && this.component.length === 0) ? false : this.component;
18237         this.hasInput = this.component && this.inputEl().length;
18238         
18239         if (typeof(this.minViewMode === 'string')) {
18240             switch (this.minViewMode) {
18241                 case 'months':
18242                     this.minViewMode = 1;
18243                     break;
18244                 case 'years':
18245                     this.minViewMode = 2;
18246                     break;
18247                 default:
18248                     this.minViewMode = 0;
18249                     break;
18250             }
18251         }
18252         
18253         if (typeof(this.viewMode === 'string')) {
18254             switch (this.viewMode) {
18255                 case 'months':
18256                     this.viewMode = 1;
18257                     break;
18258                 case 'years':
18259                     this.viewMode = 2;
18260                     break;
18261                 default:
18262                     this.viewMode = 0;
18263                     break;
18264             }
18265         }
18266                 
18267         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18268         
18269 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18270         
18271         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18272         
18273         this.picker().on('mousedown', this.onMousedown, this);
18274         this.picker().on('click', this.onClick, this);
18275         
18276         this.picker().addClass('datepicker-dropdown');
18277         
18278         this.startViewMode = this.viewMode;
18279         
18280         if(this.singleMode){
18281             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18282                 v.setVisibilityMode(Roo.Element.DISPLAY);
18283                 v.hide();
18284             });
18285             
18286             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18287                 v.setStyle('width', '189px');
18288             });
18289         }
18290         
18291         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18292             if(!this.calendarWeeks){
18293                 v.remove();
18294                 return;
18295             }
18296             
18297             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18298             v.attr('colspan', function(i, val){
18299                 return parseInt(val) + 1;
18300             });
18301         });
18302                         
18303         
18304         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18305         
18306         this.setStartDate(this.startDate);
18307         this.setEndDate(this.endDate);
18308         
18309         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18310         
18311         this.fillDow();
18312         this.fillMonths();
18313         this.update();
18314         this.showMode();
18315         
18316         if(this.isInline) {
18317             this.show();
18318         }
18319     },
18320     
18321     picker : function()
18322     {
18323         return this.pickerEl;
18324 //        return this.el.select('.datepicker', true).first();
18325     },
18326     
18327     fillDow: function()
18328     {
18329         var dowCnt = this.weekStart;
18330         
18331         var dow = {
18332             tag: 'tr',
18333             cn: [
18334                 
18335             ]
18336         };
18337         
18338         if(this.calendarWeeks){
18339             dow.cn.push({
18340                 tag: 'th',
18341                 cls: 'cw',
18342                 html: '&nbsp;'
18343             })
18344         }
18345         
18346         while (dowCnt < this.weekStart + 7) {
18347             dow.cn.push({
18348                 tag: 'th',
18349                 cls: 'dow',
18350                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18351             });
18352         }
18353         
18354         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18355     },
18356     
18357     fillMonths: function()
18358     {    
18359         var i = 0;
18360         var months = this.picker().select('>.datepicker-months td', true).first();
18361         
18362         months.dom.innerHTML = '';
18363         
18364         while (i < 12) {
18365             var month = {
18366                 tag: 'span',
18367                 cls: 'month',
18368                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18369             };
18370             
18371             months.createChild(month);
18372         }
18373         
18374     },
18375     
18376     update: function()
18377     {
18378         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;
18379         
18380         if (this.date < this.startDate) {
18381             this.viewDate = new Date(this.startDate);
18382         } else if (this.date > this.endDate) {
18383             this.viewDate = new Date(this.endDate);
18384         } else {
18385             this.viewDate = new Date(this.date);
18386         }
18387         
18388         this.fill();
18389     },
18390     
18391     fill: function() 
18392     {
18393         var d = new Date(this.viewDate),
18394                 year = d.getUTCFullYear(),
18395                 month = d.getUTCMonth(),
18396                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18397                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18398                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18399                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18400                 currentDate = this.date && this.date.valueOf(),
18401                 today = this.UTCToday();
18402         
18403         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18404         
18405 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18406         
18407 //        this.picker.select('>tfoot th.today').
18408 //                                              .text(dates[this.language].today)
18409 //                                              .toggle(this.todayBtn !== false);
18410     
18411         this.updateNavArrows();
18412         this.fillMonths();
18413                                                 
18414         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18415         
18416         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18417          
18418         prevMonth.setUTCDate(day);
18419         
18420         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18421         
18422         var nextMonth = new Date(prevMonth);
18423         
18424         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18425         
18426         nextMonth = nextMonth.valueOf();
18427         
18428         var fillMonths = false;
18429         
18430         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18431         
18432         while(prevMonth.valueOf() < nextMonth) {
18433             var clsName = '';
18434             
18435             if (prevMonth.getUTCDay() === this.weekStart) {
18436                 if(fillMonths){
18437                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18438                 }
18439                     
18440                 fillMonths = {
18441                     tag: 'tr',
18442                     cn: []
18443                 };
18444                 
18445                 if(this.calendarWeeks){
18446                     // ISO 8601: First week contains first thursday.
18447                     // ISO also states week starts on Monday, but we can be more abstract here.
18448                     var
18449                     // Start of current week: based on weekstart/current date
18450                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18451                     // Thursday of this week
18452                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18453                     // First Thursday of year, year from thursday
18454                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18455                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18456                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18457                     
18458                     fillMonths.cn.push({
18459                         tag: 'td',
18460                         cls: 'cw',
18461                         html: calWeek
18462                     });
18463                 }
18464             }
18465             
18466             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18467                 clsName += ' old';
18468             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18469                 clsName += ' new';
18470             }
18471             if (this.todayHighlight &&
18472                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18473                 prevMonth.getUTCMonth() == today.getMonth() &&
18474                 prevMonth.getUTCDate() == today.getDate()) {
18475                 clsName += ' today';
18476             }
18477             
18478             if (currentDate && prevMonth.valueOf() === currentDate) {
18479                 clsName += ' active';
18480             }
18481             
18482             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18483                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18484                     clsName += ' disabled';
18485             }
18486             
18487             fillMonths.cn.push({
18488                 tag: 'td',
18489                 cls: 'day ' + clsName,
18490                 html: prevMonth.getDate()
18491             });
18492             
18493             prevMonth.setDate(prevMonth.getDate()+1);
18494         }
18495           
18496         var currentYear = this.date && this.date.getUTCFullYear();
18497         var currentMonth = this.date && this.date.getUTCMonth();
18498         
18499         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18500         
18501         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18502             v.removeClass('active');
18503             
18504             if(currentYear === year && k === currentMonth){
18505                 v.addClass('active');
18506             }
18507             
18508             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18509                 v.addClass('disabled');
18510             }
18511             
18512         });
18513         
18514         
18515         year = parseInt(year/10, 10) * 10;
18516         
18517         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18518         
18519         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18520         
18521         year -= 1;
18522         for (var i = -1; i < 11; i++) {
18523             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18524                 tag: 'span',
18525                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18526                 html: year
18527             });
18528             
18529             year += 1;
18530         }
18531     },
18532     
18533     showMode: function(dir) 
18534     {
18535         if (dir) {
18536             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18537         }
18538         
18539         Roo.each(this.picker().select('>div',true).elements, function(v){
18540             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18541             v.hide();
18542         });
18543         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18544     },
18545     
18546     place: function()
18547     {
18548         if(this.isInline) {
18549             return;
18550         }
18551         
18552         this.picker().removeClass(['bottom', 'top']);
18553         
18554         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18555             /*
18556              * place to the top of element!
18557              *
18558              */
18559             
18560             this.picker().addClass('top');
18561             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18562             
18563             return;
18564         }
18565         
18566         this.picker().addClass('bottom');
18567         
18568         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18569     },
18570     
18571     parseDate : function(value)
18572     {
18573         if(!value || value instanceof Date){
18574             return value;
18575         }
18576         var v = Date.parseDate(value, this.format);
18577         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18578             v = Date.parseDate(value, 'Y-m-d');
18579         }
18580         if(!v && this.altFormats){
18581             if(!this.altFormatsArray){
18582                 this.altFormatsArray = this.altFormats.split("|");
18583             }
18584             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18585                 v = Date.parseDate(value, this.altFormatsArray[i]);
18586             }
18587         }
18588         return v;
18589     },
18590     
18591     formatDate : function(date, fmt)
18592     {   
18593         return (!date || !(date instanceof Date)) ?
18594         date : date.dateFormat(fmt || this.format);
18595     },
18596     
18597     onFocus : function()
18598     {
18599         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18600         this.show();
18601     },
18602     
18603     onBlur : function()
18604     {
18605         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18606         
18607         var d = this.inputEl().getValue();
18608         
18609         this.setValue(d);
18610                 
18611         this.hide();
18612     },
18613     
18614     show : function()
18615     {
18616         this.picker().show();
18617         this.update();
18618         this.place();
18619         
18620         this.fireEvent('show', this, this.date);
18621     },
18622     
18623     hide : function()
18624     {
18625         if(this.isInline) {
18626             return;
18627         }
18628         this.picker().hide();
18629         this.viewMode = this.startViewMode;
18630         this.showMode();
18631         
18632         this.fireEvent('hide', this, this.date);
18633         
18634     },
18635     
18636     onMousedown: function(e)
18637     {
18638         e.stopPropagation();
18639         e.preventDefault();
18640     },
18641     
18642     keyup: function(e)
18643     {
18644         Roo.bootstrap.DateField.superclass.keyup.call(this);
18645         this.update();
18646     },
18647
18648     setValue: function(v)
18649     {
18650         if(this.fireEvent('beforeselect', this, v) !== false){
18651             var d = new Date(this.parseDate(v) ).clearTime();
18652         
18653             if(isNaN(d.getTime())){
18654                 this.date = this.viewDate = '';
18655                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18656                 return;
18657             }
18658
18659             v = this.formatDate(d);
18660
18661             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18662
18663             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18664
18665             this.update();
18666
18667             this.fireEvent('select', this, this.date);
18668         }
18669     },
18670     
18671     getValue: function()
18672     {
18673         return this.formatDate(this.date);
18674     },
18675     
18676     fireKey: function(e)
18677     {
18678         if (!this.picker().isVisible()){
18679             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18680                 this.show();
18681             }
18682             return;
18683         }
18684         
18685         var dateChanged = false,
18686         dir, day, month,
18687         newDate, newViewDate;
18688         
18689         switch(e.keyCode){
18690             case 27: // escape
18691                 this.hide();
18692                 e.preventDefault();
18693                 break;
18694             case 37: // left
18695             case 39: // right
18696                 if (!this.keyboardNavigation) {
18697                     break;
18698                 }
18699                 dir = e.keyCode == 37 ? -1 : 1;
18700                 
18701                 if (e.ctrlKey){
18702                     newDate = this.moveYear(this.date, dir);
18703                     newViewDate = this.moveYear(this.viewDate, dir);
18704                 } else if (e.shiftKey){
18705                     newDate = this.moveMonth(this.date, dir);
18706                     newViewDate = this.moveMonth(this.viewDate, dir);
18707                 } else {
18708                     newDate = new Date(this.date);
18709                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18710                     newViewDate = new Date(this.viewDate);
18711                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18712                 }
18713                 if (this.dateWithinRange(newDate)){
18714                     this.date = newDate;
18715                     this.viewDate = newViewDate;
18716                     this.setValue(this.formatDate(this.date));
18717 //                    this.update();
18718                     e.preventDefault();
18719                     dateChanged = true;
18720                 }
18721                 break;
18722             case 38: // up
18723             case 40: // down
18724                 if (!this.keyboardNavigation) {
18725                     break;
18726                 }
18727                 dir = e.keyCode == 38 ? -1 : 1;
18728                 if (e.ctrlKey){
18729                     newDate = this.moveYear(this.date, dir);
18730                     newViewDate = this.moveYear(this.viewDate, dir);
18731                 } else if (e.shiftKey){
18732                     newDate = this.moveMonth(this.date, dir);
18733                     newViewDate = this.moveMonth(this.viewDate, dir);
18734                 } else {
18735                     newDate = new Date(this.date);
18736                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18737                     newViewDate = new Date(this.viewDate);
18738                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18739                 }
18740                 if (this.dateWithinRange(newDate)){
18741                     this.date = newDate;
18742                     this.viewDate = newViewDate;
18743                     this.setValue(this.formatDate(this.date));
18744 //                    this.update();
18745                     e.preventDefault();
18746                     dateChanged = true;
18747                 }
18748                 break;
18749             case 13: // enter
18750                 this.setValue(this.formatDate(this.date));
18751                 this.hide();
18752                 e.preventDefault();
18753                 break;
18754             case 9: // tab
18755                 this.setValue(this.formatDate(this.date));
18756                 this.hide();
18757                 break;
18758             case 16: // shift
18759             case 17: // ctrl
18760             case 18: // alt
18761                 break;
18762             default :
18763                 this.hide();
18764                 
18765         }
18766     },
18767     
18768     
18769     onClick: function(e) 
18770     {
18771         e.stopPropagation();
18772         e.preventDefault();
18773         
18774         var target = e.getTarget();
18775         
18776         if(target.nodeName.toLowerCase() === 'i'){
18777             target = Roo.get(target).dom.parentNode;
18778         }
18779         
18780         var nodeName = target.nodeName;
18781         var className = target.className;
18782         var html = target.innerHTML;
18783         //Roo.log(nodeName);
18784         
18785         switch(nodeName.toLowerCase()) {
18786             case 'th':
18787                 switch(className) {
18788                     case 'switch':
18789                         this.showMode(1);
18790                         break;
18791                     case 'prev':
18792                     case 'next':
18793                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18794                         switch(this.viewMode){
18795                                 case 0:
18796                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18797                                         break;
18798                                 case 1:
18799                                 case 2:
18800                                         this.viewDate = this.moveYear(this.viewDate, dir);
18801                                         break;
18802                         }
18803                         this.fill();
18804                         break;
18805                     case 'today':
18806                         var date = new Date();
18807                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18808 //                        this.fill()
18809                         this.setValue(this.formatDate(this.date));
18810                         
18811                         this.hide();
18812                         break;
18813                 }
18814                 break;
18815             case 'span':
18816                 if (className.indexOf('disabled') < 0) {
18817                     this.viewDate.setUTCDate(1);
18818                     if (className.indexOf('month') > -1) {
18819                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18820                     } else {
18821                         var year = parseInt(html, 10) || 0;
18822                         this.viewDate.setUTCFullYear(year);
18823                         
18824                     }
18825                     
18826                     if(this.singleMode){
18827                         this.setValue(this.formatDate(this.viewDate));
18828                         this.hide();
18829                         return;
18830                     }
18831                     
18832                     this.showMode(-1);
18833                     this.fill();
18834                 }
18835                 break;
18836                 
18837             case 'td':
18838                 //Roo.log(className);
18839                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18840                     var day = parseInt(html, 10) || 1;
18841                     var year = this.viewDate.getUTCFullYear(),
18842                         month = this.viewDate.getUTCMonth();
18843
18844                     if (className.indexOf('old') > -1) {
18845                         if(month === 0 ){
18846                             month = 11;
18847                             year -= 1;
18848                         }else{
18849                             month -= 1;
18850                         }
18851                     } else if (className.indexOf('new') > -1) {
18852                         if (month == 11) {
18853                             month = 0;
18854                             year += 1;
18855                         } else {
18856                             month += 1;
18857                         }
18858                     }
18859                     //Roo.log([year,month,day]);
18860                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18861                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18862 //                    this.fill();
18863                     //Roo.log(this.formatDate(this.date));
18864                     this.setValue(this.formatDate(this.date));
18865                     this.hide();
18866                 }
18867                 break;
18868         }
18869     },
18870     
18871     setStartDate: function(startDate)
18872     {
18873         this.startDate = startDate || -Infinity;
18874         if (this.startDate !== -Infinity) {
18875             this.startDate = this.parseDate(this.startDate);
18876         }
18877         this.update();
18878         this.updateNavArrows();
18879     },
18880
18881     setEndDate: function(endDate)
18882     {
18883         this.endDate = endDate || Infinity;
18884         if (this.endDate !== Infinity) {
18885             this.endDate = this.parseDate(this.endDate);
18886         }
18887         this.update();
18888         this.updateNavArrows();
18889     },
18890     
18891     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18892     {
18893         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18894         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18895             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18896         }
18897         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18898             return parseInt(d, 10);
18899         });
18900         this.update();
18901         this.updateNavArrows();
18902     },
18903     
18904     updateNavArrows: function() 
18905     {
18906         if(this.singleMode){
18907             return;
18908         }
18909         
18910         var d = new Date(this.viewDate),
18911         year = d.getUTCFullYear(),
18912         month = d.getUTCMonth();
18913         
18914         Roo.each(this.picker().select('.prev', true).elements, function(v){
18915             v.show();
18916             switch (this.viewMode) {
18917                 case 0:
18918
18919                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18920                         v.hide();
18921                     }
18922                     break;
18923                 case 1:
18924                 case 2:
18925                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18926                         v.hide();
18927                     }
18928                     break;
18929             }
18930         });
18931         
18932         Roo.each(this.picker().select('.next', true).elements, function(v){
18933             v.show();
18934             switch (this.viewMode) {
18935                 case 0:
18936
18937                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18938                         v.hide();
18939                     }
18940                     break;
18941                 case 1:
18942                 case 2:
18943                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18944                         v.hide();
18945                     }
18946                     break;
18947             }
18948         })
18949     },
18950     
18951     moveMonth: function(date, dir)
18952     {
18953         if (!dir) {
18954             return date;
18955         }
18956         var new_date = new Date(date.valueOf()),
18957         day = new_date.getUTCDate(),
18958         month = new_date.getUTCMonth(),
18959         mag = Math.abs(dir),
18960         new_month, test;
18961         dir = dir > 0 ? 1 : -1;
18962         if (mag == 1){
18963             test = dir == -1
18964             // If going back one month, make sure month is not current month
18965             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18966             ? function(){
18967                 return new_date.getUTCMonth() == month;
18968             }
18969             // If going forward one month, make sure month is as expected
18970             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18971             : function(){
18972                 return new_date.getUTCMonth() != new_month;
18973             };
18974             new_month = month + dir;
18975             new_date.setUTCMonth(new_month);
18976             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18977             if (new_month < 0 || new_month > 11) {
18978                 new_month = (new_month + 12) % 12;
18979             }
18980         } else {
18981             // For magnitudes >1, move one month at a time...
18982             for (var i=0; i<mag; i++) {
18983                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18984                 new_date = this.moveMonth(new_date, dir);
18985             }
18986             // ...then reset the day, keeping it in the new month
18987             new_month = new_date.getUTCMonth();
18988             new_date.setUTCDate(day);
18989             test = function(){
18990                 return new_month != new_date.getUTCMonth();
18991             };
18992         }
18993         // Common date-resetting loop -- if date is beyond end of month, make it
18994         // end of month
18995         while (test()){
18996             new_date.setUTCDate(--day);
18997             new_date.setUTCMonth(new_month);
18998         }
18999         return new_date;
19000     },
19001
19002     moveYear: function(date, dir)
19003     {
19004         return this.moveMonth(date, dir*12);
19005     },
19006
19007     dateWithinRange: function(date)
19008     {
19009         return date >= this.startDate && date <= this.endDate;
19010     },
19011
19012     
19013     remove: function() 
19014     {
19015         this.picker().remove();
19016     },
19017     
19018     validateValue : function(value)
19019     {
19020         if(value.length < 1)  {
19021             if(this.allowBlank){
19022                 return true;
19023             }
19024             return false;
19025         }
19026         
19027         if(value.length < this.minLength){
19028             return false;
19029         }
19030         if(value.length > this.maxLength){
19031             return false;
19032         }
19033         if(this.vtype){
19034             var vt = Roo.form.VTypes;
19035             if(!vt[this.vtype](value, this)){
19036                 return false;
19037             }
19038         }
19039         if(typeof this.validator == "function"){
19040             var msg = this.validator(value);
19041             if(msg !== true){
19042                 return false;
19043             }
19044         }
19045         
19046         if(this.regex && !this.regex.test(value)){
19047             return false;
19048         }
19049         
19050         if(typeof(this.parseDate(value)) == 'undefined'){
19051             return false;
19052         }
19053         
19054         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19055             return false;
19056         }      
19057         
19058         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19059             return false;
19060         } 
19061         
19062         
19063         return true;
19064     }
19065    
19066 });
19067
19068 Roo.apply(Roo.bootstrap.DateField,  {
19069     
19070     head : {
19071         tag: 'thead',
19072         cn: [
19073         {
19074             tag: 'tr',
19075             cn: [
19076             {
19077                 tag: 'th',
19078                 cls: 'prev',
19079                 html: '<i class="fa fa-arrow-left"/>'
19080             },
19081             {
19082                 tag: 'th',
19083                 cls: 'switch',
19084                 colspan: '5'
19085             },
19086             {
19087                 tag: 'th',
19088                 cls: 'next',
19089                 html: '<i class="fa fa-arrow-right"/>'
19090             }
19091
19092             ]
19093         }
19094         ]
19095     },
19096     
19097     content : {
19098         tag: 'tbody',
19099         cn: [
19100         {
19101             tag: 'tr',
19102             cn: [
19103             {
19104                 tag: 'td',
19105                 colspan: '7'
19106             }
19107             ]
19108         }
19109         ]
19110     },
19111     
19112     footer : {
19113         tag: 'tfoot',
19114         cn: [
19115         {
19116             tag: 'tr',
19117             cn: [
19118             {
19119                 tag: 'th',
19120                 colspan: '7',
19121                 cls: 'today'
19122             }
19123                     
19124             ]
19125         }
19126         ]
19127     },
19128     
19129     dates:{
19130         en: {
19131             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19132             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19133             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19134             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19135             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19136             today: "Today"
19137         }
19138     },
19139     
19140     modes: [
19141     {
19142         clsName: 'days',
19143         navFnc: 'Month',
19144         navStep: 1
19145     },
19146     {
19147         clsName: 'months',
19148         navFnc: 'FullYear',
19149         navStep: 1
19150     },
19151     {
19152         clsName: 'years',
19153         navFnc: 'FullYear',
19154         navStep: 10
19155     }]
19156 });
19157
19158 Roo.apply(Roo.bootstrap.DateField,  {
19159   
19160     template : {
19161         tag: 'div',
19162         cls: 'datepicker dropdown-menu roo-dynamic',
19163         cn: [
19164         {
19165             tag: 'div',
19166             cls: 'datepicker-days',
19167             cn: [
19168             {
19169                 tag: 'table',
19170                 cls: 'table-condensed',
19171                 cn:[
19172                 Roo.bootstrap.DateField.head,
19173                 {
19174                     tag: 'tbody'
19175                 },
19176                 Roo.bootstrap.DateField.footer
19177                 ]
19178             }
19179             ]
19180         },
19181         {
19182             tag: 'div',
19183             cls: 'datepicker-months',
19184             cn: [
19185             {
19186                 tag: 'table',
19187                 cls: 'table-condensed',
19188                 cn:[
19189                 Roo.bootstrap.DateField.head,
19190                 Roo.bootstrap.DateField.content,
19191                 Roo.bootstrap.DateField.footer
19192                 ]
19193             }
19194             ]
19195         },
19196         {
19197             tag: 'div',
19198             cls: 'datepicker-years',
19199             cn: [
19200             {
19201                 tag: 'table',
19202                 cls: 'table-condensed',
19203                 cn:[
19204                 Roo.bootstrap.DateField.head,
19205                 Roo.bootstrap.DateField.content,
19206                 Roo.bootstrap.DateField.footer
19207                 ]
19208             }
19209             ]
19210         }
19211         ]
19212     }
19213 });
19214
19215  
19216
19217  /*
19218  * - LGPL
19219  *
19220  * TimeField
19221  * 
19222  */
19223
19224 /**
19225  * @class Roo.bootstrap.TimeField
19226  * @extends Roo.bootstrap.Input
19227  * Bootstrap DateField class
19228  * 
19229  * 
19230  * @constructor
19231  * Create a new TimeField
19232  * @param {Object} config The config object
19233  */
19234
19235 Roo.bootstrap.TimeField = function(config){
19236     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19237     this.addEvents({
19238             /**
19239              * @event show
19240              * Fires when this field show.
19241              * @param {Roo.bootstrap.DateField} thisthis
19242              * @param {Mixed} date The date value
19243              */
19244             show : true,
19245             /**
19246              * @event show
19247              * Fires when this field hide.
19248              * @param {Roo.bootstrap.DateField} this
19249              * @param {Mixed} date The date value
19250              */
19251             hide : true,
19252             /**
19253              * @event select
19254              * Fires when select a date.
19255              * @param {Roo.bootstrap.DateField} this
19256              * @param {Mixed} date The date value
19257              */
19258             select : true
19259         });
19260 };
19261
19262 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19263     
19264     /**
19265      * @cfg {String} format
19266      * The default time format string which can be overriden for localization support.  The format must be
19267      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19268      */
19269     format : "H:i",
19270        
19271     onRender: function(ct, position)
19272     {
19273         
19274         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19275                 
19276         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19277         
19278         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19279         
19280         this.pop = this.picker().select('>.datepicker-time',true).first();
19281         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19282         
19283         this.picker().on('mousedown', this.onMousedown, this);
19284         this.picker().on('click', this.onClick, this);
19285         
19286         this.picker().addClass('datepicker-dropdown');
19287     
19288         this.fillTime();
19289         this.update();
19290             
19291         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19292         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19293         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19294         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19295         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19296         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19297
19298     },
19299     
19300     fireKey: function(e){
19301         if (!this.picker().isVisible()){
19302             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19303                 this.show();
19304             }
19305             return;
19306         }
19307
19308         e.preventDefault();
19309         
19310         switch(e.keyCode){
19311             case 27: // escape
19312                 this.hide();
19313                 break;
19314             case 37: // left
19315             case 39: // right
19316                 this.onTogglePeriod();
19317                 break;
19318             case 38: // up
19319                 this.onIncrementMinutes();
19320                 break;
19321             case 40: // down
19322                 this.onDecrementMinutes();
19323                 break;
19324             case 13: // enter
19325             case 9: // tab
19326                 this.setTime();
19327                 break;
19328         }
19329     },
19330     
19331     onClick: function(e) {
19332         e.stopPropagation();
19333         e.preventDefault();
19334     },
19335     
19336     picker : function()
19337     {
19338         return this.el.select('.datepicker', true).first();
19339     },
19340     
19341     fillTime: function()
19342     {    
19343         var time = this.pop.select('tbody', true).first();
19344         
19345         time.dom.innerHTML = '';
19346         
19347         time.createChild({
19348             tag: 'tr',
19349             cn: [
19350                 {
19351                     tag: 'td',
19352                     cn: [
19353                         {
19354                             tag: 'a',
19355                             href: '#',
19356                             cls: 'btn',
19357                             cn: [
19358                                 {
19359                                     tag: 'span',
19360                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19361                                 }
19362                             ]
19363                         } 
19364                     ]
19365                 },
19366                 {
19367                     tag: 'td',
19368                     cls: 'separator'
19369                 },
19370                 {
19371                     tag: 'td',
19372                     cn: [
19373                         {
19374                             tag: 'a',
19375                             href: '#',
19376                             cls: 'btn',
19377                             cn: [
19378                                 {
19379                                     tag: 'span',
19380                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19381                                 }
19382                             ]
19383                         }
19384                     ]
19385                 },
19386                 {
19387                     tag: 'td',
19388                     cls: 'separator'
19389                 }
19390             ]
19391         });
19392         
19393         time.createChild({
19394             tag: 'tr',
19395             cn: [
19396                 {
19397                     tag: 'td',
19398                     cn: [
19399                         {
19400                             tag: 'span',
19401                             cls: 'timepicker-hour',
19402                             html: '00'
19403                         }  
19404                     ]
19405                 },
19406                 {
19407                     tag: 'td',
19408                     cls: 'separator',
19409                     html: ':'
19410                 },
19411                 {
19412                     tag: 'td',
19413                     cn: [
19414                         {
19415                             tag: 'span',
19416                             cls: 'timepicker-minute',
19417                             html: '00'
19418                         }  
19419                     ]
19420                 },
19421                 {
19422                     tag: 'td',
19423                     cls: 'separator'
19424                 },
19425                 {
19426                     tag: 'td',
19427                     cn: [
19428                         {
19429                             tag: 'button',
19430                             type: 'button',
19431                             cls: 'btn btn-primary period',
19432                             html: 'AM'
19433                             
19434                         }
19435                     ]
19436                 }
19437             ]
19438         });
19439         
19440         time.createChild({
19441             tag: 'tr',
19442             cn: [
19443                 {
19444                     tag: 'td',
19445                     cn: [
19446                         {
19447                             tag: 'a',
19448                             href: '#',
19449                             cls: 'btn',
19450                             cn: [
19451                                 {
19452                                     tag: 'span',
19453                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19454                                 }
19455                             ]
19456                         }
19457                     ]
19458                 },
19459                 {
19460                     tag: 'td',
19461                     cls: 'separator'
19462                 },
19463                 {
19464                     tag: 'td',
19465                     cn: [
19466                         {
19467                             tag: 'a',
19468                             href: '#',
19469                             cls: 'btn',
19470                             cn: [
19471                                 {
19472                                     tag: 'span',
19473                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19474                                 }
19475                             ]
19476                         }
19477                     ]
19478                 },
19479                 {
19480                     tag: 'td',
19481                     cls: 'separator'
19482                 }
19483             ]
19484         });
19485         
19486     },
19487     
19488     update: function()
19489     {
19490         
19491         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19492         
19493         this.fill();
19494     },
19495     
19496     fill: function() 
19497     {
19498         var hours = this.time.getHours();
19499         var minutes = this.time.getMinutes();
19500         var period = 'AM';
19501         
19502         if(hours > 11){
19503             period = 'PM';
19504         }
19505         
19506         if(hours == 0){
19507             hours = 12;
19508         }
19509         
19510         
19511         if(hours > 12){
19512             hours = hours - 12;
19513         }
19514         
19515         if(hours < 10){
19516             hours = '0' + hours;
19517         }
19518         
19519         if(minutes < 10){
19520             minutes = '0' + minutes;
19521         }
19522         
19523         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19524         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19525         this.pop.select('button', true).first().dom.innerHTML = period;
19526         
19527     },
19528     
19529     place: function()
19530     {   
19531         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19532         
19533         var cls = ['bottom'];
19534         
19535         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19536             cls.pop();
19537             cls.push('top');
19538         }
19539         
19540         cls.push('right');
19541         
19542         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19543             cls.pop();
19544             cls.push('left');
19545         }
19546         
19547         this.picker().addClass(cls.join('-'));
19548         
19549         var _this = this;
19550         
19551         Roo.each(cls, function(c){
19552             if(c == 'bottom'){
19553                 _this.picker().setTop(_this.inputEl().getHeight());
19554                 return;
19555             }
19556             if(c == 'top'){
19557                 _this.picker().setTop(0 - _this.picker().getHeight());
19558                 return;
19559             }
19560             
19561             if(c == 'left'){
19562                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19563                 return;
19564             }
19565             if(c == 'right'){
19566                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19567                 return;
19568             }
19569         });
19570         
19571     },
19572   
19573     onFocus : function()
19574     {
19575         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19576         this.show();
19577     },
19578     
19579     onBlur : function()
19580     {
19581         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19582         this.hide();
19583     },
19584     
19585     show : function()
19586     {
19587         this.picker().show();
19588         this.pop.show();
19589         this.update();
19590         this.place();
19591         
19592         this.fireEvent('show', this, this.date);
19593     },
19594     
19595     hide : function()
19596     {
19597         this.picker().hide();
19598         this.pop.hide();
19599         
19600         this.fireEvent('hide', this, this.date);
19601     },
19602     
19603     setTime : function()
19604     {
19605         this.hide();
19606         this.setValue(this.time.format(this.format));
19607         
19608         this.fireEvent('select', this, this.date);
19609         
19610         
19611     },
19612     
19613     onMousedown: function(e){
19614         e.stopPropagation();
19615         e.preventDefault();
19616     },
19617     
19618     onIncrementHours: function()
19619     {
19620         Roo.log('onIncrementHours');
19621         this.time = this.time.add(Date.HOUR, 1);
19622         this.update();
19623         
19624     },
19625     
19626     onDecrementHours: function()
19627     {
19628         Roo.log('onDecrementHours');
19629         this.time = this.time.add(Date.HOUR, -1);
19630         this.update();
19631     },
19632     
19633     onIncrementMinutes: function()
19634     {
19635         Roo.log('onIncrementMinutes');
19636         this.time = this.time.add(Date.MINUTE, 1);
19637         this.update();
19638     },
19639     
19640     onDecrementMinutes: function()
19641     {
19642         Roo.log('onDecrementMinutes');
19643         this.time = this.time.add(Date.MINUTE, -1);
19644         this.update();
19645     },
19646     
19647     onTogglePeriod: function()
19648     {
19649         Roo.log('onTogglePeriod');
19650         this.time = this.time.add(Date.HOUR, 12);
19651         this.update();
19652     }
19653     
19654    
19655 });
19656
19657 Roo.apply(Roo.bootstrap.TimeField,  {
19658     
19659     content : {
19660         tag: 'tbody',
19661         cn: [
19662             {
19663                 tag: 'tr',
19664                 cn: [
19665                 {
19666                     tag: 'td',
19667                     colspan: '7'
19668                 }
19669                 ]
19670             }
19671         ]
19672     },
19673     
19674     footer : {
19675         tag: 'tfoot',
19676         cn: [
19677             {
19678                 tag: 'tr',
19679                 cn: [
19680                 {
19681                     tag: 'th',
19682                     colspan: '7',
19683                     cls: '',
19684                     cn: [
19685                         {
19686                             tag: 'button',
19687                             cls: 'btn btn-info ok',
19688                             html: 'OK'
19689                         }
19690                     ]
19691                 }
19692
19693                 ]
19694             }
19695         ]
19696     }
19697 });
19698
19699 Roo.apply(Roo.bootstrap.TimeField,  {
19700   
19701     template : {
19702         tag: 'div',
19703         cls: 'datepicker dropdown-menu',
19704         cn: [
19705             {
19706                 tag: 'div',
19707                 cls: 'datepicker-time',
19708                 cn: [
19709                 {
19710                     tag: 'table',
19711                     cls: 'table-condensed',
19712                     cn:[
19713                     Roo.bootstrap.TimeField.content,
19714                     Roo.bootstrap.TimeField.footer
19715                     ]
19716                 }
19717                 ]
19718             }
19719         ]
19720     }
19721 });
19722
19723  
19724
19725  /*
19726  * - LGPL
19727  *
19728  * MonthField
19729  * 
19730  */
19731
19732 /**
19733  * @class Roo.bootstrap.MonthField
19734  * @extends Roo.bootstrap.Input
19735  * Bootstrap MonthField class
19736  * 
19737  * @cfg {String} language default en
19738  * 
19739  * @constructor
19740  * Create a new MonthField
19741  * @param {Object} config The config object
19742  */
19743
19744 Roo.bootstrap.MonthField = function(config){
19745     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19746     
19747     this.addEvents({
19748         /**
19749          * @event show
19750          * Fires when this field show.
19751          * @param {Roo.bootstrap.MonthField} this
19752          * @param {Mixed} date The date value
19753          */
19754         show : true,
19755         /**
19756          * @event show
19757          * Fires when this field hide.
19758          * @param {Roo.bootstrap.MonthField} this
19759          * @param {Mixed} date The date value
19760          */
19761         hide : true,
19762         /**
19763          * @event select
19764          * Fires when select a date.
19765          * @param {Roo.bootstrap.MonthField} this
19766          * @param {String} oldvalue The old value
19767          * @param {String} newvalue The new value
19768          */
19769         select : true
19770     });
19771 };
19772
19773 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19774     
19775     onRender: function(ct, position)
19776     {
19777         
19778         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19779         
19780         this.language = this.language || 'en';
19781         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19782         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19783         
19784         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19785         this.isInline = false;
19786         this.isInput = true;
19787         this.component = this.el.select('.add-on', true).first() || false;
19788         this.component = (this.component && this.component.length === 0) ? false : this.component;
19789         this.hasInput = this.component && this.inputEL().length;
19790         
19791         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19792         
19793         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19794         
19795         this.picker().on('mousedown', this.onMousedown, this);
19796         this.picker().on('click', this.onClick, this);
19797         
19798         this.picker().addClass('datepicker-dropdown');
19799         
19800         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19801             v.setStyle('width', '189px');
19802         });
19803         
19804         this.fillMonths();
19805         
19806         this.update();
19807         
19808         if(this.isInline) {
19809             this.show();
19810         }
19811         
19812     },
19813     
19814     setValue: function(v, suppressEvent)
19815     {   
19816         var o = this.getValue();
19817         
19818         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19819         
19820         this.update();
19821
19822         if(suppressEvent !== true){
19823             this.fireEvent('select', this, o, v);
19824         }
19825         
19826     },
19827     
19828     getValue: function()
19829     {
19830         return this.value;
19831     },
19832     
19833     onClick: function(e) 
19834     {
19835         e.stopPropagation();
19836         e.preventDefault();
19837         
19838         var target = e.getTarget();
19839         
19840         if(target.nodeName.toLowerCase() === 'i'){
19841             target = Roo.get(target).dom.parentNode;
19842         }
19843         
19844         var nodeName = target.nodeName;
19845         var className = target.className;
19846         var html = target.innerHTML;
19847         
19848         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19849             return;
19850         }
19851         
19852         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19853         
19854         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19855         
19856         this.hide();
19857                         
19858     },
19859     
19860     picker : function()
19861     {
19862         return this.pickerEl;
19863     },
19864     
19865     fillMonths: function()
19866     {    
19867         var i = 0;
19868         var months = this.picker().select('>.datepicker-months td', true).first();
19869         
19870         months.dom.innerHTML = '';
19871         
19872         while (i < 12) {
19873             var month = {
19874                 tag: 'span',
19875                 cls: 'month',
19876                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19877             };
19878             
19879             months.createChild(month);
19880         }
19881         
19882     },
19883     
19884     update: function()
19885     {
19886         var _this = this;
19887         
19888         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19889             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19890         }
19891         
19892         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19893             e.removeClass('active');
19894             
19895             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19896                 e.addClass('active');
19897             }
19898         })
19899     },
19900     
19901     place: function()
19902     {
19903         if(this.isInline) {
19904             return;
19905         }
19906         
19907         this.picker().removeClass(['bottom', 'top']);
19908         
19909         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19910             /*
19911              * place to the top of element!
19912              *
19913              */
19914             
19915             this.picker().addClass('top');
19916             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19917             
19918             return;
19919         }
19920         
19921         this.picker().addClass('bottom');
19922         
19923         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19924     },
19925     
19926     onFocus : function()
19927     {
19928         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19929         this.show();
19930     },
19931     
19932     onBlur : function()
19933     {
19934         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19935         
19936         var d = this.inputEl().getValue();
19937         
19938         this.setValue(d);
19939                 
19940         this.hide();
19941     },
19942     
19943     show : function()
19944     {
19945         this.picker().show();
19946         this.picker().select('>.datepicker-months', true).first().show();
19947         this.update();
19948         this.place();
19949         
19950         this.fireEvent('show', this, this.date);
19951     },
19952     
19953     hide : function()
19954     {
19955         if(this.isInline) {
19956             return;
19957         }
19958         this.picker().hide();
19959         this.fireEvent('hide', this, this.date);
19960         
19961     },
19962     
19963     onMousedown: function(e)
19964     {
19965         e.stopPropagation();
19966         e.preventDefault();
19967     },
19968     
19969     keyup: function(e)
19970     {
19971         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19972         this.update();
19973     },
19974
19975     fireKey: function(e)
19976     {
19977         if (!this.picker().isVisible()){
19978             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19979                 this.show();
19980             }
19981             return;
19982         }
19983         
19984         var dir;
19985         
19986         switch(e.keyCode){
19987             case 27: // escape
19988                 this.hide();
19989                 e.preventDefault();
19990                 break;
19991             case 37: // left
19992             case 39: // right
19993                 dir = e.keyCode == 37 ? -1 : 1;
19994                 
19995                 this.vIndex = this.vIndex + dir;
19996                 
19997                 if(this.vIndex < 0){
19998                     this.vIndex = 0;
19999                 }
20000                 
20001                 if(this.vIndex > 11){
20002                     this.vIndex = 11;
20003                 }
20004                 
20005                 if(isNaN(this.vIndex)){
20006                     this.vIndex = 0;
20007                 }
20008                 
20009                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20010                 
20011                 break;
20012             case 38: // up
20013             case 40: // down
20014                 
20015                 dir = e.keyCode == 38 ? -1 : 1;
20016                 
20017                 this.vIndex = this.vIndex + dir * 4;
20018                 
20019                 if(this.vIndex < 0){
20020                     this.vIndex = 0;
20021                 }
20022                 
20023                 if(this.vIndex > 11){
20024                     this.vIndex = 11;
20025                 }
20026                 
20027                 if(isNaN(this.vIndex)){
20028                     this.vIndex = 0;
20029                 }
20030                 
20031                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20032                 break;
20033                 
20034             case 13: // enter
20035                 
20036                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20037                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20038                 }
20039                 
20040                 this.hide();
20041                 e.preventDefault();
20042                 break;
20043             case 9: // tab
20044                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20045                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20046                 }
20047                 this.hide();
20048                 break;
20049             case 16: // shift
20050             case 17: // ctrl
20051             case 18: // alt
20052                 break;
20053             default :
20054                 this.hide();
20055                 
20056         }
20057     },
20058     
20059     remove: function() 
20060     {
20061         this.picker().remove();
20062     }
20063    
20064 });
20065
20066 Roo.apply(Roo.bootstrap.MonthField,  {
20067     
20068     content : {
20069         tag: 'tbody',
20070         cn: [
20071         {
20072             tag: 'tr',
20073             cn: [
20074             {
20075                 tag: 'td',
20076                 colspan: '7'
20077             }
20078             ]
20079         }
20080         ]
20081     },
20082     
20083     dates:{
20084         en: {
20085             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20086             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20087         }
20088     }
20089 });
20090
20091 Roo.apply(Roo.bootstrap.MonthField,  {
20092   
20093     template : {
20094         tag: 'div',
20095         cls: 'datepicker dropdown-menu roo-dynamic',
20096         cn: [
20097             {
20098                 tag: 'div',
20099                 cls: 'datepicker-months',
20100                 cn: [
20101                 {
20102                     tag: 'table',
20103                     cls: 'table-condensed',
20104                     cn:[
20105                         Roo.bootstrap.DateField.content
20106                     ]
20107                 }
20108                 ]
20109             }
20110         ]
20111     }
20112 });
20113
20114  
20115
20116  
20117  /*
20118  * - LGPL
20119  *
20120  * CheckBox
20121  * 
20122  */
20123
20124 /**
20125  * @class Roo.bootstrap.CheckBox
20126  * @extends Roo.bootstrap.Input
20127  * Bootstrap CheckBox class
20128  * 
20129  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20130  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20131  * @cfg {String} boxLabel The text that appears beside the checkbox
20132  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20133  * @cfg {Boolean} checked initnal the element
20134  * @cfg {Boolean} inline inline the element (default false)
20135  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20136  * @cfg {String} tooltip label tooltip
20137  * 
20138  * @constructor
20139  * Create a new CheckBox
20140  * @param {Object} config The config object
20141  */
20142
20143 Roo.bootstrap.CheckBox = function(config){
20144     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20145    
20146     this.addEvents({
20147         /**
20148         * @event check
20149         * Fires when the element is checked or unchecked.
20150         * @param {Roo.bootstrap.CheckBox} this This input
20151         * @param {Boolean} checked The new checked value
20152         */
20153        check : true,
20154        /**
20155         * @event click
20156         * Fires when the element is click.
20157         * @param {Roo.bootstrap.CheckBox} this This input
20158         */
20159        click : true
20160     });
20161     
20162 };
20163
20164 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20165   
20166     inputType: 'checkbox',
20167     inputValue: 1,
20168     valueOff: 0,
20169     boxLabel: false,
20170     checked: false,
20171     weight : false,
20172     inline: false,
20173     tooltip : '',
20174     
20175     getAutoCreate : function()
20176     {
20177         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20178         
20179         var id = Roo.id();
20180         
20181         var cfg = {};
20182         
20183         cfg.cls = 'form-group ' + this.inputType; //input-group
20184         
20185         if(this.inline){
20186             cfg.cls += ' ' + this.inputType + '-inline';
20187         }
20188         
20189         var input =  {
20190             tag: 'input',
20191             id : id,
20192             type : this.inputType,
20193             value : this.inputValue,
20194             cls : 'roo-' + this.inputType, //'form-box',
20195             placeholder : this.placeholder || ''
20196             
20197         };
20198         
20199         if(this.inputType != 'radio'){
20200             var hidden =  {
20201                 tag: 'input',
20202                 type : 'hidden',
20203                 cls : 'roo-hidden-value',
20204                 value : this.checked ? this.inputValue : this.valueOff
20205             };
20206         }
20207         
20208             
20209         if (this.weight) { // Validity check?
20210             cfg.cls += " " + this.inputType + "-" + this.weight;
20211         }
20212         
20213         if (this.disabled) {
20214             input.disabled=true;
20215         }
20216         
20217         if(this.checked){
20218             input.checked = this.checked;
20219         }
20220         
20221         if (this.name) {
20222             
20223             input.name = this.name;
20224             
20225             if(this.inputType != 'radio'){
20226                 hidden.name = this.name;
20227                 input.name = '_hidden_' + this.name;
20228             }
20229         }
20230         
20231         if (this.size) {
20232             input.cls += ' input-' + this.size;
20233         }
20234         
20235         var settings=this;
20236         
20237         ['xs','sm','md','lg'].map(function(size){
20238             if (settings[size]) {
20239                 cfg.cls += ' col-' + size + '-' + settings[size];
20240             }
20241         });
20242         
20243         var inputblock = input;
20244          
20245         if (this.before || this.after) {
20246             
20247             inputblock = {
20248                 cls : 'input-group',
20249                 cn :  [] 
20250             };
20251             
20252             if (this.before) {
20253                 inputblock.cn.push({
20254                     tag :'span',
20255                     cls : 'input-group-addon',
20256                     html : this.before
20257                 });
20258             }
20259             
20260             inputblock.cn.push(input);
20261             
20262             if(this.inputType != 'radio'){
20263                 inputblock.cn.push(hidden);
20264             }
20265             
20266             if (this.after) {
20267                 inputblock.cn.push({
20268                     tag :'span',
20269                     cls : 'input-group-addon',
20270                     html : this.after
20271                 });
20272             }
20273             
20274         }
20275         
20276         if (align ==='left' && this.fieldLabel.length) {
20277 //                Roo.log("left and has label");
20278             cfg.cn = [
20279                 {
20280                     tag: 'label',
20281                     'for' :  id,
20282                     cls : 'control-label',
20283                     html : this.fieldLabel
20284                 },
20285                 {
20286                     cls : "", 
20287                     cn: [
20288                         inputblock
20289                     ]
20290                 }
20291             ];
20292             
20293             if(this.labelWidth > 12){
20294                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20295             }
20296             
20297             if(this.labelWidth < 13 && this.labelmd == 0){
20298                 this.labelmd = this.labelWidth;
20299             }
20300             
20301             if(this.labellg > 0){
20302                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20303                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20304             }
20305             
20306             if(this.labelmd > 0){
20307                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20308                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20309             }
20310             
20311             if(this.labelsm > 0){
20312                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20313                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20314             }
20315             
20316             if(this.labelxs > 0){
20317                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20318                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20319             }
20320             
20321         } else if ( this.fieldLabel.length) {
20322 //                Roo.log(" label");
20323                 cfg.cn = [
20324                    
20325                     {
20326                         tag: this.boxLabel ? 'span' : 'label',
20327                         'for': id,
20328                         cls: 'control-label box-input-label',
20329                         //cls : 'input-group-addon',
20330                         html : this.fieldLabel
20331                     },
20332                     
20333                     inputblock
20334                     
20335                 ];
20336
20337         } else {
20338             
20339 //                Roo.log(" no label && no align");
20340                 cfg.cn = [  inputblock ] ;
20341                 
20342                 
20343         }
20344         
20345         if(this.boxLabel){
20346              var boxLabelCfg = {
20347                 tag: 'label',
20348                 //'for': id, // box label is handled by onclick - so no for...
20349                 cls: 'box-label',
20350                 html: this.boxLabel
20351             };
20352             
20353             if(this.tooltip){
20354                 boxLabelCfg.tooltip = this.tooltip;
20355             }
20356              
20357             cfg.cn.push(boxLabelCfg);
20358         }
20359         
20360         if(this.inputType != 'radio'){
20361             cfg.cn.push(hidden);
20362         }
20363         
20364         return cfg;
20365         
20366     },
20367     
20368     /**
20369      * return the real input element.
20370      */
20371     inputEl: function ()
20372     {
20373         return this.el.select('input.roo-' + this.inputType,true).first();
20374     },
20375     hiddenEl: function ()
20376     {
20377         return this.el.select('input.roo-hidden-value',true).first();
20378     },
20379     
20380     labelEl: function()
20381     {
20382         return this.el.select('label.control-label',true).first();
20383     },
20384     /* depricated... */
20385     
20386     label: function()
20387     {
20388         return this.labelEl();
20389     },
20390     
20391     boxLabelEl: function()
20392     {
20393         return this.el.select('label.box-label',true).first();
20394     },
20395     
20396     initEvents : function()
20397     {
20398 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20399         
20400         this.inputEl().on('click', this.onClick,  this);
20401         
20402         if (this.boxLabel) { 
20403             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20404         }
20405         
20406         this.startValue = this.getValue();
20407         
20408         if(this.groupId){
20409             Roo.bootstrap.CheckBox.register(this);
20410         }
20411     },
20412     
20413     onClick : function(e)
20414     {   
20415         if(this.fireEvent('click', this, e) !== false){
20416             this.setChecked(!this.checked);
20417         }
20418         
20419     },
20420     
20421     setChecked : function(state,suppressEvent)
20422     {
20423         this.startValue = this.getValue();
20424
20425         if(this.inputType == 'radio'){
20426             
20427             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20428                 e.dom.checked = false;
20429             });
20430             
20431             this.inputEl().dom.checked = true;
20432             
20433             this.inputEl().dom.value = this.inputValue;
20434             
20435             if(suppressEvent !== true){
20436                 this.fireEvent('check', this, true);
20437             }
20438             
20439             this.validate();
20440             
20441             return;
20442         }
20443         
20444         this.checked = state;
20445         
20446         this.inputEl().dom.checked = state;
20447         
20448         
20449         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20450         
20451         if(suppressEvent !== true){
20452             this.fireEvent('check', this, state);
20453         }
20454         
20455         this.validate();
20456     },
20457     
20458     getValue : function()
20459     {
20460         if(this.inputType == 'radio'){
20461             return this.getGroupValue();
20462         }
20463         
20464         return this.hiddenEl().dom.value;
20465         
20466     },
20467     
20468     getGroupValue : function()
20469     {
20470         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20471             return '';
20472         }
20473         
20474         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20475     },
20476     
20477     setValue : function(v,suppressEvent)
20478     {
20479         if(this.inputType == 'radio'){
20480             this.setGroupValue(v, suppressEvent);
20481             return;
20482         }
20483         
20484         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20485         
20486         this.validate();
20487     },
20488     
20489     setGroupValue : function(v, suppressEvent)
20490     {
20491         this.startValue = this.getValue();
20492         
20493         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20494             e.dom.checked = false;
20495             
20496             if(e.dom.value == v){
20497                 e.dom.checked = true;
20498             }
20499         });
20500         
20501         if(suppressEvent !== true){
20502             this.fireEvent('check', this, true);
20503         }
20504
20505         this.validate();
20506         
20507         return;
20508     },
20509     
20510     validate : function()
20511     {
20512         if(
20513                 this.disabled || 
20514                 (this.inputType == 'radio' && this.validateRadio()) ||
20515                 (this.inputType == 'checkbox' && this.validateCheckbox())
20516         ){
20517             this.markValid();
20518             return true;
20519         }
20520         
20521         this.markInvalid();
20522         return false;
20523     },
20524     
20525     validateRadio : function()
20526     {
20527         if(this.allowBlank){
20528             return true;
20529         }
20530         
20531         var valid = false;
20532         
20533         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20534             if(!e.dom.checked){
20535                 return;
20536             }
20537             
20538             valid = true;
20539             
20540             return false;
20541         });
20542         
20543         return valid;
20544     },
20545     
20546     validateCheckbox : function()
20547     {
20548         if(!this.groupId){
20549             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20550             //return (this.getValue() == this.inputValue) ? true : false;
20551         }
20552         
20553         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20554         
20555         if(!group){
20556             return false;
20557         }
20558         
20559         var r = false;
20560         
20561         for(var i in group){
20562             if(group[i].el.isVisible(true)){
20563                 r = false;
20564                 break;
20565             }
20566             
20567             r = true;
20568         }
20569         
20570         for(var i in group){
20571             if(r){
20572                 break;
20573             }
20574             
20575             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20576         }
20577         
20578         return r;
20579     },
20580     
20581     /**
20582      * Mark this field as valid
20583      */
20584     markValid : function()
20585     {
20586         var _this = this;
20587         
20588         this.fireEvent('valid', this);
20589         
20590         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20591         
20592         if(this.groupId){
20593             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20594         }
20595         
20596         if(label){
20597             label.markValid();
20598         }
20599
20600         if(this.inputType == 'radio'){
20601             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20602                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20603                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20604             });
20605             
20606             return;
20607         }
20608
20609         if(!this.groupId){
20610             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20611             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20612             return;
20613         }
20614         
20615         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20616         
20617         if(!group){
20618             return;
20619         }
20620         
20621         for(var i in group){
20622             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20623             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20624         }
20625     },
20626     
20627      /**
20628      * Mark this field as invalid
20629      * @param {String} msg The validation message
20630      */
20631     markInvalid : function(msg)
20632     {
20633         if(this.allowBlank){
20634             return;
20635         }
20636         
20637         var _this = this;
20638         
20639         this.fireEvent('invalid', this, msg);
20640         
20641         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20642         
20643         if(this.groupId){
20644             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20645         }
20646         
20647         if(label){
20648             label.markInvalid();
20649         }
20650             
20651         if(this.inputType == 'radio'){
20652             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20653                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20654                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20655             });
20656             
20657             return;
20658         }
20659         
20660         if(!this.groupId){
20661             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20662             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20663             return;
20664         }
20665         
20666         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20667         
20668         if(!group){
20669             return;
20670         }
20671         
20672         for(var i in group){
20673             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20674             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20675         }
20676         
20677     },
20678     
20679     clearInvalid : function()
20680     {
20681         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20682         
20683         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20684         
20685         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20686         
20687         if (label && label.iconEl) {
20688             label.iconEl.removeClass(label.validClass);
20689             label.iconEl.removeClass(label.invalidClass);
20690         }
20691     },
20692     
20693     disable : function()
20694     {
20695         if(this.inputType != 'radio'){
20696             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20697             return;
20698         }
20699         
20700         var _this = this;
20701         
20702         if(this.rendered){
20703             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20704                 _this.getActionEl().addClass(this.disabledClass);
20705                 e.dom.disabled = true;
20706             });
20707         }
20708         
20709         this.disabled = true;
20710         this.fireEvent("disable", this);
20711         return this;
20712     },
20713
20714     enable : function()
20715     {
20716         if(this.inputType != 'radio'){
20717             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20718             return;
20719         }
20720         
20721         var _this = this;
20722         
20723         if(this.rendered){
20724             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20725                 _this.getActionEl().removeClass(this.disabledClass);
20726                 e.dom.disabled = false;
20727             });
20728         }
20729         
20730         this.disabled = false;
20731         this.fireEvent("enable", this);
20732         return this;
20733     }
20734
20735 });
20736
20737 Roo.apply(Roo.bootstrap.CheckBox, {
20738     
20739     groups: {},
20740     
20741      /**
20742     * register a CheckBox Group
20743     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20744     */
20745     register : function(checkbox)
20746     {
20747         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20748             this.groups[checkbox.groupId] = {};
20749         }
20750         
20751         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20752             return;
20753         }
20754         
20755         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20756         
20757     },
20758     /**
20759     * fetch a CheckBox Group based on the group ID
20760     * @param {string} the group ID
20761     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20762     */
20763     get: function(groupId) {
20764         if (typeof(this.groups[groupId]) == 'undefined') {
20765             return false;
20766         }
20767         
20768         return this.groups[groupId] ;
20769     }
20770     
20771     
20772 });
20773 /*
20774  * - LGPL
20775  *
20776  * RadioItem
20777  * 
20778  */
20779
20780 /**
20781  * @class Roo.bootstrap.Radio
20782  * @extends Roo.bootstrap.Component
20783  * Bootstrap Radio class
20784  * @cfg {String} boxLabel - the label associated
20785  * @cfg {String} value - the value of radio
20786  * 
20787  * @constructor
20788  * Create a new Radio
20789  * @param {Object} config The config object
20790  */
20791 Roo.bootstrap.Radio = function(config){
20792     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20793     
20794 };
20795
20796 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20797     
20798     boxLabel : '',
20799     
20800     value : '',
20801     
20802     getAutoCreate : function()
20803     {
20804         var cfg = {
20805             tag : 'div',
20806             cls : 'form-group radio',
20807             cn : [
20808                 {
20809                     tag : 'label',
20810                     cls : 'box-label',
20811                     html : this.boxLabel
20812                 }
20813             ]
20814         };
20815         
20816         return cfg;
20817     },
20818     
20819     initEvents : function() 
20820     {
20821         this.parent().register(this);
20822         
20823         this.el.on('click', this.onClick, this);
20824         
20825     },
20826     
20827     onClick : function()
20828     {
20829         this.setChecked(true);
20830     },
20831     
20832     setChecked : function(state, suppressEvent)
20833     {
20834         this.parent().setValue(this.value, suppressEvent);
20835         
20836     },
20837     
20838     setBoxLabel : function(v)
20839     {
20840         this.boxLabel = v;
20841         
20842         if(this.rendered){
20843             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20844         }
20845     }
20846     
20847 });
20848  
20849
20850  /*
20851  * - LGPL
20852  *
20853  * Input
20854  * 
20855  */
20856
20857 /**
20858  * @class Roo.bootstrap.SecurePass
20859  * @extends Roo.bootstrap.Input
20860  * Bootstrap SecurePass class
20861  *
20862  * 
20863  * @constructor
20864  * Create a new SecurePass
20865  * @param {Object} config The config object
20866  */
20867  
20868 Roo.bootstrap.SecurePass = function (config) {
20869     // these go here, so the translation tool can replace them..
20870     this.errors = {
20871         PwdEmpty: "Please type a password, and then retype it to confirm.",
20872         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20873         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20874         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20875         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20876         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20877         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20878         TooWeak: "Your password is Too Weak."
20879     },
20880     this.meterLabel = "Password strength:";
20881     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20882     this.meterClass = [
20883         "roo-password-meter-tooweak", 
20884         "roo-password-meter-weak", 
20885         "roo-password-meter-medium", 
20886         "roo-password-meter-strong", 
20887         "roo-password-meter-grey"
20888     ];
20889     
20890     this.errors = {};
20891     
20892     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20893 }
20894
20895 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20896     /**
20897      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20898      * {
20899      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20900      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20901      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20902      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20903      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20904      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20905      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20906      * })
20907      */
20908     // private
20909     
20910     meterWidth: 300,
20911     errorMsg :'',    
20912     errors: false,
20913     imageRoot: '/',
20914     /**
20915      * @cfg {String/Object} Label for the strength meter (defaults to
20916      * 'Password strength:')
20917      */
20918     // private
20919     meterLabel: '',
20920     /**
20921      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20922      * ['Weak', 'Medium', 'Strong'])
20923      */
20924     // private    
20925     pwdStrengths: false,    
20926     // private
20927     strength: 0,
20928     // private
20929     _lastPwd: null,
20930     // private
20931     kCapitalLetter: 0,
20932     kSmallLetter: 1,
20933     kDigit: 2,
20934     kPunctuation: 3,
20935     
20936     insecure: false,
20937     // private
20938     initEvents: function ()
20939     {
20940         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20941
20942         if (this.el.is('input[type=password]') && Roo.isSafari) {
20943             this.el.on('keydown', this.SafariOnKeyDown, this);
20944         }
20945
20946         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20947     },
20948     // private
20949     onRender: function (ct, position)
20950     {
20951         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20952         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20953         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20954
20955         this.trigger.createChild({
20956                    cn: [
20957                     {
20958                     //id: 'PwdMeter',
20959                     tag: 'div',
20960                     cls: 'roo-password-meter-grey col-xs-12',
20961                     style: {
20962                         //width: 0,
20963                         //width: this.meterWidth + 'px'                                                
20964                         }
20965                     },
20966                     {                            
20967                          cls: 'roo-password-meter-text'                          
20968                     }
20969                 ]            
20970         });
20971
20972          
20973         if (this.hideTrigger) {
20974             this.trigger.setDisplayed(false);
20975         }
20976         this.setSize(this.width || '', this.height || '');
20977     },
20978     // private
20979     onDestroy: function ()
20980     {
20981         if (this.trigger) {
20982             this.trigger.removeAllListeners();
20983             this.trigger.remove();
20984         }
20985         if (this.wrap) {
20986             this.wrap.remove();
20987         }
20988         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20989     },
20990     // private
20991     checkStrength: function ()
20992     {
20993         var pwd = this.inputEl().getValue();
20994         if (pwd == this._lastPwd) {
20995             return;
20996         }
20997
20998         var strength;
20999         if (this.ClientSideStrongPassword(pwd)) {
21000             strength = 3;
21001         } else if (this.ClientSideMediumPassword(pwd)) {
21002             strength = 2;
21003         } else if (this.ClientSideWeakPassword(pwd)) {
21004             strength = 1;
21005         } else {
21006             strength = 0;
21007         }
21008         
21009         Roo.log('strength1: ' + strength);
21010         
21011         //var pm = this.trigger.child('div/div/div').dom;
21012         var pm = this.trigger.child('div/div');
21013         pm.removeClass(this.meterClass);
21014         pm.addClass(this.meterClass[strength]);
21015                 
21016         
21017         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21018                 
21019         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21020         
21021         this._lastPwd = pwd;
21022     },
21023     reset: function ()
21024     {
21025         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21026         
21027         this._lastPwd = '';
21028         
21029         var pm = this.trigger.child('div/div');
21030         pm.removeClass(this.meterClass);
21031         pm.addClass('roo-password-meter-grey');        
21032         
21033         
21034         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21035         
21036         pt.innerHTML = '';
21037         this.inputEl().dom.type='password';
21038     },
21039     // private
21040     validateValue: function (value)
21041     {
21042         
21043         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21044             return false;
21045         }
21046         if (value.length == 0) {
21047             if (this.allowBlank) {
21048                 this.clearInvalid();
21049                 return true;
21050             }
21051
21052             this.markInvalid(this.errors.PwdEmpty);
21053             this.errorMsg = this.errors.PwdEmpty;
21054             return false;
21055         }
21056         
21057         if(this.insecure){
21058             return true;
21059         }
21060         
21061         if ('[\x21-\x7e]*'.match(value)) {
21062             this.markInvalid(this.errors.PwdBadChar);
21063             this.errorMsg = this.errors.PwdBadChar;
21064             return false;
21065         }
21066         if (value.length < 6) {
21067             this.markInvalid(this.errors.PwdShort);
21068             this.errorMsg = this.errors.PwdShort;
21069             return false;
21070         }
21071         if (value.length > 16) {
21072             this.markInvalid(this.errors.PwdLong);
21073             this.errorMsg = this.errors.PwdLong;
21074             return false;
21075         }
21076         var strength;
21077         if (this.ClientSideStrongPassword(value)) {
21078             strength = 3;
21079         } else if (this.ClientSideMediumPassword(value)) {
21080             strength = 2;
21081         } else if (this.ClientSideWeakPassword(value)) {
21082             strength = 1;
21083         } else {
21084             strength = 0;
21085         }
21086
21087         
21088         if (strength < 2) {
21089             //this.markInvalid(this.errors.TooWeak);
21090             this.errorMsg = this.errors.TooWeak;
21091             //return false;
21092         }
21093         
21094         
21095         console.log('strength2: ' + strength);
21096         
21097         //var pm = this.trigger.child('div/div/div').dom;
21098         
21099         var pm = this.trigger.child('div/div');
21100         pm.removeClass(this.meterClass);
21101         pm.addClass(this.meterClass[strength]);
21102                 
21103         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21104                 
21105         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21106         
21107         this.errorMsg = ''; 
21108         return true;
21109     },
21110     // private
21111     CharacterSetChecks: function (type)
21112     {
21113         this.type = type;
21114         this.fResult = false;
21115     },
21116     // private
21117     isctype: function (character, type)
21118     {
21119         switch (type) {  
21120             case this.kCapitalLetter:
21121                 if (character >= 'A' && character <= 'Z') {
21122                     return true;
21123                 }
21124                 break;
21125             
21126             case this.kSmallLetter:
21127                 if (character >= 'a' && character <= 'z') {
21128                     return true;
21129                 }
21130                 break;
21131             
21132             case this.kDigit:
21133                 if (character >= '0' && character <= '9') {
21134                     return true;
21135                 }
21136                 break;
21137             
21138             case this.kPunctuation:
21139                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21140                     return true;
21141                 }
21142                 break;
21143             
21144             default:
21145                 return false;
21146         }
21147
21148     },
21149     // private
21150     IsLongEnough: function (pwd, size)
21151     {
21152         return !(pwd == null || isNaN(size) || pwd.length < size);
21153     },
21154     // private
21155     SpansEnoughCharacterSets: function (word, nb)
21156     {
21157         if (!this.IsLongEnough(word, nb))
21158         {
21159             return false;
21160         }
21161
21162         var characterSetChecks = new Array(
21163             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21164             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21165         );
21166         
21167         for (var index = 0; index < word.length; ++index) {
21168             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21169                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21170                     characterSetChecks[nCharSet].fResult = true;
21171                     break;
21172                 }
21173             }
21174         }
21175
21176         var nCharSets = 0;
21177         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21178             if (characterSetChecks[nCharSet].fResult) {
21179                 ++nCharSets;
21180             }
21181         }
21182
21183         if (nCharSets < nb) {
21184             return false;
21185         }
21186         return true;
21187     },
21188     // private
21189     ClientSideStrongPassword: function (pwd)
21190     {
21191         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21192     },
21193     // private
21194     ClientSideMediumPassword: function (pwd)
21195     {
21196         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21197     },
21198     // private
21199     ClientSideWeakPassword: function (pwd)
21200     {
21201         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21202     }
21203           
21204 })//<script type="text/javascript">
21205
21206 /*
21207  * Based  Ext JS Library 1.1.1
21208  * Copyright(c) 2006-2007, Ext JS, LLC.
21209  * LGPL
21210  *
21211  */
21212  
21213 /**
21214  * @class Roo.HtmlEditorCore
21215  * @extends Roo.Component
21216  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21217  *
21218  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21219  */
21220
21221 Roo.HtmlEditorCore = function(config){
21222     
21223     
21224     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21225     
21226     
21227     this.addEvents({
21228         /**
21229          * @event initialize
21230          * Fires when the editor is fully initialized (including the iframe)
21231          * @param {Roo.HtmlEditorCore} this
21232          */
21233         initialize: true,
21234         /**
21235          * @event activate
21236          * Fires when the editor is first receives the focus. Any insertion must wait
21237          * until after this event.
21238          * @param {Roo.HtmlEditorCore} this
21239          */
21240         activate: true,
21241          /**
21242          * @event beforesync
21243          * Fires before the textarea is updated with content from the editor iframe. Return false
21244          * to cancel the sync.
21245          * @param {Roo.HtmlEditorCore} this
21246          * @param {String} html
21247          */
21248         beforesync: true,
21249          /**
21250          * @event beforepush
21251          * Fires before the iframe editor is updated with content from the textarea. Return false
21252          * to cancel the push.
21253          * @param {Roo.HtmlEditorCore} this
21254          * @param {String} html
21255          */
21256         beforepush: true,
21257          /**
21258          * @event sync
21259          * Fires when the textarea is updated with content from the editor iframe.
21260          * @param {Roo.HtmlEditorCore} this
21261          * @param {String} html
21262          */
21263         sync: true,
21264          /**
21265          * @event push
21266          * Fires when the iframe editor is updated with content from the textarea.
21267          * @param {Roo.HtmlEditorCore} this
21268          * @param {String} html
21269          */
21270         push: true,
21271         
21272         /**
21273          * @event editorevent
21274          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21275          * @param {Roo.HtmlEditorCore} this
21276          */
21277         editorevent: true
21278         
21279     });
21280     
21281     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21282     
21283     // defaults : white / black...
21284     this.applyBlacklists();
21285     
21286     
21287     
21288 };
21289
21290
21291 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21292
21293
21294      /**
21295      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21296      */
21297     
21298     owner : false,
21299     
21300      /**
21301      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21302      *                        Roo.resizable.
21303      */
21304     resizable : false,
21305      /**
21306      * @cfg {Number} height (in pixels)
21307      */   
21308     height: 300,
21309    /**
21310      * @cfg {Number} width (in pixels)
21311      */   
21312     width: 500,
21313     
21314     /**
21315      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21316      * 
21317      */
21318     stylesheets: false,
21319     
21320     // id of frame..
21321     frameId: false,
21322     
21323     // private properties
21324     validationEvent : false,
21325     deferHeight: true,
21326     initialized : false,
21327     activated : false,
21328     sourceEditMode : false,
21329     onFocus : Roo.emptyFn,
21330     iframePad:3,
21331     hideMode:'offsets',
21332     
21333     clearUp: true,
21334     
21335     // blacklist + whitelisted elements..
21336     black: false,
21337     white: false,
21338      
21339     bodyCls : '',
21340
21341     /**
21342      * Protected method that will not generally be called directly. It
21343      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21344      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21345      */
21346     getDocMarkup : function(){
21347         // body styles..
21348         var st = '';
21349         
21350         // inherit styels from page...?? 
21351         if (this.stylesheets === false) {
21352             
21353             Roo.get(document.head).select('style').each(function(node) {
21354                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21355             });
21356             
21357             Roo.get(document.head).select('link').each(function(node) { 
21358                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21359             });
21360             
21361         } else if (!this.stylesheets.length) {
21362                 // simple..
21363                 st = '<style type="text/css">' +
21364                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21365                    '</style>';
21366         } else { 
21367             st = '<style type="text/css">' +
21368                     this.stylesheets +
21369                 '</style>';
21370         }
21371         
21372         st +=  '<style type="text/css">' +
21373             'IMG { cursor: pointer } ' +
21374         '</style>';
21375
21376         var cls = 'roo-htmleditor-body';
21377         
21378         if(this.bodyCls.length){
21379             cls += ' ' + this.bodyCls;
21380         }
21381         
21382         return '<html><head>' + st  +
21383             //<style type="text/css">' +
21384             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21385             //'</style>' +
21386             ' </head><body class="' +  cls + '"></body></html>';
21387     },
21388
21389     // private
21390     onRender : function(ct, position)
21391     {
21392         var _t = this;
21393         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21394         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21395         
21396         
21397         this.el.dom.style.border = '0 none';
21398         this.el.dom.setAttribute('tabIndex', -1);
21399         this.el.addClass('x-hidden hide');
21400         
21401         
21402         
21403         if(Roo.isIE){ // fix IE 1px bogus margin
21404             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21405         }
21406        
21407         
21408         this.frameId = Roo.id();
21409         
21410          
21411         
21412         var iframe = this.owner.wrap.createChild({
21413             tag: 'iframe',
21414             cls: 'form-control', // bootstrap..
21415             id: this.frameId,
21416             name: this.frameId,
21417             frameBorder : 'no',
21418             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21419         }, this.el
21420         );
21421         
21422         
21423         this.iframe = iframe.dom;
21424
21425          this.assignDocWin();
21426         
21427         this.doc.designMode = 'on';
21428        
21429         this.doc.open();
21430         this.doc.write(this.getDocMarkup());
21431         this.doc.close();
21432
21433         
21434         var task = { // must defer to wait for browser to be ready
21435             run : function(){
21436                 //console.log("run task?" + this.doc.readyState);
21437                 this.assignDocWin();
21438                 if(this.doc.body || this.doc.readyState == 'complete'){
21439                     try {
21440                         this.doc.designMode="on";
21441                     } catch (e) {
21442                         return;
21443                     }
21444                     Roo.TaskMgr.stop(task);
21445                     this.initEditor.defer(10, this);
21446                 }
21447             },
21448             interval : 10,
21449             duration: 10000,
21450             scope: this
21451         };
21452         Roo.TaskMgr.start(task);
21453
21454     },
21455
21456     // private
21457     onResize : function(w, h)
21458     {
21459          Roo.log('resize: ' +w + ',' + h );
21460         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21461         if(!this.iframe){
21462             return;
21463         }
21464         if(typeof w == 'number'){
21465             
21466             this.iframe.style.width = w + 'px';
21467         }
21468         if(typeof h == 'number'){
21469             
21470             this.iframe.style.height = h + 'px';
21471             if(this.doc){
21472                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21473             }
21474         }
21475         
21476     },
21477
21478     /**
21479      * Toggles the editor between standard and source edit mode.
21480      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21481      */
21482     toggleSourceEdit : function(sourceEditMode){
21483         
21484         this.sourceEditMode = sourceEditMode === true;
21485         
21486         if(this.sourceEditMode){
21487  
21488             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21489             
21490         }else{
21491             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21492             //this.iframe.className = '';
21493             this.deferFocus();
21494         }
21495         //this.setSize(this.owner.wrap.getSize());
21496         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21497     },
21498
21499     
21500   
21501
21502     /**
21503      * Protected method that will not generally be called directly. If you need/want
21504      * custom HTML cleanup, this is the method you should override.
21505      * @param {String} html The HTML to be cleaned
21506      * return {String} The cleaned HTML
21507      */
21508     cleanHtml : function(html){
21509         html = String(html);
21510         if(html.length > 5){
21511             if(Roo.isSafari){ // strip safari nonsense
21512                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21513             }
21514         }
21515         if(html == '&nbsp;'){
21516             html = '';
21517         }
21518         return html;
21519     },
21520
21521     /**
21522      * HTML Editor -> Textarea
21523      * Protected method that will not generally be called directly. Syncs the contents
21524      * of the editor iframe with the textarea.
21525      */
21526     syncValue : function(){
21527         if(this.initialized){
21528             var bd = (this.doc.body || this.doc.documentElement);
21529             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21530             var html = bd.innerHTML;
21531             if(Roo.isSafari){
21532                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21533                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21534                 if(m && m[1]){
21535                     html = '<div style="'+m[0]+'">' + html + '</div>';
21536                 }
21537             }
21538             html = this.cleanHtml(html);
21539             // fix up the special chars.. normaly like back quotes in word...
21540             // however we do not want to do this with chinese..
21541             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21542                 var cc = b.charCodeAt();
21543                 if (
21544                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21545                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21546                     (cc >= 0xf900 && cc < 0xfb00 )
21547                 ) {
21548                         return b;
21549                 }
21550                 return "&#"+cc+";" 
21551             });
21552             if(this.owner.fireEvent('beforesync', this, html) !== false){
21553                 this.el.dom.value = html;
21554                 this.owner.fireEvent('sync', this, html);
21555             }
21556         }
21557     },
21558
21559     /**
21560      * Protected method that will not generally be called directly. Pushes the value of the textarea
21561      * into the iframe editor.
21562      */
21563     pushValue : function(){
21564         if(this.initialized){
21565             var v = this.el.dom.value.trim();
21566             
21567 //            if(v.length < 1){
21568 //                v = '&#160;';
21569 //            }
21570             
21571             if(this.owner.fireEvent('beforepush', this, v) !== false){
21572                 var d = (this.doc.body || this.doc.documentElement);
21573                 d.innerHTML = v;
21574                 this.cleanUpPaste();
21575                 this.el.dom.value = d.innerHTML;
21576                 this.owner.fireEvent('push', this, v);
21577             }
21578         }
21579     },
21580
21581     // private
21582     deferFocus : function(){
21583         this.focus.defer(10, this);
21584     },
21585
21586     // doc'ed in Field
21587     focus : function(){
21588         if(this.win && !this.sourceEditMode){
21589             this.win.focus();
21590         }else{
21591             this.el.focus();
21592         }
21593     },
21594     
21595     assignDocWin: function()
21596     {
21597         var iframe = this.iframe;
21598         
21599          if(Roo.isIE){
21600             this.doc = iframe.contentWindow.document;
21601             this.win = iframe.contentWindow;
21602         } else {
21603 //            if (!Roo.get(this.frameId)) {
21604 //                return;
21605 //            }
21606 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21607 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21608             
21609             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21610                 return;
21611             }
21612             
21613             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21614             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21615         }
21616     },
21617     
21618     // private
21619     initEditor : function(){
21620         //console.log("INIT EDITOR");
21621         this.assignDocWin();
21622         
21623         
21624         
21625         this.doc.designMode="on";
21626         this.doc.open();
21627         this.doc.write(this.getDocMarkup());
21628         this.doc.close();
21629         
21630         var dbody = (this.doc.body || this.doc.documentElement);
21631         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21632         // this copies styles from the containing element into thsi one..
21633         // not sure why we need all of this..
21634         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21635         
21636         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21637         //ss['background-attachment'] = 'fixed'; // w3c
21638         dbody.bgProperties = 'fixed'; // ie
21639         //Roo.DomHelper.applyStyles(dbody, ss);
21640         Roo.EventManager.on(this.doc, {
21641             //'mousedown': this.onEditorEvent,
21642             'mouseup': this.onEditorEvent,
21643             'dblclick': this.onEditorEvent,
21644             'click': this.onEditorEvent,
21645             'keyup': this.onEditorEvent,
21646             buffer:100,
21647             scope: this
21648         });
21649         if(Roo.isGecko){
21650             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21651         }
21652         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21653             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21654         }
21655         this.initialized = true;
21656
21657         this.owner.fireEvent('initialize', this);
21658         this.pushValue();
21659     },
21660
21661     // private
21662     onDestroy : function(){
21663         
21664         
21665         
21666         if(this.rendered){
21667             
21668             //for (var i =0; i < this.toolbars.length;i++) {
21669             //    // fixme - ask toolbars for heights?
21670             //    this.toolbars[i].onDestroy();
21671            // }
21672             
21673             //this.wrap.dom.innerHTML = '';
21674             //this.wrap.remove();
21675         }
21676     },
21677
21678     // private
21679     onFirstFocus : function(){
21680         
21681         this.assignDocWin();
21682         
21683         
21684         this.activated = true;
21685          
21686     
21687         if(Roo.isGecko){ // prevent silly gecko errors
21688             this.win.focus();
21689             var s = this.win.getSelection();
21690             if(!s.focusNode || s.focusNode.nodeType != 3){
21691                 var r = s.getRangeAt(0);
21692                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21693                 r.collapse(true);
21694                 this.deferFocus();
21695             }
21696             try{
21697                 this.execCmd('useCSS', true);
21698                 this.execCmd('styleWithCSS', false);
21699             }catch(e){}
21700         }
21701         this.owner.fireEvent('activate', this);
21702     },
21703
21704     // private
21705     adjustFont: function(btn){
21706         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21707         //if(Roo.isSafari){ // safari
21708         //    adjust *= 2;
21709        // }
21710         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21711         if(Roo.isSafari){ // safari
21712             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21713             v =  (v < 10) ? 10 : v;
21714             v =  (v > 48) ? 48 : v;
21715             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21716             
21717         }
21718         
21719         
21720         v = Math.max(1, v+adjust);
21721         
21722         this.execCmd('FontSize', v  );
21723     },
21724
21725     onEditorEvent : function(e)
21726     {
21727         this.owner.fireEvent('editorevent', this, e);
21728       //  this.updateToolbar();
21729         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21730     },
21731
21732     insertTag : function(tg)
21733     {
21734         // could be a bit smarter... -> wrap the current selected tRoo..
21735         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21736             
21737             range = this.createRange(this.getSelection());
21738             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21739             wrappingNode.appendChild(range.extractContents());
21740             range.insertNode(wrappingNode);
21741
21742             return;
21743             
21744             
21745             
21746         }
21747         this.execCmd("formatblock",   tg);
21748         
21749     },
21750     
21751     insertText : function(txt)
21752     {
21753         
21754         
21755         var range = this.createRange();
21756         range.deleteContents();
21757                //alert(Sender.getAttribute('label'));
21758                
21759         range.insertNode(this.doc.createTextNode(txt));
21760     } ,
21761     
21762      
21763
21764     /**
21765      * Executes a Midas editor command on the editor document and performs necessary focus and
21766      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21767      * @param {String} cmd The Midas command
21768      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21769      */
21770     relayCmd : function(cmd, value){
21771         this.win.focus();
21772         this.execCmd(cmd, value);
21773         this.owner.fireEvent('editorevent', this);
21774         //this.updateToolbar();
21775         this.owner.deferFocus();
21776     },
21777
21778     /**
21779      * Executes a Midas editor command directly on the editor document.
21780      * For visual commands, you should use {@link #relayCmd} instead.
21781      * <b>This should only be called after the editor is initialized.</b>
21782      * @param {String} cmd The Midas command
21783      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21784      */
21785     execCmd : function(cmd, value){
21786         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21787         this.syncValue();
21788     },
21789  
21790  
21791    
21792     /**
21793      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21794      * to insert tRoo.
21795      * @param {String} text | dom node.. 
21796      */
21797     insertAtCursor : function(text)
21798     {
21799         
21800         if(!this.activated){
21801             return;
21802         }
21803         /*
21804         if(Roo.isIE){
21805             this.win.focus();
21806             var r = this.doc.selection.createRange();
21807             if(r){
21808                 r.collapse(true);
21809                 r.pasteHTML(text);
21810                 this.syncValue();
21811                 this.deferFocus();
21812             
21813             }
21814             return;
21815         }
21816         */
21817         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21818             this.win.focus();
21819             
21820             
21821             // from jquery ui (MIT licenced)
21822             var range, node;
21823             var win = this.win;
21824             
21825             if (win.getSelection && win.getSelection().getRangeAt) {
21826                 range = win.getSelection().getRangeAt(0);
21827                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21828                 range.insertNode(node);
21829             } else if (win.document.selection && win.document.selection.createRange) {
21830                 // no firefox support
21831                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21832                 win.document.selection.createRange().pasteHTML(txt);
21833             } else {
21834                 // no firefox support
21835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21836                 this.execCmd('InsertHTML', txt);
21837             } 
21838             
21839             this.syncValue();
21840             
21841             this.deferFocus();
21842         }
21843     },
21844  // private
21845     mozKeyPress : function(e){
21846         if(e.ctrlKey){
21847             var c = e.getCharCode(), cmd;
21848           
21849             if(c > 0){
21850                 c = String.fromCharCode(c).toLowerCase();
21851                 switch(c){
21852                     case 'b':
21853                         cmd = 'bold';
21854                         break;
21855                     case 'i':
21856                         cmd = 'italic';
21857                         break;
21858                     
21859                     case 'u':
21860                         cmd = 'underline';
21861                         break;
21862                     
21863                     case 'v':
21864                         this.cleanUpPaste.defer(100, this);
21865                         return;
21866                         
21867                 }
21868                 if(cmd){
21869                     this.win.focus();
21870                     this.execCmd(cmd);
21871                     this.deferFocus();
21872                     e.preventDefault();
21873                 }
21874                 
21875             }
21876         }
21877     },
21878
21879     // private
21880     fixKeys : function(){ // load time branching for fastest keydown performance
21881         if(Roo.isIE){
21882             return function(e){
21883                 var k = e.getKey(), r;
21884                 if(k == e.TAB){
21885                     e.stopEvent();
21886                     r = this.doc.selection.createRange();
21887                     if(r){
21888                         r.collapse(true);
21889                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21890                         this.deferFocus();
21891                     }
21892                     return;
21893                 }
21894                 
21895                 if(k == e.ENTER){
21896                     r = this.doc.selection.createRange();
21897                     if(r){
21898                         var target = r.parentElement();
21899                         if(!target || target.tagName.toLowerCase() != 'li'){
21900                             e.stopEvent();
21901                             r.pasteHTML('<br />');
21902                             r.collapse(false);
21903                             r.select();
21904                         }
21905                     }
21906                 }
21907                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21908                     this.cleanUpPaste.defer(100, this);
21909                     return;
21910                 }
21911                 
21912                 
21913             };
21914         }else if(Roo.isOpera){
21915             return function(e){
21916                 var k = e.getKey();
21917                 if(k == e.TAB){
21918                     e.stopEvent();
21919                     this.win.focus();
21920                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21921                     this.deferFocus();
21922                 }
21923                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21924                     this.cleanUpPaste.defer(100, this);
21925                     return;
21926                 }
21927                 
21928             };
21929         }else if(Roo.isSafari){
21930             return function(e){
21931                 var k = e.getKey();
21932                 
21933                 if(k == e.TAB){
21934                     e.stopEvent();
21935                     this.execCmd('InsertText','\t');
21936                     this.deferFocus();
21937                     return;
21938                 }
21939                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21940                     this.cleanUpPaste.defer(100, this);
21941                     return;
21942                 }
21943                 
21944              };
21945         }
21946     }(),
21947     
21948     getAllAncestors: function()
21949     {
21950         var p = this.getSelectedNode();
21951         var a = [];
21952         if (!p) {
21953             a.push(p); // push blank onto stack..
21954             p = this.getParentElement();
21955         }
21956         
21957         
21958         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21959             a.push(p);
21960             p = p.parentNode;
21961         }
21962         a.push(this.doc.body);
21963         return a;
21964     },
21965     lastSel : false,
21966     lastSelNode : false,
21967     
21968     
21969     getSelection : function() 
21970     {
21971         this.assignDocWin();
21972         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21973     },
21974     
21975     getSelectedNode: function() 
21976     {
21977         // this may only work on Gecko!!!
21978         
21979         // should we cache this!!!!
21980         
21981         
21982         
21983          
21984         var range = this.createRange(this.getSelection()).cloneRange();
21985         
21986         if (Roo.isIE) {
21987             var parent = range.parentElement();
21988             while (true) {
21989                 var testRange = range.duplicate();
21990                 testRange.moveToElementText(parent);
21991                 if (testRange.inRange(range)) {
21992                     break;
21993                 }
21994                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21995                     break;
21996                 }
21997                 parent = parent.parentElement;
21998             }
21999             return parent;
22000         }
22001         
22002         // is ancestor a text element.
22003         var ac =  range.commonAncestorContainer;
22004         if (ac.nodeType == 3) {
22005             ac = ac.parentNode;
22006         }
22007         
22008         var ar = ac.childNodes;
22009          
22010         var nodes = [];
22011         var other_nodes = [];
22012         var has_other_nodes = false;
22013         for (var i=0;i<ar.length;i++) {
22014             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22015                 continue;
22016             }
22017             // fullly contained node.
22018             
22019             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22020                 nodes.push(ar[i]);
22021                 continue;
22022             }
22023             
22024             // probably selected..
22025             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22026                 other_nodes.push(ar[i]);
22027                 continue;
22028             }
22029             // outer..
22030             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22031                 continue;
22032             }
22033             
22034             
22035             has_other_nodes = true;
22036         }
22037         if (!nodes.length && other_nodes.length) {
22038             nodes= other_nodes;
22039         }
22040         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22041             return false;
22042         }
22043         
22044         return nodes[0];
22045     },
22046     createRange: function(sel)
22047     {
22048         // this has strange effects when using with 
22049         // top toolbar - not sure if it's a great idea.
22050         //this.editor.contentWindow.focus();
22051         if (typeof sel != "undefined") {
22052             try {
22053                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22054             } catch(e) {
22055                 return this.doc.createRange();
22056             }
22057         } else {
22058             return this.doc.createRange();
22059         }
22060     },
22061     getParentElement: function()
22062     {
22063         
22064         this.assignDocWin();
22065         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22066         
22067         var range = this.createRange(sel);
22068          
22069         try {
22070             var p = range.commonAncestorContainer;
22071             while (p.nodeType == 3) { // text node
22072                 p = p.parentNode;
22073             }
22074             return p;
22075         } catch (e) {
22076             return null;
22077         }
22078     
22079     },
22080     /***
22081      *
22082      * Range intersection.. the hard stuff...
22083      *  '-1' = before
22084      *  '0' = hits..
22085      *  '1' = after.
22086      *         [ -- selected range --- ]
22087      *   [fail]                        [fail]
22088      *
22089      *    basically..
22090      *      if end is before start or  hits it. fail.
22091      *      if start is after end or hits it fail.
22092      *
22093      *   if either hits (but other is outside. - then it's not 
22094      *   
22095      *    
22096      **/
22097     
22098     
22099     // @see http://www.thismuchiknow.co.uk/?p=64.
22100     rangeIntersectsNode : function(range, node)
22101     {
22102         var nodeRange = node.ownerDocument.createRange();
22103         try {
22104             nodeRange.selectNode(node);
22105         } catch (e) {
22106             nodeRange.selectNodeContents(node);
22107         }
22108     
22109         var rangeStartRange = range.cloneRange();
22110         rangeStartRange.collapse(true);
22111     
22112         var rangeEndRange = range.cloneRange();
22113         rangeEndRange.collapse(false);
22114     
22115         var nodeStartRange = nodeRange.cloneRange();
22116         nodeStartRange.collapse(true);
22117     
22118         var nodeEndRange = nodeRange.cloneRange();
22119         nodeEndRange.collapse(false);
22120     
22121         return rangeStartRange.compareBoundaryPoints(
22122                  Range.START_TO_START, nodeEndRange) == -1 &&
22123                rangeEndRange.compareBoundaryPoints(
22124                  Range.START_TO_START, nodeStartRange) == 1;
22125         
22126          
22127     },
22128     rangeCompareNode : function(range, node)
22129     {
22130         var nodeRange = node.ownerDocument.createRange();
22131         try {
22132             nodeRange.selectNode(node);
22133         } catch (e) {
22134             nodeRange.selectNodeContents(node);
22135         }
22136         
22137         
22138         range.collapse(true);
22139     
22140         nodeRange.collapse(true);
22141      
22142         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22143         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22144          
22145         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22146         
22147         var nodeIsBefore   =  ss == 1;
22148         var nodeIsAfter    = ee == -1;
22149         
22150         if (nodeIsBefore && nodeIsAfter) {
22151             return 0; // outer
22152         }
22153         if (!nodeIsBefore && nodeIsAfter) {
22154             return 1; //right trailed.
22155         }
22156         
22157         if (nodeIsBefore && !nodeIsAfter) {
22158             return 2;  // left trailed.
22159         }
22160         // fully contined.
22161         return 3;
22162     },
22163
22164     // private? - in a new class?
22165     cleanUpPaste :  function()
22166     {
22167         // cleans up the whole document..
22168         Roo.log('cleanuppaste');
22169         
22170         this.cleanUpChildren(this.doc.body);
22171         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22172         if (clean != this.doc.body.innerHTML) {
22173             this.doc.body.innerHTML = clean;
22174         }
22175         
22176     },
22177     
22178     cleanWordChars : function(input) {// change the chars to hex code
22179         var he = Roo.HtmlEditorCore;
22180         
22181         var output = input;
22182         Roo.each(he.swapCodes, function(sw) { 
22183             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22184             
22185             output = output.replace(swapper, sw[1]);
22186         });
22187         
22188         return output;
22189     },
22190     
22191     
22192     cleanUpChildren : function (n)
22193     {
22194         if (!n.childNodes.length) {
22195             return;
22196         }
22197         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22198            this.cleanUpChild(n.childNodes[i]);
22199         }
22200     },
22201     
22202     
22203         
22204     
22205     cleanUpChild : function (node)
22206     {
22207         var ed = this;
22208         //console.log(node);
22209         if (node.nodeName == "#text") {
22210             // clean up silly Windows -- stuff?
22211             return; 
22212         }
22213         if (node.nodeName == "#comment") {
22214             node.parentNode.removeChild(node);
22215             // clean up silly Windows -- stuff?
22216             return; 
22217         }
22218         var lcname = node.tagName.toLowerCase();
22219         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22220         // whitelist of tags..
22221         
22222         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22223             // remove node.
22224             node.parentNode.removeChild(node);
22225             return;
22226             
22227         }
22228         
22229         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22230         
22231         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22232         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22233         
22234         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22235         //    remove_keep_children = true;
22236         //}
22237         
22238         if (remove_keep_children) {
22239             this.cleanUpChildren(node);
22240             // inserts everything just before this node...
22241             while (node.childNodes.length) {
22242                 var cn = node.childNodes[0];
22243                 node.removeChild(cn);
22244                 node.parentNode.insertBefore(cn, node);
22245             }
22246             node.parentNode.removeChild(node);
22247             return;
22248         }
22249         
22250         if (!node.attributes || !node.attributes.length) {
22251             this.cleanUpChildren(node);
22252             return;
22253         }
22254         
22255         function cleanAttr(n,v)
22256         {
22257             
22258             if (v.match(/^\./) || v.match(/^\//)) {
22259                 return;
22260             }
22261             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22262                 return;
22263             }
22264             if (v.match(/^#/)) {
22265                 return;
22266             }
22267 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22268             node.removeAttribute(n);
22269             
22270         }
22271         
22272         var cwhite = this.cwhite;
22273         var cblack = this.cblack;
22274             
22275         function cleanStyle(n,v)
22276         {
22277             if (v.match(/expression/)) { //XSS?? should we even bother..
22278                 node.removeAttribute(n);
22279                 return;
22280             }
22281             
22282             var parts = v.split(/;/);
22283             var clean = [];
22284             
22285             Roo.each(parts, function(p) {
22286                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22287                 if (!p.length) {
22288                     return true;
22289                 }
22290                 var l = p.split(':').shift().replace(/\s+/g,'');
22291                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22292                 
22293                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22294 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22295                     //node.removeAttribute(n);
22296                     return true;
22297                 }
22298                 //Roo.log()
22299                 // only allow 'c whitelisted system attributes'
22300                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22301 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22302                     //node.removeAttribute(n);
22303                     return true;
22304                 }
22305                 
22306                 
22307                  
22308                 
22309                 clean.push(p);
22310                 return true;
22311             });
22312             if (clean.length) { 
22313                 node.setAttribute(n, clean.join(';'));
22314             } else {
22315                 node.removeAttribute(n);
22316             }
22317             
22318         }
22319         
22320         
22321         for (var i = node.attributes.length-1; i > -1 ; i--) {
22322             var a = node.attributes[i];
22323             //console.log(a);
22324             
22325             if (a.name.toLowerCase().substr(0,2)=='on')  {
22326                 node.removeAttribute(a.name);
22327                 continue;
22328             }
22329             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22330                 node.removeAttribute(a.name);
22331                 continue;
22332             }
22333             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22334                 cleanAttr(a.name,a.value); // fixme..
22335                 continue;
22336             }
22337             if (a.name == 'style') {
22338                 cleanStyle(a.name,a.value);
22339                 continue;
22340             }
22341             /// clean up MS crap..
22342             // tecnically this should be a list of valid class'es..
22343             
22344             
22345             if (a.name == 'class') {
22346                 if (a.value.match(/^Mso/)) {
22347                     node.className = '';
22348                 }
22349                 
22350                 if (a.value.match(/^body$/)) {
22351                     node.className = '';
22352                 }
22353                 continue;
22354             }
22355             
22356             // style cleanup!?
22357             // class cleanup?
22358             
22359         }
22360         
22361         
22362         this.cleanUpChildren(node);
22363         
22364         
22365     },
22366     
22367     /**
22368      * Clean up MS wordisms...
22369      */
22370     cleanWord : function(node)
22371     {
22372         
22373         
22374         if (!node) {
22375             this.cleanWord(this.doc.body);
22376             return;
22377         }
22378         if (node.nodeName == "#text") {
22379             // clean up silly Windows -- stuff?
22380             return; 
22381         }
22382         if (node.nodeName == "#comment") {
22383             node.parentNode.removeChild(node);
22384             // clean up silly Windows -- stuff?
22385             return; 
22386         }
22387         
22388         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22389             node.parentNode.removeChild(node);
22390             return;
22391         }
22392         
22393         // remove - but keep children..
22394         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22395             while (node.childNodes.length) {
22396                 var cn = node.childNodes[0];
22397                 node.removeChild(cn);
22398                 node.parentNode.insertBefore(cn, node);
22399             }
22400             node.parentNode.removeChild(node);
22401             this.iterateChildren(node, this.cleanWord);
22402             return;
22403         }
22404         // clean styles
22405         if (node.className.length) {
22406             
22407             var cn = node.className.split(/\W+/);
22408             var cna = [];
22409             Roo.each(cn, function(cls) {
22410                 if (cls.match(/Mso[a-zA-Z]+/)) {
22411                     return;
22412                 }
22413                 cna.push(cls);
22414             });
22415             node.className = cna.length ? cna.join(' ') : '';
22416             if (!cna.length) {
22417                 node.removeAttribute("class");
22418             }
22419         }
22420         
22421         if (node.hasAttribute("lang")) {
22422             node.removeAttribute("lang");
22423         }
22424         
22425         if (node.hasAttribute("style")) {
22426             
22427             var styles = node.getAttribute("style").split(";");
22428             var nstyle = [];
22429             Roo.each(styles, function(s) {
22430                 if (!s.match(/:/)) {
22431                     return;
22432                 }
22433                 var kv = s.split(":");
22434                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22435                     return;
22436                 }
22437                 // what ever is left... we allow.
22438                 nstyle.push(s);
22439             });
22440             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22441             if (!nstyle.length) {
22442                 node.removeAttribute('style');
22443             }
22444         }
22445         this.iterateChildren(node, this.cleanWord);
22446         
22447         
22448         
22449     },
22450     /**
22451      * iterateChildren of a Node, calling fn each time, using this as the scole..
22452      * @param {DomNode} node node to iterate children of.
22453      * @param {Function} fn method of this class to call on each item.
22454      */
22455     iterateChildren : function(node, fn)
22456     {
22457         if (!node.childNodes.length) {
22458                 return;
22459         }
22460         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22461            fn.call(this, node.childNodes[i])
22462         }
22463     },
22464     
22465     
22466     /**
22467      * cleanTableWidths.
22468      *
22469      * Quite often pasting from word etc.. results in tables with column and widths.
22470      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22471      *
22472      */
22473     cleanTableWidths : function(node)
22474     {
22475          
22476          
22477         if (!node) {
22478             this.cleanTableWidths(this.doc.body);
22479             return;
22480         }
22481         
22482         // ignore list...
22483         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22484             return; 
22485         }
22486         Roo.log(node.tagName);
22487         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22488             this.iterateChildren(node, this.cleanTableWidths);
22489             return;
22490         }
22491         if (node.hasAttribute('width')) {
22492             node.removeAttribute('width');
22493         }
22494         
22495          
22496         if (node.hasAttribute("style")) {
22497             // pretty basic...
22498             
22499             var styles = node.getAttribute("style").split(";");
22500             var nstyle = [];
22501             Roo.each(styles, function(s) {
22502                 if (!s.match(/:/)) {
22503                     return;
22504                 }
22505                 var kv = s.split(":");
22506                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22507                     return;
22508                 }
22509                 // what ever is left... we allow.
22510                 nstyle.push(s);
22511             });
22512             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22513             if (!nstyle.length) {
22514                 node.removeAttribute('style');
22515             }
22516         }
22517         
22518         this.iterateChildren(node, this.cleanTableWidths);
22519         
22520         
22521     },
22522     
22523     
22524     
22525     
22526     domToHTML : function(currentElement, depth, nopadtext) {
22527         
22528         depth = depth || 0;
22529         nopadtext = nopadtext || false;
22530     
22531         if (!currentElement) {
22532             return this.domToHTML(this.doc.body);
22533         }
22534         
22535         //Roo.log(currentElement);
22536         var j;
22537         var allText = false;
22538         var nodeName = currentElement.nodeName;
22539         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22540         
22541         if  (nodeName == '#text') {
22542             
22543             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22544         }
22545         
22546         
22547         var ret = '';
22548         if (nodeName != 'BODY') {
22549              
22550             var i = 0;
22551             // Prints the node tagName, such as <A>, <IMG>, etc
22552             if (tagName) {
22553                 var attr = [];
22554                 for(i = 0; i < currentElement.attributes.length;i++) {
22555                     // quoting?
22556                     var aname = currentElement.attributes.item(i).name;
22557                     if (!currentElement.attributes.item(i).value.length) {
22558                         continue;
22559                     }
22560                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22561                 }
22562                 
22563                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22564             } 
22565             else {
22566                 
22567                 // eack
22568             }
22569         } else {
22570             tagName = false;
22571         }
22572         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22573             return ret;
22574         }
22575         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22576             nopadtext = true;
22577         }
22578         
22579         
22580         // Traverse the tree
22581         i = 0;
22582         var currentElementChild = currentElement.childNodes.item(i);
22583         var allText = true;
22584         var innerHTML  = '';
22585         lastnode = '';
22586         while (currentElementChild) {
22587             // Formatting code (indent the tree so it looks nice on the screen)
22588             var nopad = nopadtext;
22589             if (lastnode == 'SPAN') {
22590                 nopad  = true;
22591             }
22592             // text
22593             if  (currentElementChild.nodeName == '#text') {
22594                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22595                 toadd = nopadtext ? toadd : toadd.trim();
22596                 if (!nopad && toadd.length > 80) {
22597                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22598                 }
22599                 innerHTML  += toadd;
22600                 
22601                 i++;
22602                 currentElementChild = currentElement.childNodes.item(i);
22603                 lastNode = '';
22604                 continue;
22605             }
22606             allText = false;
22607             
22608             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22609                 
22610             // Recursively traverse the tree structure of the child node
22611             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22612             lastnode = currentElementChild.nodeName;
22613             i++;
22614             currentElementChild=currentElement.childNodes.item(i);
22615         }
22616         
22617         ret += innerHTML;
22618         
22619         if (!allText) {
22620                 // The remaining code is mostly for formatting the tree
22621             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22622         }
22623         
22624         
22625         if (tagName) {
22626             ret+= "</"+tagName+">";
22627         }
22628         return ret;
22629         
22630     },
22631         
22632     applyBlacklists : function()
22633     {
22634         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22635         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22636         
22637         this.white = [];
22638         this.black = [];
22639         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22640             if (b.indexOf(tag) > -1) {
22641                 return;
22642             }
22643             this.white.push(tag);
22644             
22645         }, this);
22646         
22647         Roo.each(w, function(tag) {
22648             if (b.indexOf(tag) > -1) {
22649                 return;
22650             }
22651             if (this.white.indexOf(tag) > -1) {
22652                 return;
22653             }
22654             this.white.push(tag);
22655             
22656         }, this);
22657         
22658         
22659         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22660             if (w.indexOf(tag) > -1) {
22661                 return;
22662             }
22663             this.black.push(tag);
22664             
22665         }, this);
22666         
22667         Roo.each(b, function(tag) {
22668             if (w.indexOf(tag) > -1) {
22669                 return;
22670             }
22671             if (this.black.indexOf(tag) > -1) {
22672                 return;
22673             }
22674             this.black.push(tag);
22675             
22676         }, this);
22677         
22678         
22679         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22680         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22681         
22682         this.cwhite = [];
22683         this.cblack = [];
22684         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22685             if (b.indexOf(tag) > -1) {
22686                 return;
22687             }
22688             this.cwhite.push(tag);
22689             
22690         }, this);
22691         
22692         Roo.each(w, function(tag) {
22693             if (b.indexOf(tag) > -1) {
22694                 return;
22695             }
22696             if (this.cwhite.indexOf(tag) > -1) {
22697                 return;
22698             }
22699             this.cwhite.push(tag);
22700             
22701         }, this);
22702         
22703         
22704         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22705             if (w.indexOf(tag) > -1) {
22706                 return;
22707             }
22708             this.cblack.push(tag);
22709             
22710         }, this);
22711         
22712         Roo.each(b, function(tag) {
22713             if (w.indexOf(tag) > -1) {
22714                 return;
22715             }
22716             if (this.cblack.indexOf(tag) > -1) {
22717                 return;
22718             }
22719             this.cblack.push(tag);
22720             
22721         }, this);
22722     },
22723     
22724     setStylesheets : function(stylesheets)
22725     {
22726         if(typeof(stylesheets) == 'string'){
22727             Roo.get(this.iframe.contentDocument.head).createChild({
22728                 tag : 'link',
22729                 rel : 'stylesheet',
22730                 type : 'text/css',
22731                 href : stylesheets
22732             });
22733             
22734             return;
22735         }
22736         var _this = this;
22737      
22738         Roo.each(stylesheets, function(s) {
22739             if(!s.length){
22740                 return;
22741             }
22742             
22743             Roo.get(_this.iframe.contentDocument.head).createChild({
22744                 tag : 'link',
22745                 rel : 'stylesheet',
22746                 type : 'text/css',
22747                 href : s
22748             });
22749         });
22750
22751         
22752     },
22753     
22754     removeStylesheets : function()
22755     {
22756         var _this = this;
22757         
22758         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22759             s.remove();
22760         });
22761     },
22762     
22763     setStyle : function(style)
22764     {
22765         Roo.get(this.iframe.contentDocument.head).createChild({
22766             tag : 'style',
22767             type : 'text/css',
22768             html : style
22769         });
22770
22771         return;
22772     }
22773     
22774     // hide stuff that is not compatible
22775     /**
22776      * @event blur
22777      * @hide
22778      */
22779     /**
22780      * @event change
22781      * @hide
22782      */
22783     /**
22784      * @event focus
22785      * @hide
22786      */
22787     /**
22788      * @event specialkey
22789      * @hide
22790      */
22791     /**
22792      * @cfg {String} fieldClass @hide
22793      */
22794     /**
22795      * @cfg {String} focusClass @hide
22796      */
22797     /**
22798      * @cfg {String} autoCreate @hide
22799      */
22800     /**
22801      * @cfg {String} inputType @hide
22802      */
22803     /**
22804      * @cfg {String} invalidClass @hide
22805      */
22806     /**
22807      * @cfg {String} invalidText @hide
22808      */
22809     /**
22810      * @cfg {String} msgFx @hide
22811      */
22812     /**
22813      * @cfg {String} validateOnBlur @hide
22814      */
22815 });
22816
22817 Roo.HtmlEditorCore.white = [
22818         'area', 'br', 'img', 'input', 'hr', 'wbr',
22819         
22820        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22821        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22822        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22823        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22824        'table',   'ul',         'xmp', 
22825        
22826        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22827       'thead',   'tr', 
22828      
22829       'dir', 'menu', 'ol', 'ul', 'dl',
22830        
22831       'embed',  'object'
22832 ];
22833
22834
22835 Roo.HtmlEditorCore.black = [
22836     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22837         'applet', // 
22838         'base',   'basefont', 'bgsound', 'blink',  'body', 
22839         'frame',  'frameset', 'head',    'html',   'ilayer', 
22840         'iframe', 'layer',  'link',     'meta',    'object',   
22841         'script', 'style' ,'title',  'xml' // clean later..
22842 ];
22843 Roo.HtmlEditorCore.clean = [
22844     'script', 'style', 'title', 'xml'
22845 ];
22846 Roo.HtmlEditorCore.remove = [
22847     'font'
22848 ];
22849 // attributes..
22850
22851 Roo.HtmlEditorCore.ablack = [
22852     'on'
22853 ];
22854     
22855 Roo.HtmlEditorCore.aclean = [ 
22856     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22857 ];
22858
22859 // protocols..
22860 Roo.HtmlEditorCore.pwhite= [
22861         'http',  'https',  'mailto'
22862 ];
22863
22864 // white listed style attributes.
22865 Roo.HtmlEditorCore.cwhite= [
22866       //  'text-align', /// default is to allow most things..
22867       
22868          
22869 //        'font-size'//??
22870 ];
22871
22872 // black listed style attributes.
22873 Roo.HtmlEditorCore.cblack= [
22874       //  'font-size' -- this can be set by the project 
22875 ];
22876
22877
22878 Roo.HtmlEditorCore.swapCodes   =[ 
22879     [    8211, "--" ], 
22880     [    8212, "--" ], 
22881     [    8216,  "'" ],  
22882     [    8217, "'" ],  
22883     [    8220, '"' ],  
22884     [    8221, '"' ],  
22885     [    8226, "*" ],  
22886     [    8230, "..." ]
22887 ]; 
22888
22889     /*
22890  * - LGPL
22891  *
22892  * HtmlEditor
22893  * 
22894  */
22895
22896 /**
22897  * @class Roo.bootstrap.HtmlEditor
22898  * @extends Roo.bootstrap.TextArea
22899  * Bootstrap HtmlEditor class
22900
22901  * @constructor
22902  * Create a new HtmlEditor
22903  * @param {Object} config The config object
22904  */
22905
22906 Roo.bootstrap.HtmlEditor = function(config){
22907     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22908     if (!this.toolbars) {
22909         this.toolbars = [];
22910     }
22911     
22912     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22913     this.addEvents({
22914             /**
22915              * @event initialize
22916              * Fires when the editor is fully initialized (including the iframe)
22917              * @param {HtmlEditor} this
22918              */
22919             initialize: true,
22920             /**
22921              * @event activate
22922              * Fires when the editor is first receives the focus. Any insertion must wait
22923              * until after this event.
22924              * @param {HtmlEditor} this
22925              */
22926             activate: true,
22927              /**
22928              * @event beforesync
22929              * Fires before the textarea is updated with content from the editor iframe. Return false
22930              * to cancel the sync.
22931              * @param {HtmlEditor} this
22932              * @param {String} html
22933              */
22934             beforesync: true,
22935              /**
22936              * @event beforepush
22937              * Fires before the iframe editor is updated with content from the textarea. Return false
22938              * to cancel the push.
22939              * @param {HtmlEditor} this
22940              * @param {String} html
22941              */
22942             beforepush: true,
22943              /**
22944              * @event sync
22945              * Fires when the textarea is updated with content from the editor iframe.
22946              * @param {HtmlEditor} this
22947              * @param {String} html
22948              */
22949             sync: true,
22950              /**
22951              * @event push
22952              * Fires when the iframe editor is updated with content from the textarea.
22953              * @param {HtmlEditor} this
22954              * @param {String} html
22955              */
22956             push: true,
22957              /**
22958              * @event editmodechange
22959              * Fires when the editor switches edit modes
22960              * @param {HtmlEditor} this
22961              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22962              */
22963             editmodechange: true,
22964             /**
22965              * @event editorevent
22966              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22967              * @param {HtmlEditor} this
22968              */
22969             editorevent: true,
22970             /**
22971              * @event firstfocus
22972              * Fires when on first focus - needed by toolbars..
22973              * @param {HtmlEditor} this
22974              */
22975             firstfocus: true,
22976             /**
22977              * @event autosave
22978              * Auto save the htmlEditor value as a file into Events
22979              * @param {HtmlEditor} this
22980              */
22981             autosave: true,
22982             /**
22983              * @event savedpreview
22984              * preview the saved version of htmlEditor
22985              * @param {HtmlEditor} this
22986              */
22987             savedpreview: true
22988         });
22989 };
22990
22991
22992 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22993     
22994     
22995       /**
22996      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22997      */
22998     toolbars : false,
22999     
23000      /**
23001     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23002     */
23003     btns : [],
23004    
23005      /**
23006      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23007      *                        Roo.resizable.
23008      */
23009     resizable : false,
23010      /**
23011      * @cfg {Number} height (in pixels)
23012      */   
23013     height: 300,
23014    /**
23015      * @cfg {Number} width (in pixels)
23016      */   
23017     width: false,
23018     
23019     /**
23020      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23021      * 
23022      */
23023     stylesheets: false,
23024     
23025     // id of frame..
23026     frameId: false,
23027     
23028     // private properties
23029     validationEvent : false,
23030     deferHeight: true,
23031     initialized : false,
23032     activated : false,
23033     
23034     onFocus : Roo.emptyFn,
23035     iframePad:3,
23036     hideMode:'offsets',
23037     
23038     tbContainer : false,
23039     
23040     bodyCls : '',
23041     
23042     toolbarContainer :function() {
23043         return this.wrap.select('.x-html-editor-tb',true).first();
23044     },
23045
23046     /**
23047      * Protected method that will not generally be called directly. It
23048      * is called when the editor creates its toolbar. Override this method if you need to
23049      * add custom toolbar buttons.
23050      * @param {HtmlEditor} editor
23051      */
23052     createToolbar : function(){
23053         Roo.log('renewing');
23054         Roo.log("create toolbars");
23055         
23056         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23057         this.toolbars[0].render(this.toolbarContainer());
23058         
23059         return;
23060         
23061 //        if (!editor.toolbars || !editor.toolbars.length) {
23062 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23063 //        }
23064 //        
23065 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23066 //            editor.toolbars[i] = Roo.factory(
23067 //                    typeof(editor.toolbars[i]) == 'string' ?
23068 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23069 //                Roo.bootstrap.HtmlEditor);
23070 //            editor.toolbars[i].init(editor);
23071 //        }
23072     },
23073
23074      
23075     // private
23076     onRender : function(ct, position)
23077     {
23078        // Roo.log("Call onRender: " + this.xtype);
23079         var _t = this;
23080         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23081       
23082         this.wrap = this.inputEl().wrap({
23083             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23084         });
23085         
23086         this.editorcore.onRender(ct, position);
23087          
23088         if (this.resizable) {
23089             this.resizeEl = new Roo.Resizable(this.wrap, {
23090                 pinned : true,
23091                 wrap: true,
23092                 dynamic : true,
23093                 minHeight : this.height,
23094                 height: this.height,
23095                 handles : this.resizable,
23096                 width: this.width,
23097                 listeners : {
23098                     resize : function(r, w, h) {
23099                         _t.onResize(w,h); // -something
23100                     }
23101                 }
23102             });
23103             
23104         }
23105         this.createToolbar(this);
23106        
23107         
23108         if(!this.width && this.resizable){
23109             this.setSize(this.wrap.getSize());
23110         }
23111         if (this.resizeEl) {
23112             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23113             // should trigger onReize..
23114         }
23115         
23116     },
23117
23118     // private
23119     onResize : function(w, h)
23120     {
23121         Roo.log('resize: ' +w + ',' + h );
23122         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23123         var ew = false;
23124         var eh = false;
23125         
23126         if(this.inputEl() ){
23127             if(typeof w == 'number'){
23128                 var aw = w - this.wrap.getFrameWidth('lr');
23129                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23130                 ew = aw;
23131             }
23132             if(typeof h == 'number'){
23133                  var tbh = -11;  // fixme it needs to tool bar size!
23134                 for (var i =0; i < this.toolbars.length;i++) {
23135                     // fixme - ask toolbars for heights?
23136                     tbh += this.toolbars[i].el.getHeight();
23137                     //if (this.toolbars[i].footer) {
23138                     //    tbh += this.toolbars[i].footer.el.getHeight();
23139                     //}
23140                 }
23141               
23142                 
23143                 
23144                 
23145                 
23146                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23147                 ah -= 5; // knock a few pixes off for look..
23148                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23149                 var eh = ah;
23150             }
23151         }
23152         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23153         this.editorcore.onResize(ew,eh);
23154         
23155     },
23156
23157     /**
23158      * Toggles the editor between standard and source edit mode.
23159      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23160      */
23161     toggleSourceEdit : function(sourceEditMode)
23162     {
23163         this.editorcore.toggleSourceEdit(sourceEditMode);
23164         
23165         if(this.editorcore.sourceEditMode){
23166             Roo.log('editor - showing textarea');
23167             
23168 //            Roo.log('in');
23169 //            Roo.log(this.syncValue());
23170             this.syncValue();
23171             this.inputEl().removeClass(['hide', 'x-hidden']);
23172             this.inputEl().dom.removeAttribute('tabIndex');
23173             this.inputEl().focus();
23174         }else{
23175             Roo.log('editor - hiding textarea');
23176 //            Roo.log('out')
23177 //            Roo.log(this.pushValue()); 
23178             this.pushValue();
23179             
23180             this.inputEl().addClass(['hide', 'x-hidden']);
23181             this.inputEl().dom.setAttribute('tabIndex', -1);
23182             //this.deferFocus();
23183         }
23184          
23185         if(this.resizable){
23186             this.setSize(this.wrap.getSize());
23187         }
23188         
23189         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23190     },
23191  
23192     // private (for BoxComponent)
23193     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23194
23195     // private (for BoxComponent)
23196     getResizeEl : function(){
23197         return this.wrap;
23198     },
23199
23200     // private (for BoxComponent)
23201     getPositionEl : function(){
23202         return this.wrap;
23203     },
23204
23205     // private
23206     initEvents : function(){
23207         this.originalValue = this.getValue();
23208     },
23209
23210 //    /**
23211 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23212 //     * @method
23213 //     */
23214 //    markInvalid : Roo.emptyFn,
23215 //    /**
23216 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23217 //     * @method
23218 //     */
23219 //    clearInvalid : Roo.emptyFn,
23220
23221     setValue : function(v){
23222         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23223         this.editorcore.pushValue();
23224     },
23225
23226      
23227     // private
23228     deferFocus : function(){
23229         this.focus.defer(10, this);
23230     },
23231
23232     // doc'ed in Field
23233     focus : function(){
23234         this.editorcore.focus();
23235         
23236     },
23237       
23238
23239     // private
23240     onDestroy : function(){
23241         
23242         
23243         
23244         if(this.rendered){
23245             
23246             for (var i =0; i < this.toolbars.length;i++) {
23247                 // fixme - ask toolbars for heights?
23248                 this.toolbars[i].onDestroy();
23249             }
23250             
23251             this.wrap.dom.innerHTML = '';
23252             this.wrap.remove();
23253         }
23254     },
23255
23256     // private
23257     onFirstFocus : function(){
23258         //Roo.log("onFirstFocus");
23259         this.editorcore.onFirstFocus();
23260          for (var i =0; i < this.toolbars.length;i++) {
23261             this.toolbars[i].onFirstFocus();
23262         }
23263         
23264     },
23265     
23266     // private
23267     syncValue : function()
23268     {   
23269         this.editorcore.syncValue();
23270     },
23271     
23272     pushValue : function()
23273     {   
23274         this.editorcore.pushValue();
23275     }
23276      
23277     
23278     // hide stuff that is not compatible
23279     /**
23280      * @event blur
23281      * @hide
23282      */
23283     /**
23284      * @event change
23285      * @hide
23286      */
23287     /**
23288      * @event focus
23289      * @hide
23290      */
23291     /**
23292      * @event specialkey
23293      * @hide
23294      */
23295     /**
23296      * @cfg {String} fieldClass @hide
23297      */
23298     /**
23299      * @cfg {String} focusClass @hide
23300      */
23301     /**
23302      * @cfg {String} autoCreate @hide
23303      */
23304     /**
23305      * @cfg {String} inputType @hide
23306      */
23307     /**
23308      * @cfg {String} invalidClass @hide
23309      */
23310     /**
23311      * @cfg {String} invalidText @hide
23312      */
23313     /**
23314      * @cfg {String} msgFx @hide
23315      */
23316     /**
23317      * @cfg {String} validateOnBlur @hide
23318      */
23319 });
23320  
23321     
23322    
23323    
23324    
23325       
23326 Roo.namespace('Roo.bootstrap.htmleditor');
23327 /**
23328  * @class Roo.bootstrap.HtmlEditorToolbar1
23329  * Basic Toolbar
23330  * 
23331  * Usage:
23332  *
23333  new Roo.bootstrap.HtmlEditor({
23334     ....
23335     toolbars : [
23336         new Roo.bootstrap.HtmlEditorToolbar1({
23337             disable : { fonts: 1 , format: 1, ..., ... , ...],
23338             btns : [ .... ]
23339         })
23340     }
23341      
23342  * 
23343  * @cfg {Object} disable List of elements to disable..
23344  * @cfg {Array} btns List of additional buttons.
23345  * 
23346  * 
23347  * NEEDS Extra CSS? 
23348  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23349  */
23350  
23351 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23352 {
23353     
23354     Roo.apply(this, config);
23355     
23356     // default disabled, based on 'good practice'..
23357     this.disable = this.disable || {};
23358     Roo.applyIf(this.disable, {
23359         fontSize : true,
23360         colors : true,
23361         specialElements : true
23362     });
23363     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23364     
23365     this.editor = config.editor;
23366     this.editorcore = config.editor.editorcore;
23367     
23368     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23369     
23370     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23371     // dont call parent... till later.
23372 }
23373 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23374      
23375     bar : true,
23376     
23377     editor : false,
23378     editorcore : false,
23379     
23380     
23381     formats : [
23382         "p" ,  
23383         "h1","h2","h3","h4","h5","h6", 
23384         "pre", "code", 
23385         "abbr", "acronym", "address", "cite", "samp", "var",
23386         'div','span'
23387     ],
23388     
23389     onRender : function(ct, position)
23390     {
23391        // Roo.log("Call onRender: " + this.xtype);
23392         
23393        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23394        Roo.log(this.el);
23395        this.el.dom.style.marginBottom = '0';
23396        var _this = this;
23397        var editorcore = this.editorcore;
23398        var editor= this.editor;
23399        
23400        var children = [];
23401        var btn = function(id,cmd , toggle, handler, html){
23402        
23403             var  event = toggle ? 'toggle' : 'click';
23404        
23405             var a = {
23406                 size : 'sm',
23407                 xtype: 'Button',
23408                 xns: Roo.bootstrap,
23409                 glyphicon : id,
23410                 cmd : id || cmd,
23411                 enableToggle:toggle !== false,
23412                 html : html || '',
23413                 pressed : toggle ? false : null,
23414                 listeners : {}
23415             };
23416             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23417                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23418             };
23419             children.push(a);
23420             return a;
23421        }
23422        
23423     //    var cb_box = function...
23424         
23425         var style = {
23426                 xtype: 'Button',
23427                 size : 'sm',
23428                 xns: Roo.bootstrap,
23429                 glyphicon : 'font',
23430                 //html : 'submit'
23431                 menu : {
23432                     xtype: 'Menu',
23433                     xns: Roo.bootstrap,
23434                     items:  []
23435                 }
23436         };
23437         Roo.each(this.formats, function(f) {
23438             style.menu.items.push({
23439                 xtype :'MenuItem',
23440                 xns: Roo.bootstrap,
23441                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23442                 tagname : f,
23443                 listeners : {
23444                     click : function()
23445                     {
23446                         editorcore.insertTag(this.tagname);
23447                         editor.focus();
23448                     }
23449                 }
23450                 
23451             });
23452         });
23453         children.push(style);   
23454         
23455         btn('bold',false,true);
23456         btn('italic',false,true);
23457         btn('align-left', 'justifyleft',true);
23458         btn('align-center', 'justifycenter',true);
23459         btn('align-right' , 'justifyright',true);
23460         btn('link', false, false, function(btn) {
23461             //Roo.log("create link?");
23462             var url = prompt(this.createLinkText, this.defaultLinkValue);
23463             if(url && url != 'http:/'+'/'){
23464                 this.editorcore.relayCmd('createlink', url);
23465             }
23466         }),
23467         btn('list','insertunorderedlist',true);
23468         btn('pencil', false,true, function(btn){
23469                 Roo.log(this);
23470                 this.toggleSourceEdit(btn.pressed);
23471         });
23472         
23473         if (this.editor.btns.length > 0) {
23474             for (var i = 0; i<this.editor.btns.length; i++) {
23475                 children.push(this.editor.btns[i]);
23476             }
23477         }
23478         
23479         /*
23480         var cog = {
23481                 xtype: 'Button',
23482                 size : 'sm',
23483                 xns: Roo.bootstrap,
23484                 glyphicon : 'cog',
23485                 //html : 'submit'
23486                 menu : {
23487                     xtype: 'Menu',
23488                     xns: Roo.bootstrap,
23489                     items:  []
23490                 }
23491         };
23492         
23493         cog.menu.items.push({
23494             xtype :'MenuItem',
23495             xns: Roo.bootstrap,
23496             html : Clean styles,
23497             tagname : f,
23498             listeners : {
23499                 click : function()
23500                 {
23501                     editorcore.insertTag(this.tagname);
23502                     editor.focus();
23503                 }
23504             }
23505             
23506         });
23507        */
23508         
23509          
23510        this.xtype = 'NavSimplebar';
23511         
23512         for(var i=0;i< children.length;i++) {
23513             
23514             this.buttons.add(this.addxtypeChild(children[i]));
23515             
23516         }
23517         
23518         editor.on('editorevent', this.updateToolbar, this);
23519     },
23520     onBtnClick : function(id)
23521     {
23522        this.editorcore.relayCmd(id);
23523        this.editorcore.focus();
23524     },
23525     
23526     /**
23527      * Protected method that will not generally be called directly. It triggers
23528      * a toolbar update by reading the markup state of the current selection in the editor.
23529      */
23530     updateToolbar: function(){
23531
23532         if(!this.editorcore.activated){
23533             this.editor.onFirstFocus(); // is this neeed?
23534             return;
23535         }
23536
23537         var btns = this.buttons; 
23538         var doc = this.editorcore.doc;
23539         btns.get('bold').setActive(doc.queryCommandState('bold'));
23540         btns.get('italic').setActive(doc.queryCommandState('italic'));
23541         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23542         
23543         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23544         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23545         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23546         
23547         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23548         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23549          /*
23550         
23551         var ans = this.editorcore.getAllAncestors();
23552         if (this.formatCombo) {
23553             
23554             
23555             var store = this.formatCombo.store;
23556             this.formatCombo.setValue("");
23557             for (var i =0; i < ans.length;i++) {
23558                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23559                     // select it..
23560                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23561                     break;
23562                 }
23563             }
23564         }
23565         
23566         
23567         
23568         // hides menus... - so this cant be on a menu...
23569         Roo.bootstrap.MenuMgr.hideAll();
23570         */
23571         Roo.bootstrap.MenuMgr.hideAll();
23572         //this.editorsyncValue();
23573     },
23574     onFirstFocus: function() {
23575         this.buttons.each(function(item){
23576            item.enable();
23577         });
23578     },
23579     toggleSourceEdit : function(sourceEditMode){
23580         
23581           
23582         if(sourceEditMode){
23583             Roo.log("disabling buttons");
23584            this.buttons.each( function(item){
23585                 if(item.cmd != 'pencil'){
23586                     item.disable();
23587                 }
23588             });
23589           
23590         }else{
23591             Roo.log("enabling buttons");
23592             if(this.editorcore.initialized){
23593                 this.buttons.each( function(item){
23594                     item.enable();
23595                 });
23596             }
23597             
23598         }
23599         Roo.log("calling toggole on editor");
23600         // tell the editor that it's been pressed..
23601         this.editor.toggleSourceEdit(sourceEditMode);
23602        
23603     }
23604 });
23605
23606
23607
23608
23609
23610 /**
23611  * @class Roo.bootstrap.Table.AbstractSelectionModel
23612  * @extends Roo.util.Observable
23613  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23614  * implemented by descendant classes.  This class should not be directly instantiated.
23615  * @constructor
23616  */
23617 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23618     this.locked = false;
23619     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23620 };
23621
23622
23623 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23624     /** @ignore Called by the grid automatically. Do not call directly. */
23625     init : function(grid){
23626         this.grid = grid;
23627         this.initEvents();
23628     },
23629
23630     /**
23631      * Locks the selections.
23632      */
23633     lock : function(){
23634         this.locked = true;
23635     },
23636
23637     /**
23638      * Unlocks the selections.
23639      */
23640     unlock : function(){
23641         this.locked = false;
23642     },
23643
23644     /**
23645      * Returns true if the selections are locked.
23646      * @return {Boolean}
23647      */
23648     isLocked : function(){
23649         return this.locked;
23650     }
23651 });
23652 /**
23653  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23654  * @class Roo.bootstrap.Table.RowSelectionModel
23655  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23656  * It supports multiple selections and keyboard selection/navigation. 
23657  * @constructor
23658  * @param {Object} config
23659  */
23660
23661 Roo.bootstrap.Table.RowSelectionModel = function(config){
23662     Roo.apply(this, config);
23663     this.selections = new Roo.util.MixedCollection(false, function(o){
23664         return o.id;
23665     });
23666
23667     this.last = false;
23668     this.lastActive = false;
23669
23670     this.addEvents({
23671         /**
23672              * @event selectionchange
23673              * Fires when the selection changes
23674              * @param {SelectionModel} this
23675              */
23676             "selectionchange" : true,
23677         /**
23678              * @event afterselectionchange
23679              * Fires after the selection changes (eg. by key press or clicking)
23680              * @param {SelectionModel} this
23681              */
23682             "afterselectionchange" : true,
23683         /**
23684              * @event beforerowselect
23685              * Fires when a row is selected being selected, return false to cancel.
23686              * @param {SelectionModel} this
23687              * @param {Number} rowIndex The selected index
23688              * @param {Boolean} keepExisting False if other selections will be cleared
23689              */
23690             "beforerowselect" : true,
23691         /**
23692              * @event rowselect
23693              * Fires when a row is selected.
23694              * @param {SelectionModel} this
23695              * @param {Number} rowIndex The selected index
23696              * @param {Roo.data.Record} r The record
23697              */
23698             "rowselect" : true,
23699         /**
23700              * @event rowdeselect
23701              * Fires when a row is deselected.
23702              * @param {SelectionModel} this
23703              * @param {Number} rowIndex The selected index
23704              */
23705         "rowdeselect" : true
23706     });
23707     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23708     this.locked = false;
23709  };
23710
23711 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23712     /**
23713      * @cfg {Boolean} singleSelect
23714      * True to allow selection of only one row at a time (defaults to false)
23715      */
23716     singleSelect : false,
23717
23718     // private
23719     initEvents : function()
23720     {
23721
23722         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23723         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23724         //}else{ // allow click to work like normal
23725          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23726         //}
23727         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23728         this.grid.on("rowclick", this.handleMouseDown, this);
23729         
23730         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23731             "up" : function(e){
23732                 if(!e.shiftKey){
23733                     this.selectPrevious(e.shiftKey);
23734                 }else if(this.last !== false && this.lastActive !== false){
23735                     var last = this.last;
23736                     this.selectRange(this.last,  this.lastActive-1);
23737                     this.grid.getView().focusRow(this.lastActive);
23738                     if(last !== false){
23739                         this.last = last;
23740                     }
23741                 }else{
23742                     this.selectFirstRow();
23743                 }
23744                 this.fireEvent("afterselectionchange", this);
23745             },
23746             "down" : function(e){
23747                 if(!e.shiftKey){
23748                     this.selectNext(e.shiftKey);
23749                 }else if(this.last !== false && this.lastActive !== false){
23750                     var last = this.last;
23751                     this.selectRange(this.last,  this.lastActive+1);
23752                     this.grid.getView().focusRow(this.lastActive);
23753                     if(last !== false){
23754                         this.last = last;
23755                     }
23756                 }else{
23757                     this.selectFirstRow();
23758                 }
23759                 this.fireEvent("afterselectionchange", this);
23760             },
23761             scope: this
23762         });
23763         this.grid.store.on('load', function(){
23764             this.selections.clear();
23765         },this);
23766         /*
23767         var view = this.grid.view;
23768         view.on("refresh", this.onRefresh, this);
23769         view.on("rowupdated", this.onRowUpdated, this);
23770         view.on("rowremoved", this.onRemove, this);
23771         */
23772     },
23773
23774     // private
23775     onRefresh : function()
23776     {
23777         var ds = this.grid.store, i, v = this.grid.view;
23778         var s = this.selections;
23779         s.each(function(r){
23780             if((i = ds.indexOfId(r.id)) != -1){
23781                 v.onRowSelect(i);
23782             }else{
23783                 s.remove(r);
23784             }
23785         });
23786     },
23787
23788     // private
23789     onRemove : function(v, index, r){
23790         this.selections.remove(r);
23791     },
23792
23793     // private
23794     onRowUpdated : function(v, index, r){
23795         if(this.isSelected(r)){
23796             v.onRowSelect(index);
23797         }
23798     },
23799
23800     /**
23801      * Select records.
23802      * @param {Array} records The records to select
23803      * @param {Boolean} keepExisting (optional) True to keep existing selections
23804      */
23805     selectRecords : function(records, keepExisting)
23806     {
23807         if(!keepExisting){
23808             this.clearSelections();
23809         }
23810             var ds = this.grid.store;
23811         for(var i = 0, len = records.length; i < len; i++){
23812             this.selectRow(ds.indexOf(records[i]), true);
23813         }
23814     },
23815
23816     /**
23817      * Gets the number of selected rows.
23818      * @return {Number}
23819      */
23820     getCount : function(){
23821         return this.selections.length;
23822     },
23823
23824     /**
23825      * Selects the first row in the grid.
23826      */
23827     selectFirstRow : function(){
23828         this.selectRow(0);
23829     },
23830
23831     /**
23832      * Select the last row.
23833      * @param {Boolean} keepExisting (optional) True to keep existing selections
23834      */
23835     selectLastRow : function(keepExisting){
23836         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23837         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23838     },
23839
23840     /**
23841      * Selects the row immediately following the last selected row.
23842      * @param {Boolean} keepExisting (optional) True to keep existing selections
23843      */
23844     selectNext : function(keepExisting)
23845     {
23846             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23847             this.selectRow(this.last+1, keepExisting);
23848             this.grid.getView().focusRow(this.last);
23849         }
23850     },
23851
23852     /**
23853      * Selects the row that precedes the last selected row.
23854      * @param {Boolean} keepExisting (optional) True to keep existing selections
23855      */
23856     selectPrevious : function(keepExisting){
23857         if(this.last){
23858             this.selectRow(this.last-1, keepExisting);
23859             this.grid.getView().focusRow(this.last);
23860         }
23861     },
23862
23863     /**
23864      * Returns the selected records
23865      * @return {Array} Array of selected records
23866      */
23867     getSelections : function(){
23868         return [].concat(this.selections.items);
23869     },
23870
23871     /**
23872      * Returns the first selected record.
23873      * @return {Record}
23874      */
23875     getSelected : function(){
23876         return this.selections.itemAt(0);
23877     },
23878
23879
23880     /**
23881      * Clears all selections.
23882      */
23883     clearSelections : function(fast)
23884     {
23885         if(this.locked) {
23886             return;
23887         }
23888         if(fast !== true){
23889                 var ds = this.grid.store;
23890             var s = this.selections;
23891             s.each(function(r){
23892                 this.deselectRow(ds.indexOfId(r.id));
23893             }, this);
23894             s.clear();
23895         }else{
23896             this.selections.clear();
23897         }
23898         this.last = false;
23899     },
23900
23901
23902     /**
23903      * Selects all rows.
23904      */
23905     selectAll : function(){
23906         if(this.locked) {
23907             return;
23908         }
23909         this.selections.clear();
23910         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23911             this.selectRow(i, true);
23912         }
23913     },
23914
23915     /**
23916      * Returns True if there is a selection.
23917      * @return {Boolean}
23918      */
23919     hasSelection : function(){
23920         return this.selections.length > 0;
23921     },
23922
23923     /**
23924      * Returns True if the specified row is selected.
23925      * @param {Number/Record} record The record or index of the record to check
23926      * @return {Boolean}
23927      */
23928     isSelected : function(index){
23929             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23930         return (r && this.selections.key(r.id) ? true : false);
23931     },
23932
23933     /**
23934      * Returns True if the specified record id is selected.
23935      * @param {String} id The id of record to check
23936      * @return {Boolean}
23937      */
23938     isIdSelected : function(id){
23939         return (this.selections.key(id) ? true : false);
23940     },
23941
23942
23943     // private
23944     handleMouseDBClick : function(e, t){
23945         
23946     },
23947     // private
23948     handleMouseDown : function(e, t)
23949     {
23950             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23951         if(this.isLocked() || rowIndex < 0 ){
23952             return;
23953         };
23954         if(e.shiftKey && this.last !== false){
23955             var last = this.last;
23956             this.selectRange(last, rowIndex, e.ctrlKey);
23957             this.last = last; // reset the last
23958             t.focus();
23959     
23960         }else{
23961             var isSelected = this.isSelected(rowIndex);
23962             //Roo.log("select row:" + rowIndex);
23963             if(isSelected){
23964                 this.deselectRow(rowIndex);
23965             } else {
23966                         this.selectRow(rowIndex, true);
23967             }
23968     
23969             /*
23970                 if(e.button !== 0 && isSelected){
23971                 alert('rowIndex 2: ' + rowIndex);
23972                     view.focusRow(rowIndex);
23973                 }else if(e.ctrlKey && isSelected){
23974                     this.deselectRow(rowIndex);
23975                 }else if(!isSelected){
23976                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23977                     view.focusRow(rowIndex);
23978                 }
23979             */
23980         }
23981         this.fireEvent("afterselectionchange", this);
23982     },
23983     // private
23984     handleDragableRowClick :  function(grid, rowIndex, e) 
23985     {
23986         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23987             this.selectRow(rowIndex, false);
23988             grid.view.focusRow(rowIndex);
23989              this.fireEvent("afterselectionchange", this);
23990         }
23991     },
23992     
23993     /**
23994      * Selects multiple rows.
23995      * @param {Array} rows Array of the indexes of the row to select
23996      * @param {Boolean} keepExisting (optional) True to keep existing selections
23997      */
23998     selectRows : function(rows, keepExisting){
23999         if(!keepExisting){
24000             this.clearSelections();
24001         }
24002         for(var i = 0, len = rows.length; i < len; i++){
24003             this.selectRow(rows[i], true);
24004         }
24005     },
24006
24007     /**
24008      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24009      * @param {Number} startRow The index of the first row in the range
24010      * @param {Number} endRow The index of the last row in the range
24011      * @param {Boolean} keepExisting (optional) True to retain existing selections
24012      */
24013     selectRange : function(startRow, endRow, keepExisting){
24014         if(this.locked) {
24015             return;
24016         }
24017         if(!keepExisting){
24018             this.clearSelections();
24019         }
24020         if(startRow <= endRow){
24021             for(var i = startRow; i <= endRow; i++){
24022                 this.selectRow(i, true);
24023             }
24024         }else{
24025             for(var i = startRow; i >= endRow; i--){
24026                 this.selectRow(i, true);
24027             }
24028         }
24029     },
24030
24031     /**
24032      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24033      * @param {Number} startRow The index of the first row in the range
24034      * @param {Number} endRow The index of the last row in the range
24035      */
24036     deselectRange : function(startRow, endRow, preventViewNotify){
24037         if(this.locked) {
24038             return;
24039         }
24040         for(var i = startRow; i <= endRow; i++){
24041             this.deselectRow(i, preventViewNotify);
24042         }
24043     },
24044
24045     /**
24046      * Selects a row.
24047      * @param {Number} row The index of the row to select
24048      * @param {Boolean} keepExisting (optional) True to keep existing selections
24049      */
24050     selectRow : function(index, keepExisting, preventViewNotify)
24051     {
24052             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24053             return;
24054         }
24055         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24056             if(!keepExisting || this.singleSelect){
24057                 this.clearSelections();
24058             }
24059             
24060             var r = this.grid.store.getAt(index);
24061             //console.log('selectRow - record id :' + r.id);
24062             
24063             this.selections.add(r);
24064             this.last = this.lastActive = index;
24065             if(!preventViewNotify){
24066                 var proxy = new Roo.Element(
24067                                 this.grid.getRowDom(index)
24068                 );
24069                 proxy.addClass('bg-info info');
24070             }
24071             this.fireEvent("rowselect", this, index, r);
24072             this.fireEvent("selectionchange", this);
24073         }
24074     },
24075
24076     /**
24077      * Deselects a row.
24078      * @param {Number} row The index of the row to deselect
24079      */
24080     deselectRow : function(index, preventViewNotify)
24081     {
24082         if(this.locked) {
24083             return;
24084         }
24085         if(this.last == index){
24086             this.last = false;
24087         }
24088         if(this.lastActive == index){
24089             this.lastActive = false;
24090         }
24091         
24092         var r = this.grid.store.getAt(index);
24093         if (!r) {
24094             return;
24095         }
24096         
24097         this.selections.remove(r);
24098         //.console.log('deselectRow - record id :' + r.id);
24099         if(!preventViewNotify){
24100         
24101             var proxy = new Roo.Element(
24102                 this.grid.getRowDom(index)
24103             );
24104             proxy.removeClass('bg-info info');
24105         }
24106         this.fireEvent("rowdeselect", this, index);
24107         this.fireEvent("selectionchange", this);
24108     },
24109
24110     // private
24111     restoreLast : function(){
24112         if(this._last){
24113             this.last = this._last;
24114         }
24115     },
24116
24117     // private
24118     acceptsNav : function(row, col, cm){
24119         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24120     },
24121
24122     // private
24123     onEditorKey : function(field, e){
24124         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24125         if(k == e.TAB){
24126             e.stopEvent();
24127             ed.completeEdit();
24128             if(e.shiftKey){
24129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24130             }else{
24131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24132             }
24133         }else if(k == e.ENTER && !e.ctrlKey){
24134             e.stopEvent();
24135             ed.completeEdit();
24136             if(e.shiftKey){
24137                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24138             }else{
24139                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24140             }
24141         }else if(k == e.ESC){
24142             ed.cancelEdit();
24143         }
24144         if(newCell){
24145             g.startEditing(newCell[0], newCell[1]);
24146         }
24147     }
24148 });
24149 /*
24150  * Based on:
24151  * Ext JS Library 1.1.1
24152  * Copyright(c) 2006-2007, Ext JS, LLC.
24153  *
24154  * Originally Released Under LGPL - original licence link has changed is not relivant.
24155  *
24156  * Fork - LGPL
24157  * <script type="text/javascript">
24158  */
24159  
24160 /**
24161  * @class Roo.bootstrap.PagingToolbar
24162  * @extends Roo.bootstrap.NavSimplebar
24163  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24164  * @constructor
24165  * Create a new PagingToolbar
24166  * @param {Object} config The config object
24167  * @param {Roo.data.Store} store
24168  */
24169 Roo.bootstrap.PagingToolbar = function(config)
24170 {
24171     // old args format still supported... - xtype is prefered..
24172         // created from xtype...
24173     
24174     this.ds = config.dataSource;
24175     
24176     if (config.store && !this.ds) {
24177         this.store= Roo.factory(config.store, Roo.data);
24178         this.ds = this.store;
24179         this.ds.xmodule = this.xmodule || false;
24180     }
24181     
24182     this.toolbarItems = [];
24183     if (config.items) {
24184         this.toolbarItems = config.items;
24185     }
24186     
24187     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24188     
24189     this.cursor = 0;
24190     
24191     if (this.ds) { 
24192         this.bind(this.ds);
24193     }
24194     
24195     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24196     
24197 };
24198
24199 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24200     /**
24201      * @cfg {Roo.data.Store} dataSource
24202      * The underlying data store providing the paged data
24203      */
24204     /**
24205      * @cfg {String/HTMLElement/Element} container
24206      * container The id or element that will contain the toolbar
24207      */
24208     /**
24209      * @cfg {Boolean} displayInfo
24210      * True to display the displayMsg (defaults to false)
24211      */
24212     /**
24213      * @cfg {Number} pageSize
24214      * The number of records to display per page (defaults to 20)
24215      */
24216     pageSize: 20,
24217     /**
24218      * @cfg {String} displayMsg
24219      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24220      */
24221     displayMsg : 'Displaying {0} - {1} of {2}',
24222     /**
24223      * @cfg {String} emptyMsg
24224      * The message to display when no records are found (defaults to "No data to display")
24225      */
24226     emptyMsg : 'No data to display',
24227     /**
24228      * Customizable piece of the default paging text (defaults to "Page")
24229      * @type String
24230      */
24231     beforePageText : "Page",
24232     /**
24233      * Customizable piece of the default paging text (defaults to "of %0")
24234      * @type String
24235      */
24236     afterPageText : "of {0}",
24237     /**
24238      * Customizable piece of the default paging text (defaults to "First Page")
24239      * @type String
24240      */
24241     firstText : "First Page",
24242     /**
24243      * Customizable piece of the default paging text (defaults to "Previous Page")
24244      * @type String
24245      */
24246     prevText : "Previous Page",
24247     /**
24248      * Customizable piece of the default paging text (defaults to "Next Page")
24249      * @type String
24250      */
24251     nextText : "Next Page",
24252     /**
24253      * Customizable piece of the default paging text (defaults to "Last Page")
24254      * @type String
24255      */
24256     lastText : "Last Page",
24257     /**
24258      * Customizable piece of the default paging text (defaults to "Refresh")
24259      * @type String
24260      */
24261     refreshText : "Refresh",
24262
24263     buttons : false,
24264     // private
24265     onRender : function(ct, position) 
24266     {
24267         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24268         this.navgroup.parentId = this.id;
24269         this.navgroup.onRender(this.el, null);
24270         // add the buttons to the navgroup
24271         
24272         if(this.displayInfo){
24273             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24274             this.displayEl = this.el.select('.x-paging-info', true).first();
24275 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24276 //            this.displayEl = navel.el.select('span',true).first();
24277         }
24278         
24279         var _this = this;
24280         
24281         if(this.buttons){
24282             Roo.each(_this.buttons, function(e){ // this might need to use render????
24283                Roo.factory(e).onRender(_this.el, null);
24284             });
24285         }
24286             
24287         Roo.each(_this.toolbarItems, function(e) {
24288             _this.navgroup.addItem(e);
24289         });
24290         
24291         
24292         this.first = this.navgroup.addItem({
24293             tooltip: this.firstText,
24294             cls: "prev",
24295             icon : 'fa fa-backward',
24296             disabled: true,
24297             preventDefault: true,
24298             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24299         });
24300         
24301         this.prev =  this.navgroup.addItem({
24302             tooltip: this.prevText,
24303             cls: "prev",
24304             icon : 'fa fa-step-backward',
24305             disabled: true,
24306             preventDefault: true,
24307             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24308         });
24309     //this.addSeparator();
24310         
24311         
24312         var field = this.navgroup.addItem( {
24313             tagtype : 'span',
24314             cls : 'x-paging-position',
24315             
24316             html : this.beforePageText  +
24317                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24318                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24319          } ); //?? escaped?
24320         
24321         this.field = field.el.select('input', true).first();
24322         this.field.on("keydown", this.onPagingKeydown, this);
24323         this.field.on("focus", function(){this.dom.select();});
24324     
24325     
24326         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24327         //this.field.setHeight(18);
24328         //this.addSeparator();
24329         this.next = this.navgroup.addItem({
24330             tooltip: this.nextText,
24331             cls: "next",
24332             html : ' <i class="fa fa-step-forward">',
24333             disabled: true,
24334             preventDefault: true,
24335             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24336         });
24337         this.last = this.navgroup.addItem({
24338             tooltip: this.lastText,
24339             icon : 'fa fa-forward',
24340             cls: "next",
24341             disabled: true,
24342             preventDefault: true,
24343             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24344         });
24345     //this.addSeparator();
24346         this.loading = this.navgroup.addItem({
24347             tooltip: this.refreshText,
24348             icon: 'fa fa-refresh',
24349             preventDefault: true,
24350             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24351         });
24352         
24353     },
24354
24355     // private
24356     updateInfo : function(){
24357         if(this.displayEl){
24358             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24359             var msg = count == 0 ?
24360                 this.emptyMsg :
24361                 String.format(
24362                     this.displayMsg,
24363                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24364                 );
24365             this.displayEl.update(msg);
24366         }
24367     },
24368
24369     // private
24370     onLoad : function(ds, r, o)
24371     {
24372         this.cursor = o.params ? o.params.start : 0;
24373         var d = this.getPageData(),
24374             ap = d.activePage,
24375             ps = d.pages;
24376         
24377         
24378         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24379         this.field.dom.value = ap;
24380         this.first.setDisabled(ap == 1);
24381         this.prev.setDisabled(ap == 1);
24382         this.next.setDisabled(ap == ps);
24383         this.last.setDisabled(ap == ps);
24384         this.loading.enable();
24385         this.updateInfo();
24386     },
24387
24388     // private
24389     getPageData : function(){
24390         var total = this.ds.getTotalCount();
24391         return {
24392             total : total,
24393             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24394             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24395         };
24396     },
24397
24398     // private
24399     onLoadError : function(){
24400         this.loading.enable();
24401     },
24402
24403     // private
24404     onPagingKeydown : function(e){
24405         var k = e.getKey();
24406         var d = this.getPageData();
24407         if(k == e.RETURN){
24408             var v = this.field.dom.value, pageNum;
24409             if(!v || isNaN(pageNum = parseInt(v, 10))){
24410                 this.field.dom.value = d.activePage;
24411                 return;
24412             }
24413             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24414             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24415             e.stopEvent();
24416         }
24417         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))
24418         {
24419           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24420           this.field.dom.value = pageNum;
24421           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24422           e.stopEvent();
24423         }
24424         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24425         {
24426           var v = this.field.dom.value, pageNum; 
24427           var increment = (e.shiftKey) ? 10 : 1;
24428           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24429                 increment *= -1;
24430           }
24431           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24432             this.field.dom.value = d.activePage;
24433             return;
24434           }
24435           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24436           {
24437             this.field.dom.value = parseInt(v, 10) + increment;
24438             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24439             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24440           }
24441           e.stopEvent();
24442         }
24443     },
24444
24445     // private
24446     beforeLoad : function(){
24447         if(this.loading){
24448             this.loading.disable();
24449         }
24450     },
24451
24452     // private
24453     onClick : function(which){
24454         
24455         var ds = this.ds;
24456         if (!ds) {
24457             return;
24458         }
24459         
24460         switch(which){
24461             case "first":
24462                 ds.load({params:{start: 0, limit: this.pageSize}});
24463             break;
24464             case "prev":
24465                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24466             break;
24467             case "next":
24468                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24469             break;
24470             case "last":
24471                 var total = ds.getTotalCount();
24472                 var extra = total % this.pageSize;
24473                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24474                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24475             break;
24476             case "refresh":
24477                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24478             break;
24479         }
24480     },
24481
24482     /**
24483      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24484      * @param {Roo.data.Store} store The data store to unbind
24485      */
24486     unbind : function(ds){
24487         ds.un("beforeload", this.beforeLoad, this);
24488         ds.un("load", this.onLoad, this);
24489         ds.un("loadexception", this.onLoadError, this);
24490         ds.un("remove", this.updateInfo, this);
24491         ds.un("add", this.updateInfo, this);
24492         this.ds = undefined;
24493     },
24494
24495     /**
24496      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24497      * @param {Roo.data.Store} store The data store to bind
24498      */
24499     bind : function(ds){
24500         ds.on("beforeload", this.beforeLoad, this);
24501         ds.on("load", this.onLoad, this);
24502         ds.on("loadexception", this.onLoadError, this);
24503         ds.on("remove", this.updateInfo, this);
24504         ds.on("add", this.updateInfo, this);
24505         this.ds = ds;
24506     }
24507 });/*
24508  * - LGPL
24509  *
24510  * element
24511  * 
24512  */
24513
24514 /**
24515  * @class Roo.bootstrap.MessageBar
24516  * @extends Roo.bootstrap.Component
24517  * Bootstrap MessageBar class
24518  * @cfg {String} html contents of the MessageBar
24519  * @cfg {String} weight (info | success | warning | danger) default info
24520  * @cfg {String} beforeClass insert the bar before the given class
24521  * @cfg {Boolean} closable (true | false) default false
24522  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24523  * 
24524  * @constructor
24525  * Create a new Element
24526  * @param {Object} config The config object
24527  */
24528
24529 Roo.bootstrap.MessageBar = function(config){
24530     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24531 };
24532
24533 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24534     
24535     html: '',
24536     weight: 'info',
24537     closable: false,
24538     fixed: false,
24539     beforeClass: 'bootstrap-sticky-wrap',
24540     
24541     getAutoCreate : function(){
24542         
24543         var cfg = {
24544             tag: 'div',
24545             cls: 'alert alert-dismissable alert-' + this.weight,
24546             cn: [
24547                 {
24548                     tag: 'span',
24549                     cls: 'message',
24550                     html: this.html || ''
24551                 }
24552             ]
24553         };
24554         
24555         if(this.fixed){
24556             cfg.cls += ' alert-messages-fixed';
24557         }
24558         
24559         if(this.closable){
24560             cfg.cn.push({
24561                 tag: 'button',
24562                 cls: 'close',
24563                 html: 'x'
24564             });
24565         }
24566         
24567         return cfg;
24568     },
24569     
24570     onRender : function(ct, position)
24571     {
24572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24573         
24574         if(!this.el){
24575             var cfg = Roo.apply({},  this.getAutoCreate());
24576             cfg.id = Roo.id();
24577             
24578             if (this.cls) {
24579                 cfg.cls += ' ' + this.cls;
24580             }
24581             if (this.style) {
24582                 cfg.style = this.style;
24583             }
24584             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24585             
24586             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24587         }
24588         
24589         this.el.select('>button.close').on('click', this.hide, this);
24590         
24591     },
24592     
24593     show : function()
24594     {
24595         if (!this.rendered) {
24596             this.render();
24597         }
24598         
24599         this.el.show();
24600         
24601         this.fireEvent('show', this);
24602         
24603     },
24604     
24605     hide : function()
24606     {
24607         if (!this.rendered) {
24608             this.render();
24609         }
24610         
24611         this.el.hide();
24612         
24613         this.fireEvent('hide', this);
24614     },
24615     
24616     update : function()
24617     {
24618 //        var e = this.el.dom.firstChild;
24619 //        
24620 //        if(this.closable){
24621 //            e = e.nextSibling;
24622 //        }
24623 //        
24624 //        e.data = this.html || '';
24625
24626         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24627     }
24628    
24629 });
24630
24631  
24632
24633      /*
24634  * - LGPL
24635  *
24636  * Graph
24637  * 
24638  */
24639
24640
24641 /**
24642  * @class Roo.bootstrap.Graph
24643  * @extends Roo.bootstrap.Component
24644  * Bootstrap Graph class
24645 > Prameters
24646  -sm {number} sm 4
24647  -md {number} md 5
24648  @cfg {String} graphtype  bar | vbar | pie
24649  @cfg {number} g_x coodinator | centre x (pie)
24650  @cfg {number} g_y coodinator | centre y (pie)
24651  @cfg {number} g_r radius (pie)
24652  @cfg {number} g_height height of the chart (respected by all elements in the set)
24653  @cfg {number} g_width width of the chart (respected by all elements in the set)
24654  @cfg {Object} title The title of the chart
24655     
24656  -{Array}  values
24657  -opts (object) options for the chart 
24658      o {
24659      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24660      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24661      o vgutter (number)
24662      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.
24663      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24664      o to
24665      o stretch (boolean)
24666      o }
24667  -opts (object) options for the pie
24668      o{
24669      o cut
24670      o startAngle (number)
24671      o endAngle (number)
24672      } 
24673  *
24674  * @constructor
24675  * Create a new Input
24676  * @param {Object} config The config object
24677  */
24678
24679 Roo.bootstrap.Graph = function(config){
24680     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24681     
24682     this.addEvents({
24683         // img events
24684         /**
24685          * @event click
24686          * The img click event for the img.
24687          * @param {Roo.EventObject} e
24688          */
24689         "click" : true
24690     });
24691 };
24692
24693 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24694     
24695     sm: 4,
24696     md: 5,
24697     graphtype: 'bar',
24698     g_height: 250,
24699     g_width: 400,
24700     g_x: 50,
24701     g_y: 50,
24702     g_r: 30,
24703     opts:{
24704         //g_colors: this.colors,
24705         g_type: 'soft',
24706         g_gutter: '20%'
24707
24708     },
24709     title : false,
24710
24711     getAutoCreate : function(){
24712         
24713         var cfg = {
24714             tag: 'div',
24715             html : null
24716         };
24717         
24718         
24719         return  cfg;
24720     },
24721
24722     onRender : function(ct,position){
24723         
24724         
24725         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24726         
24727         if (typeof(Raphael) == 'undefined') {
24728             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24729             return;
24730         }
24731         
24732         this.raphael = Raphael(this.el.dom);
24733         
24734                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24735                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24736                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24737                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24738                 /*
24739                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24740                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24741                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24742                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24743                 
24744                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24745                 r.barchart(330, 10, 300, 220, data1);
24746                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24747                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24748                 */
24749                 
24750                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24751                 // r.barchart(30, 30, 560, 250,  xdata, {
24752                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24753                 //     axis : "0 0 1 1",
24754                 //     axisxlabels :  xdata
24755                 //     //yvalues : cols,
24756                    
24757                 // });
24758 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24759 //        
24760 //        this.load(null,xdata,{
24761 //                axis : "0 0 1 1",
24762 //                axisxlabels :  xdata
24763 //                });
24764
24765     },
24766
24767     load : function(graphtype,xdata,opts)
24768     {
24769         this.raphael.clear();
24770         if(!graphtype) {
24771             graphtype = this.graphtype;
24772         }
24773         if(!opts){
24774             opts = this.opts;
24775         }
24776         var r = this.raphael,
24777             fin = function () {
24778                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24779             },
24780             fout = function () {
24781                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24782             },
24783             pfin = function() {
24784                 this.sector.stop();
24785                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24786
24787                 if (this.label) {
24788                     this.label[0].stop();
24789                     this.label[0].attr({ r: 7.5 });
24790                     this.label[1].attr({ "font-weight": 800 });
24791                 }
24792             },
24793             pfout = function() {
24794                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24795
24796                 if (this.label) {
24797                     this.label[0].animate({ r: 5 }, 500, "bounce");
24798                     this.label[1].attr({ "font-weight": 400 });
24799                 }
24800             };
24801
24802         switch(graphtype){
24803             case 'bar':
24804                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24805                 break;
24806             case 'hbar':
24807                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24808                 break;
24809             case 'pie':
24810 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24811 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24812 //            
24813                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24814                 
24815                 break;
24816
24817         }
24818         
24819         if(this.title){
24820             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24821         }
24822         
24823     },
24824     
24825     setTitle: function(o)
24826     {
24827         this.title = o;
24828     },
24829     
24830     initEvents: function() {
24831         
24832         if(!this.href){
24833             this.el.on('click', this.onClick, this);
24834         }
24835     },
24836     
24837     onClick : function(e)
24838     {
24839         Roo.log('img onclick');
24840         this.fireEvent('click', this, e);
24841     }
24842    
24843 });
24844
24845  
24846 /*
24847  * - LGPL
24848  *
24849  * numberBox
24850  * 
24851  */
24852 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24853
24854 /**
24855  * @class Roo.bootstrap.dash.NumberBox
24856  * @extends Roo.bootstrap.Component
24857  * Bootstrap NumberBox class
24858  * @cfg {String} headline Box headline
24859  * @cfg {String} content Box content
24860  * @cfg {String} icon Box icon
24861  * @cfg {String} footer Footer text
24862  * @cfg {String} fhref Footer href
24863  * 
24864  * @constructor
24865  * Create a new NumberBox
24866  * @param {Object} config The config object
24867  */
24868
24869
24870 Roo.bootstrap.dash.NumberBox = function(config){
24871     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24872     
24873 };
24874
24875 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24876     
24877     headline : '',
24878     content : '',
24879     icon : '',
24880     footer : '',
24881     fhref : '',
24882     ficon : '',
24883     
24884     getAutoCreate : function(){
24885         
24886         var cfg = {
24887             tag : 'div',
24888             cls : 'small-box ',
24889             cn : [
24890                 {
24891                     tag : 'div',
24892                     cls : 'inner',
24893                     cn :[
24894                         {
24895                             tag : 'h3',
24896                             cls : 'roo-headline',
24897                             html : this.headline
24898                         },
24899                         {
24900                             tag : 'p',
24901                             cls : 'roo-content',
24902                             html : this.content
24903                         }
24904                     ]
24905                 }
24906             ]
24907         };
24908         
24909         if(this.icon){
24910             cfg.cn.push({
24911                 tag : 'div',
24912                 cls : 'icon',
24913                 cn :[
24914                     {
24915                         tag : 'i',
24916                         cls : 'ion ' + this.icon
24917                     }
24918                 ]
24919             });
24920         }
24921         
24922         if(this.footer){
24923             var footer = {
24924                 tag : 'a',
24925                 cls : 'small-box-footer',
24926                 href : this.fhref || '#',
24927                 html : this.footer
24928             };
24929             
24930             cfg.cn.push(footer);
24931             
24932         }
24933         
24934         return  cfg;
24935     },
24936
24937     onRender : function(ct,position){
24938         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24939
24940
24941        
24942                 
24943     },
24944
24945     setHeadline: function (value)
24946     {
24947         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24948     },
24949     
24950     setFooter: function (value, href)
24951     {
24952         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24953         
24954         if(href){
24955             this.el.select('a.small-box-footer',true).first().attr('href', href);
24956         }
24957         
24958     },
24959
24960     setContent: function (value)
24961     {
24962         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24963     },
24964
24965     initEvents: function() 
24966     {   
24967         
24968     }
24969     
24970 });
24971
24972  
24973 /*
24974  * - LGPL
24975  *
24976  * TabBox
24977  * 
24978  */
24979 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24980
24981 /**
24982  * @class Roo.bootstrap.dash.TabBox
24983  * @extends Roo.bootstrap.Component
24984  * Bootstrap TabBox class
24985  * @cfg {String} title Title of the TabBox
24986  * @cfg {String} icon Icon of the TabBox
24987  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24988  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24989  * 
24990  * @constructor
24991  * Create a new TabBox
24992  * @param {Object} config The config object
24993  */
24994
24995
24996 Roo.bootstrap.dash.TabBox = function(config){
24997     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24998     this.addEvents({
24999         // raw events
25000         /**
25001          * @event addpane
25002          * When a pane is added
25003          * @param {Roo.bootstrap.dash.TabPane} pane
25004          */
25005         "addpane" : true,
25006         /**
25007          * @event activatepane
25008          * When a pane is activated
25009          * @param {Roo.bootstrap.dash.TabPane} pane
25010          */
25011         "activatepane" : true
25012         
25013          
25014     });
25015     
25016     this.panes = [];
25017 };
25018
25019 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25020
25021     title : '',
25022     icon : false,
25023     showtabs : true,
25024     tabScrollable : false,
25025     
25026     getChildContainer : function()
25027     {
25028         return this.el.select('.tab-content', true).first();
25029     },
25030     
25031     getAutoCreate : function(){
25032         
25033         var header = {
25034             tag: 'li',
25035             cls: 'pull-left header',
25036             html: this.title,
25037             cn : []
25038         };
25039         
25040         if(this.icon){
25041             header.cn.push({
25042                 tag: 'i',
25043                 cls: 'fa ' + this.icon
25044             });
25045         }
25046         
25047         var h = {
25048             tag: 'ul',
25049             cls: 'nav nav-tabs pull-right',
25050             cn: [
25051                 header
25052             ]
25053         };
25054         
25055         if(this.tabScrollable){
25056             h = {
25057                 tag: 'div',
25058                 cls: 'tab-header',
25059                 cn: [
25060                     {
25061                         tag: 'ul',
25062                         cls: 'nav nav-tabs pull-right',
25063                         cn: [
25064                             header
25065                         ]
25066                     }
25067                 ]
25068             };
25069         }
25070         
25071         var cfg = {
25072             tag: 'div',
25073             cls: 'nav-tabs-custom',
25074             cn: [
25075                 h,
25076                 {
25077                     tag: 'div',
25078                     cls: 'tab-content no-padding',
25079                     cn: []
25080                 }
25081             ]
25082         };
25083
25084         return  cfg;
25085     },
25086     initEvents : function()
25087     {
25088         //Roo.log('add add pane handler');
25089         this.on('addpane', this.onAddPane, this);
25090     },
25091      /**
25092      * Updates the box title
25093      * @param {String} html to set the title to.
25094      */
25095     setTitle : function(value)
25096     {
25097         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25098     },
25099     onAddPane : function(pane)
25100     {
25101         this.panes.push(pane);
25102         //Roo.log('addpane');
25103         //Roo.log(pane);
25104         // tabs are rendere left to right..
25105         if(!this.showtabs){
25106             return;
25107         }
25108         
25109         var ctr = this.el.select('.nav-tabs', true).first();
25110          
25111          
25112         var existing = ctr.select('.nav-tab',true);
25113         var qty = existing.getCount();;
25114         
25115         
25116         var tab = ctr.createChild({
25117             tag : 'li',
25118             cls : 'nav-tab' + (qty ? '' : ' active'),
25119             cn : [
25120                 {
25121                     tag : 'a',
25122                     href:'#',
25123                     html : pane.title
25124                 }
25125             ]
25126         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25127         pane.tab = tab;
25128         
25129         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25130         if (!qty) {
25131             pane.el.addClass('active');
25132         }
25133         
25134                 
25135     },
25136     onTabClick : function(ev,un,ob,pane)
25137     {
25138         //Roo.log('tab - prev default');
25139         ev.preventDefault();
25140         
25141         
25142         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25143         pane.tab.addClass('active');
25144         //Roo.log(pane.title);
25145         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25146         // technically we should have a deactivate event.. but maybe add later.
25147         // and it should not de-activate the selected tab...
25148         this.fireEvent('activatepane', pane);
25149         pane.el.addClass('active');
25150         pane.fireEvent('activate');
25151         
25152         
25153     },
25154     
25155     getActivePane : function()
25156     {
25157         var r = false;
25158         Roo.each(this.panes, function(p) {
25159             if(p.el.hasClass('active')){
25160                 r = p;
25161                 return false;
25162             }
25163             
25164             return;
25165         });
25166         
25167         return r;
25168     }
25169     
25170     
25171 });
25172
25173  
25174 /*
25175  * - LGPL
25176  *
25177  * Tab pane
25178  * 
25179  */
25180 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25181 /**
25182  * @class Roo.bootstrap.TabPane
25183  * @extends Roo.bootstrap.Component
25184  * Bootstrap TabPane class
25185  * @cfg {Boolean} active (false | true) Default false
25186  * @cfg {String} title title of panel
25187
25188  * 
25189  * @constructor
25190  * Create a new TabPane
25191  * @param {Object} config The config object
25192  */
25193
25194 Roo.bootstrap.dash.TabPane = function(config){
25195     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25196     
25197     this.addEvents({
25198         // raw events
25199         /**
25200          * @event activate
25201          * When a pane is activated
25202          * @param {Roo.bootstrap.dash.TabPane} pane
25203          */
25204         "activate" : true
25205          
25206     });
25207 };
25208
25209 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25210     
25211     active : false,
25212     title : '',
25213     
25214     // the tabBox that this is attached to.
25215     tab : false,
25216      
25217     getAutoCreate : function() 
25218     {
25219         var cfg = {
25220             tag: 'div',
25221             cls: 'tab-pane'
25222         };
25223         
25224         if(this.active){
25225             cfg.cls += ' active';
25226         }
25227         
25228         return cfg;
25229     },
25230     initEvents  : function()
25231     {
25232         //Roo.log('trigger add pane handler');
25233         this.parent().fireEvent('addpane', this)
25234     },
25235     
25236      /**
25237      * Updates the tab title 
25238      * @param {String} html to set the title to.
25239      */
25240     setTitle: function(str)
25241     {
25242         if (!this.tab) {
25243             return;
25244         }
25245         this.title = str;
25246         this.tab.select('a', true).first().dom.innerHTML = str;
25247         
25248     }
25249     
25250     
25251     
25252 });
25253
25254  
25255
25256
25257  /*
25258  * - LGPL
25259  *
25260  * menu
25261  * 
25262  */
25263 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25264
25265 /**
25266  * @class Roo.bootstrap.menu.Menu
25267  * @extends Roo.bootstrap.Component
25268  * Bootstrap Menu class - container for Menu
25269  * @cfg {String} html Text of the menu
25270  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25271  * @cfg {String} icon Font awesome icon
25272  * @cfg {String} pos Menu align to (top | bottom) default bottom
25273  * 
25274  * 
25275  * @constructor
25276  * Create a new Menu
25277  * @param {Object} config The config object
25278  */
25279
25280
25281 Roo.bootstrap.menu.Menu = function(config){
25282     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25283     
25284     this.addEvents({
25285         /**
25286          * @event beforeshow
25287          * Fires before this menu is displayed
25288          * @param {Roo.bootstrap.menu.Menu} this
25289          */
25290         beforeshow : true,
25291         /**
25292          * @event beforehide
25293          * Fires before this menu is hidden
25294          * @param {Roo.bootstrap.menu.Menu} this
25295          */
25296         beforehide : true,
25297         /**
25298          * @event show
25299          * Fires after this menu is displayed
25300          * @param {Roo.bootstrap.menu.Menu} this
25301          */
25302         show : true,
25303         /**
25304          * @event hide
25305          * Fires after this menu is hidden
25306          * @param {Roo.bootstrap.menu.Menu} this
25307          */
25308         hide : true,
25309         /**
25310          * @event click
25311          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25312          * @param {Roo.bootstrap.menu.Menu} this
25313          * @param {Roo.EventObject} e
25314          */
25315         click : true
25316     });
25317     
25318 };
25319
25320 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25321     
25322     submenu : false,
25323     html : '',
25324     weight : 'default',
25325     icon : false,
25326     pos : 'bottom',
25327     
25328     
25329     getChildContainer : function() {
25330         if(this.isSubMenu){
25331             return this.el;
25332         }
25333         
25334         return this.el.select('ul.dropdown-menu', true).first();  
25335     },
25336     
25337     getAutoCreate : function()
25338     {
25339         var text = [
25340             {
25341                 tag : 'span',
25342                 cls : 'roo-menu-text',
25343                 html : this.html
25344             }
25345         ];
25346         
25347         if(this.icon){
25348             text.unshift({
25349                 tag : 'i',
25350                 cls : 'fa ' + this.icon
25351             })
25352         }
25353         
25354         
25355         var cfg = {
25356             tag : 'div',
25357             cls : 'btn-group',
25358             cn : [
25359                 {
25360                     tag : 'button',
25361                     cls : 'dropdown-button btn btn-' + this.weight,
25362                     cn : text
25363                 },
25364                 {
25365                     tag : 'button',
25366                     cls : 'dropdown-toggle btn btn-' + this.weight,
25367                     cn : [
25368                         {
25369                             tag : 'span',
25370                             cls : 'caret'
25371                         }
25372                     ]
25373                 },
25374                 {
25375                     tag : 'ul',
25376                     cls : 'dropdown-menu'
25377                 }
25378             ]
25379             
25380         };
25381         
25382         if(this.pos == 'top'){
25383             cfg.cls += ' dropup';
25384         }
25385         
25386         if(this.isSubMenu){
25387             cfg = {
25388                 tag : 'ul',
25389                 cls : 'dropdown-menu'
25390             }
25391         }
25392         
25393         return cfg;
25394     },
25395     
25396     onRender : function(ct, position)
25397     {
25398         this.isSubMenu = ct.hasClass('dropdown-submenu');
25399         
25400         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25401     },
25402     
25403     initEvents : function() 
25404     {
25405         if(this.isSubMenu){
25406             return;
25407         }
25408         
25409         this.hidden = true;
25410         
25411         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25412         this.triggerEl.on('click', this.onTriggerPress, this);
25413         
25414         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25415         this.buttonEl.on('click', this.onClick, this);
25416         
25417     },
25418     
25419     list : function()
25420     {
25421         if(this.isSubMenu){
25422             return this.el;
25423         }
25424         
25425         return this.el.select('ul.dropdown-menu', true).first();
25426     },
25427     
25428     onClick : function(e)
25429     {
25430         this.fireEvent("click", this, e);
25431     },
25432     
25433     onTriggerPress  : function(e)
25434     {   
25435         if (this.isVisible()) {
25436             this.hide();
25437         } else {
25438             this.show();
25439         }
25440     },
25441     
25442     isVisible : function(){
25443         return !this.hidden;
25444     },
25445     
25446     show : function()
25447     {
25448         this.fireEvent("beforeshow", this);
25449         
25450         this.hidden = false;
25451         this.el.addClass('open');
25452         
25453         Roo.get(document).on("mouseup", this.onMouseUp, this);
25454         
25455         this.fireEvent("show", this);
25456         
25457         
25458     },
25459     
25460     hide : function()
25461     {
25462         this.fireEvent("beforehide", this);
25463         
25464         this.hidden = true;
25465         this.el.removeClass('open');
25466         
25467         Roo.get(document).un("mouseup", this.onMouseUp);
25468         
25469         this.fireEvent("hide", this);
25470     },
25471     
25472     onMouseUp : function()
25473     {
25474         this.hide();
25475     }
25476     
25477 });
25478
25479  
25480  /*
25481  * - LGPL
25482  *
25483  * menu item
25484  * 
25485  */
25486 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25487
25488 /**
25489  * @class Roo.bootstrap.menu.Item
25490  * @extends Roo.bootstrap.Component
25491  * Bootstrap MenuItem class
25492  * @cfg {Boolean} submenu (true | false) default false
25493  * @cfg {String} html text of the item
25494  * @cfg {String} href the link
25495  * @cfg {Boolean} disable (true | false) default false
25496  * @cfg {Boolean} preventDefault (true | false) default true
25497  * @cfg {String} icon Font awesome icon
25498  * @cfg {String} pos Submenu align to (left | right) default right 
25499  * 
25500  * 
25501  * @constructor
25502  * Create a new Item
25503  * @param {Object} config The config object
25504  */
25505
25506
25507 Roo.bootstrap.menu.Item = function(config){
25508     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25509     this.addEvents({
25510         /**
25511          * @event mouseover
25512          * Fires when the mouse is hovering over this menu
25513          * @param {Roo.bootstrap.menu.Item} this
25514          * @param {Roo.EventObject} e
25515          */
25516         mouseover : true,
25517         /**
25518          * @event mouseout
25519          * Fires when the mouse exits this menu
25520          * @param {Roo.bootstrap.menu.Item} this
25521          * @param {Roo.EventObject} e
25522          */
25523         mouseout : true,
25524         // raw events
25525         /**
25526          * @event click
25527          * The raw click event for the entire grid.
25528          * @param {Roo.EventObject} e
25529          */
25530         click : true
25531     });
25532 };
25533
25534 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25535     
25536     submenu : false,
25537     href : '',
25538     html : '',
25539     preventDefault: true,
25540     disable : false,
25541     icon : false,
25542     pos : 'right',
25543     
25544     getAutoCreate : function()
25545     {
25546         var text = [
25547             {
25548                 tag : 'span',
25549                 cls : 'roo-menu-item-text',
25550                 html : this.html
25551             }
25552         ];
25553         
25554         if(this.icon){
25555             text.unshift({
25556                 tag : 'i',
25557                 cls : 'fa ' + this.icon
25558             })
25559         }
25560         
25561         var cfg = {
25562             tag : 'li',
25563             cn : [
25564                 {
25565                     tag : 'a',
25566                     href : this.href || '#',
25567                     cn : text
25568                 }
25569             ]
25570         };
25571         
25572         if(this.disable){
25573             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25574         }
25575         
25576         if(this.submenu){
25577             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25578             
25579             if(this.pos == 'left'){
25580                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25581             }
25582         }
25583         
25584         return cfg;
25585     },
25586     
25587     initEvents : function() 
25588     {
25589         this.el.on('mouseover', this.onMouseOver, this);
25590         this.el.on('mouseout', this.onMouseOut, this);
25591         
25592         this.el.select('a', true).first().on('click', this.onClick, this);
25593         
25594     },
25595     
25596     onClick : function(e)
25597     {
25598         if(this.preventDefault){
25599             e.preventDefault();
25600         }
25601         
25602         this.fireEvent("click", this, e);
25603     },
25604     
25605     onMouseOver : function(e)
25606     {
25607         if(this.submenu && this.pos == 'left'){
25608             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25609         }
25610         
25611         this.fireEvent("mouseover", this, e);
25612     },
25613     
25614     onMouseOut : function(e)
25615     {
25616         this.fireEvent("mouseout", this, e);
25617     }
25618 });
25619
25620  
25621
25622  /*
25623  * - LGPL
25624  *
25625  * menu separator
25626  * 
25627  */
25628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25629
25630 /**
25631  * @class Roo.bootstrap.menu.Separator
25632  * @extends Roo.bootstrap.Component
25633  * Bootstrap Separator class
25634  * 
25635  * @constructor
25636  * Create a new Separator
25637  * @param {Object} config The config object
25638  */
25639
25640
25641 Roo.bootstrap.menu.Separator = function(config){
25642     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25643 };
25644
25645 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25646     
25647     getAutoCreate : function(){
25648         var cfg = {
25649             tag : 'li',
25650             cls: 'divider'
25651         };
25652         
25653         return cfg;
25654     }
25655    
25656 });
25657
25658  
25659
25660  /*
25661  * - LGPL
25662  *
25663  * Tooltip
25664  * 
25665  */
25666
25667 /**
25668  * @class Roo.bootstrap.Tooltip
25669  * Bootstrap Tooltip class
25670  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25671  * to determine which dom element triggers the tooltip.
25672  * 
25673  * It needs to add support for additional attributes like tooltip-position
25674  * 
25675  * @constructor
25676  * Create a new Toolti
25677  * @param {Object} config The config object
25678  */
25679
25680 Roo.bootstrap.Tooltip = function(config){
25681     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25682     
25683     this.alignment = Roo.bootstrap.Tooltip.alignment;
25684     
25685     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25686         this.alignment = config.alignment;
25687     }
25688     
25689 };
25690
25691 Roo.apply(Roo.bootstrap.Tooltip, {
25692     /**
25693      * @function init initialize tooltip monitoring.
25694      * @static
25695      */
25696     currentEl : false,
25697     currentTip : false,
25698     currentRegion : false,
25699     
25700     //  init : delay?
25701     
25702     init : function()
25703     {
25704         Roo.get(document).on('mouseover', this.enter ,this);
25705         Roo.get(document).on('mouseout', this.leave, this);
25706          
25707         
25708         this.currentTip = new Roo.bootstrap.Tooltip();
25709     },
25710     
25711     enter : function(ev)
25712     {
25713         var dom = ev.getTarget();
25714         
25715         //Roo.log(['enter',dom]);
25716         var el = Roo.fly(dom);
25717         if (this.currentEl) {
25718             //Roo.log(dom);
25719             //Roo.log(this.currentEl);
25720             //Roo.log(this.currentEl.contains(dom));
25721             if (this.currentEl == el) {
25722                 return;
25723             }
25724             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25725                 return;
25726             }
25727
25728         }
25729         
25730         if (this.currentTip.el) {
25731             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25732         }    
25733         //Roo.log(ev);
25734         
25735         if(!el || el.dom == document){
25736             return;
25737         }
25738         
25739         var bindEl = el;
25740         
25741         // you can not look for children, as if el is the body.. then everythign is the child..
25742         if (!el.attr('tooltip')) { //
25743             if (!el.select("[tooltip]").elements.length) {
25744                 return;
25745             }
25746             // is the mouse over this child...?
25747             bindEl = el.select("[tooltip]").first();
25748             var xy = ev.getXY();
25749             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25750                 //Roo.log("not in region.");
25751                 return;
25752             }
25753             //Roo.log("child element over..");
25754             
25755         }
25756         this.currentEl = bindEl;
25757         this.currentTip.bind(bindEl);
25758         this.currentRegion = Roo.lib.Region.getRegion(dom);
25759         this.currentTip.enter();
25760         
25761     },
25762     leave : function(ev)
25763     {
25764         var dom = ev.getTarget();
25765         //Roo.log(['leave',dom]);
25766         if (!this.currentEl) {
25767             return;
25768         }
25769         
25770         
25771         if (dom != this.currentEl.dom) {
25772             return;
25773         }
25774         var xy = ev.getXY();
25775         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25776             return;
25777         }
25778         // only activate leave if mouse cursor is outside... bounding box..
25779         
25780         
25781         
25782         
25783         if (this.currentTip) {
25784             this.currentTip.leave();
25785         }
25786         //Roo.log('clear currentEl');
25787         this.currentEl = false;
25788         
25789         
25790     },
25791     alignment : {
25792         'left' : ['r-l', [-2,0], 'right'],
25793         'right' : ['l-r', [2,0], 'left'],
25794         'bottom' : ['t-b', [0,2], 'top'],
25795         'top' : [ 'b-t', [0,-2], 'bottom']
25796     }
25797     
25798 });
25799
25800
25801 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25802     
25803     
25804     bindEl : false,
25805     
25806     delay : null, // can be { show : 300 , hide: 500}
25807     
25808     timeout : null,
25809     
25810     hoverState : null, //???
25811     
25812     placement : 'bottom', 
25813     
25814     alignment : false,
25815     
25816     getAutoCreate : function(){
25817     
25818         var cfg = {
25819            cls : 'tooltip',
25820            role : 'tooltip',
25821            cn : [
25822                 {
25823                     cls : 'tooltip-arrow'
25824                 },
25825                 {
25826                     cls : 'tooltip-inner'
25827                 }
25828            ]
25829         };
25830         
25831         return cfg;
25832     },
25833     bind : function(el)
25834     {
25835         this.bindEl = el;
25836     },
25837       
25838     
25839     enter : function () {
25840        
25841         if (this.timeout != null) {
25842             clearTimeout(this.timeout);
25843         }
25844         
25845         this.hoverState = 'in';
25846          //Roo.log("enter - show");
25847         if (!this.delay || !this.delay.show) {
25848             this.show();
25849             return;
25850         }
25851         var _t = this;
25852         this.timeout = setTimeout(function () {
25853             if (_t.hoverState == 'in') {
25854                 _t.show();
25855             }
25856         }, this.delay.show);
25857     },
25858     leave : function()
25859     {
25860         clearTimeout(this.timeout);
25861     
25862         this.hoverState = 'out';
25863          if (!this.delay || !this.delay.hide) {
25864             this.hide();
25865             return;
25866         }
25867        
25868         var _t = this;
25869         this.timeout = setTimeout(function () {
25870             //Roo.log("leave - timeout");
25871             
25872             if (_t.hoverState == 'out') {
25873                 _t.hide();
25874                 Roo.bootstrap.Tooltip.currentEl = false;
25875             }
25876         }, delay);
25877     },
25878     
25879     show : function (msg)
25880     {
25881         if (!this.el) {
25882             this.render(document.body);
25883         }
25884         // set content.
25885         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25886         
25887         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25888         
25889         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25890         
25891         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25892         
25893         var placement = typeof this.placement == 'function' ?
25894             this.placement.call(this, this.el, on_el) :
25895             this.placement;
25896             
25897         var autoToken = /\s?auto?\s?/i;
25898         var autoPlace = autoToken.test(placement);
25899         if (autoPlace) {
25900             placement = placement.replace(autoToken, '') || 'top';
25901         }
25902         
25903         //this.el.detach()
25904         //this.el.setXY([0,0]);
25905         this.el.show();
25906         //this.el.dom.style.display='block';
25907         
25908         //this.el.appendTo(on_el);
25909         
25910         var p = this.getPosition();
25911         var box = this.el.getBox();
25912         
25913         if (autoPlace) {
25914             // fixme..
25915         }
25916         
25917         var align = this.alignment[placement];
25918         
25919         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25920         
25921         if(placement == 'top' || placement == 'bottom'){
25922             if(xy[0] < 0){
25923                 placement = 'right';
25924             }
25925             
25926             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25927                 placement = 'left';
25928             }
25929             
25930             var scroll = Roo.select('body', true).first().getScroll();
25931             
25932             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25933                 placement = 'top';
25934             }
25935             
25936         }
25937         
25938         this.el.alignTo(this.bindEl, align[0],align[1]);
25939         //var arrow = this.el.select('.arrow',true).first();
25940         //arrow.set(align[2], 
25941         
25942         this.el.addClass(placement);
25943         
25944         this.el.addClass('in fade');
25945         
25946         this.hoverState = null;
25947         
25948         if (this.el.hasClass('fade')) {
25949             // fade it?
25950         }
25951         
25952     },
25953     hide : function()
25954     {
25955          
25956         if (!this.el) {
25957             return;
25958         }
25959         //this.el.setXY([0,0]);
25960         this.el.removeClass('in');
25961         //this.el.hide();
25962         
25963     }
25964     
25965 });
25966  
25967
25968  /*
25969  * - LGPL
25970  *
25971  * Location Picker
25972  * 
25973  */
25974
25975 /**
25976  * @class Roo.bootstrap.LocationPicker
25977  * @extends Roo.bootstrap.Component
25978  * Bootstrap LocationPicker class
25979  * @cfg {Number} latitude Position when init default 0
25980  * @cfg {Number} longitude Position when init default 0
25981  * @cfg {Number} zoom default 15
25982  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25983  * @cfg {Boolean} mapTypeControl default false
25984  * @cfg {Boolean} disableDoubleClickZoom default false
25985  * @cfg {Boolean} scrollwheel default true
25986  * @cfg {Boolean} streetViewControl default false
25987  * @cfg {Number} radius default 0
25988  * @cfg {String} locationName
25989  * @cfg {Boolean} draggable default true
25990  * @cfg {Boolean} enableAutocomplete default false
25991  * @cfg {Boolean} enableReverseGeocode default true
25992  * @cfg {String} markerTitle
25993  * 
25994  * @constructor
25995  * Create a new LocationPicker
25996  * @param {Object} config The config object
25997  */
25998
25999
26000 Roo.bootstrap.LocationPicker = function(config){
26001     
26002     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26003     
26004     this.addEvents({
26005         /**
26006          * @event initial
26007          * Fires when the picker initialized.
26008          * @param {Roo.bootstrap.LocationPicker} this
26009          * @param {Google Location} location
26010          */
26011         initial : true,
26012         /**
26013          * @event positionchanged
26014          * Fires when the picker position changed.
26015          * @param {Roo.bootstrap.LocationPicker} this
26016          * @param {Google Location} location
26017          */
26018         positionchanged : true,
26019         /**
26020          * @event resize
26021          * Fires when the map resize.
26022          * @param {Roo.bootstrap.LocationPicker} this
26023          */
26024         resize : true,
26025         /**
26026          * @event show
26027          * Fires when the map show.
26028          * @param {Roo.bootstrap.LocationPicker} this
26029          */
26030         show : true,
26031         /**
26032          * @event hide
26033          * Fires when the map hide.
26034          * @param {Roo.bootstrap.LocationPicker} this
26035          */
26036         hide : true,
26037         /**
26038          * @event mapClick
26039          * Fires when click the map.
26040          * @param {Roo.bootstrap.LocationPicker} this
26041          * @param {Map event} e
26042          */
26043         mapClick : true,
26044         /**
26045          * @event mapRightClick
26046          * Fires when right click the map.
26047          * @param {Roo.bootstrap.LocationPicker} this
26048          * @param {Map event} e
26049          */
26050         mapRightClick : true,
26051         /**
26052          * @event markerClick
26053          * Fires when click the marker.
26054          * @param {Roo.bootstrap.LocationPicker} this
26055          * @param {Map event} e
26056          */
26057         markerClick : true,
26058         /**
26059          * @event markerRightClick
26060          * Fires when right click the marker.
26061          * @param {Roo.bootstrap.LocationPicker} this
26062          * @param {Map event} e
26063          */
26064         markerRightClick : true,
26065         /**
26066          * @event OverlayViewDraw
26067          * Fires when OverlayView Draw
26068          * @param {Roo.bootstrap.LocationPicker} this
26069          */
26070         OverlayViewDraw : true,
26071         /**
26072          * @event OverlayViewOnAdd
26073          * Fires when OverlayView Draw
26074          * @param {Roo.bootstrap.LocationPicker} this
26075          */
26076         OverlayViewOnAdd : true,
26077         /**
26078          * @event OverlayViewOnRemove
26079          * Fires when OverlayView Draw
26080          * @param {Roo.bootstrap.LocationPicker} this
26081          */
26082         OverlayViewOnRemove : true,
26083         /**
26084          * @event OverlayViewShow
26085          * Fires when OverlayView Draw
26086          * @param {Roo.bootstrap.LocationPicker} this
26087          * @param {Pixel} cpx
26088          */
26089         OverlayViewShow : true,
26090         /**
26091          * @event OverlayViewHide
26092          * Fires when OverlayView Draw
26093          * @param {Roo.bootstrap.LocationPicker} this
26094          */
26095         OverlayViewHide : true,
26096         /**
26097          * @event loadexception
26098          * Fires when load google lib failed.
26099          * @param {Roo.bootstrap.LocationPicker} this
26100          */
26101         loadexception : true
26102     });
26103         
26104 };
26105
26106 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26107     
26108     gMapContext: false,
26109     
26110     latitude: 0,
26111     longitude: 0,
26112     zoom: 15,
26113     mapTypeId: false,
26114     mapTypeControl: false,
26115     disableDoubleClickZoom: false,
26116     scrollwheel: true,
26117     streetViewControl: false,
26118     radius: 0,
26119     locationName: '',
26120     draggable: true,
26121     enableAutocomplete: false,
26122     enableReverseGeocode: true,
26123     markerTitle: '',
26124     
26125     getAutoCreate: function()
26126     {
26127
26128         var cfg = {
26129             tag: 'div',
26130             cls: 'roo-location-picker'
26131         };
26132         
26133         return cfg
26134     },
26135     
26136     initEvents: function(ct, position)
26137     {       
26138         if(!this.el.getWidth() || this.isApplied()){
26139             return;
26140         }
26141         
26142         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26143         
26144         this.initial();
26145     },
26146     
26147     initial: function()
26148     {
26149         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26150             this.fireEvent('loadexception', this);
26151             return;
26152         }
26153         
26154         if(!this.mapTypeId){
26155             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26156         }
26157         
26158         this.gMapContext = this.GMapContext();
26159         
26160         this.initOverlayView();
26161         
26162         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26163         
26164         var _this = this;
26165                 
26166         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26167             _this.setPosition(_this.gMapContext.marker.position);
26168         });
26169         
26170         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26171             _this.fireEvent('mapClick', this, event);
26172             
26173         });
26174
26175         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26176             _this.fireEvent('mapRightClick', this, event);
26177             
26178         });
26179         
26180         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26181             _this.fireEvent('markerClick', this, event);
26182             
26183         });
26184
26185         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26186             _this.fireEvent('markerRightClick', this, event);
26187             
26188         });
26189         
26190         this.setPosition(this.gMapContext.location);
26191         
26192         this.fireEvent('initial', this, this.gMapContext.location);
26193     },
26194     
26195     initOverlayView: function()
26196     {
26197         var _this = this;
26198         
26199         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26200             
26201             draw: function()
26202             {
26203                 _this.fireEvent('OverlayViewDraw', _this);
26204             },
26205             
26206             onAdd: function()
26207             {
26208                 _this.fireEvent('OverlayViewOnAdd', _this);
26209             },
26210             
26211             onRemove: function()
26212             {
26213                 _this.fireEvent('OverlayViewOnRemove', _this);
26214             },
26215             
26216             show: function(cpx)
26217             {
26218                 _this.fireEvent('OverlayViewShow', _this, cpx);
26219             },
26220             
26221             hide: function()
26222             {
26223                 _this.fireEvent('OverlayViewHide', _this);
26224             }
26225             
26226         });
26227     },
26228     
26229     fromLatLngToContainerPixel: function(event)
26230     {
26231         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26232     },
26233     
26234     isApplied: function() 
26235     {
26236         return this.getGmapContext() == false ? false : true;
26237     },
26238     
26239     getGmapContext: function() 
26240     {
26241         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26242     },
26243     
26244     GMapContext: function() 
26245     {
26246         var position = new google.maps.LatLng(this.latitude, this.longitude);
26247         
26248         var _map = new google.maps.Map(this.el.dom, {
26249             center: position,
26250             zoom: this.zoom,
26251             mapTypeId: this.mapTypeId,
26252             mapTypeControl: this.mapTypeControl,
26253             disableDoubleClickZoom: this.disableDoubleClickZoom,
26254             scrollwheel: this.scrollwheel,
26255             streetViewControl: this.streetViewControl,
26256             locationName: this.locationName,
26257             draggable: this.draggable,
26258             enableAutocomplete: this.enableAutocomplete,
26259             enableReverseGeocode: this.enableReverseGeocode
26260         });
26261         
26262         var _marker = new google.maps.Marker({
26263             position: position,
26264             map: _map,
26265             title: this.markerTitle,
26266             draggable: this.draggable
26267         });
26268         
26269         return {
26270             map: _map,
26271             marker: _marker,
26272             circle: null,
26273             location: position,
26274             radius: this.radius,
26275             locationName: this.locationName,
26276             addressComponents: {
26277                 formatted_address: null,
26278                 addressLine1: null,
26279                 addressLine2: null,
26280                 streetName: null,
26281                 streetNumber: null,
26282                 city: null,
26283                 district: null,
26284                 state: null,
26285                 stateOrProvince: null
26286             },
26287             settings: this,
26288             domContainer: this.el.dom,
26289             geodecoder: new google.maps.Geocoder()
26290         };
26291     },
26292     
26293     drawCircle: function(center, radius, options) 
26294     {
26295         if (this.gMapContext.circle != null) {
26296             this.gMapContext.circle.setMap(null);
26297         }
26298         if (radius > 0) {
26299             radius *= 1;
26300             options = Roo.apply({}, options, {
26301                 strokeColor: "#0000FF",
26302                 strokeOpacity: .35,
26303                 strokeWeight: 2,
26304                 fillColor: "#0000FF",
26305                 fillOpacity: .2
26306             });
26307             
26308             options.map = this.gMapContext.map;
26309             options.radius = radius;
26310             options.center = center;
26311             this.gMapContext.circle = new google.maps.Circle(options);
26312             return this.gMapContext.circle;
26313         }
26314         
26315         return null;
26316     },
26317     
26318     setPosition: function(location) 
26319     {
26320         this.gMapContext.location = location;
26321         this.gMapContext.marker.setPosition(location);
26322         this.gMapContext.map.panTo(location);
26323         this.drawCircle(location, this.gMapContext.radius, {});
26324         
26325         var _this = this;
26326         
26327         if (this.gMapContext.settings.enableReverseGeocode) {
26328             this.gMapContext.geodecoder.geocode({
26329                 latLng: this.gMapContext.location
26330             }, function(results, status) {
26331                 
26332                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26333                     _this.gMapContext.locationName = results[0].formatted_address;
26334                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26335                     
26336                     _this.fireEvent('positionchanged', this, location);
26337                 }
26338             });
26339             
26340             return;
26341         }
26342         
26343         this.fireEvent('positionchanged', this, location);
26344     },
26345     
26346     resize: function()
26347     {
26348         google.maps.event.trigger(this.gMapContext.map, "resize");
26349         
26350         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26351         
26352         this.fireEvent('resize', this);
26353     },
26354     
26355     setPositionByLatLng: function(latitude, longitude)
26356     {
26357         this.setPosition(new google.maps.LatLng(latitude, longitude));
26358     },
26359     
26360     getCurrentPosition: function() 
26361     {
26362         return {
26363             latitude: this.gMapContext.location.lat(),
26364             longitude: this.gMapContext.location.lng()
26365         };
26366     },
26367     
26368     getAddressName: function() 
26369     {
26370         return this.gMapContext.locationName;
26371     },
26372     
26373     getAddressComponents: function() 
26374     {
26375         return this.gMapContext.addressComponents;
26376     },
26377     
26378     address_component_from_google_geocode: function(address_components) 
26379     {
26380         var result = {};
26381         
26382         for (var i = 0; i < address_components.length; i++) {
26383             var component = address_components[i];
26384             if (component.types.indexOf("postal_code") >= 0) {
26385                 result.postalCode = component.short_name;
26386             } else if (component.types.indexOf("street_number") >= 0) {
26387                 result.streetNumber = component.short_name;
26388             } else if (component.types.indexOf("route") >= 0) {
26389                 result.streetName = component.short_name;
26390             } else if (component.types.indexOf("neighborhood") >= 0) {
26391                 result.city = component.short_name;
26392             } else if (component.types.indexOf("locality") >= 0) {
26393                 result.city = component.short_name;
26394             } else if (component.types.indexOf("sublocality") >= 0) {
26395                 result.district = component.short_name;
26396             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26397                 result.stateOrProvince = component.short_name;
26398             } else if (component.types.indexOf("country") >= 0) {
26399                 result.country = component.short_name;
26400             }
26401         }
26402         
26403         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26404         result.addressLine2 = "";
26405         return result;
26406     },
26407     
26408     setZoomLevel: function(zoom)
26409     {
26410         this.gMapContext.map.setZoom(zoom);
26411     },
26412     
26413     show: function()
26414     {
26415         if(!this.el){
26416             return;
26417         }
26418         
26419         this.el.show();
26420         
26421         this.resize();
26422         
26423         this.fireEvent('show', this);
26424     },
26425     
26426     hide: function()
26427     {
26428         if(!this.el){
26429             return;
26430         }
26431         
26432         this.el.hide();
26433         
26434         this.fireEvent('hide', this);
26435     }
26436     
26437 });
26438
26439 Roo.apply(Roo.bootstrap.LocationPicker, {
26440     
26441     OverlayView : function(map, options)
26442     {
26443         options = options || {};
26444         
26445         this.setMap(map);
26446     }
26447     
26448     
26449 });/*
26450  * - LGPL
26451  *
26452  * Alert
26453  * 
26454  */
26455
26456 /**
26457  * @class Roo.bootstrap.Alert
26458  * @extends Roo.bootstrap.Component
26459  * Bootstrap Alert class
26460  * @cfg {String} title The title of alert
26461  * @cfg {String} html The content of alert
26462  * @cfg {String} weight (  success | info | warning | danger )
26463  * @cfg {String} faicon font-awesomeicon
26464  * 
26465  * @constructor
26466  * Create a new alert
26467  * @param {Object} config The config object
26468  */
26469
26470
26471 Roo.bootstrap.Alert = function(config){
26472     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26473     
26474 };
26475
26476 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26477     
26478     title: '',
26479     html: '',
26480     weight: false,
26481     faicon: false,
26482     
26483     getAutoCreate : function()
26484     {
26485         
26486         var cfg = {
26487             tag : 'div',
26488             cls : 'alert',
26489             cn : [
26490                 {
26491                     tag : 'i',
26492                     cls : 'roo-alert-icon'
26493                     
26494                 },
26495                 {
26496                     tag : 'b',
26497                     cls : 'roo-alert-title',
26498                     html : this.title
26499                 },
26500                 {
26501                     tag : 'span',
26502                     cls : 'roo-alert-text',
26503                     html : this.html
26504                 }
26505             ]
26506         };
26507         
26508         if(this.faicon){
26509             cfg.cn[0].cls += ' fa ' + this.faicon;
26510         }
26511         
26512         if(this.weight){
26513             cfg.cls += ' alert-' + this.weight;
26514         }
26515         
26516         return cfg;
26517     },
26518     
26519     initEvents: function() 
26520     {
26521         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26522     },
26523     
26524     setTitle : function(str)
26525     {
26526         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26527     },
26528     
26529     setText : function(str)
26530     {
26531         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26532     },
26533     
26534     setWeight : function(weight)
26535     {
26536         if(this.weight){
26537             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26538         }
26539         
26540         this.weight = weight;
26541         
26542         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26543     },
26544     
26545     setIcon : function(icon)
26546     {
26547         if(this.faicon){
26548             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26549         }
26550         
26551         this.faicon = icon;
26552         
26553         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26554     },
26555     
26556     hide: function() 
26557     {
26558         this.el.hide();   
26559     },
26560     
26561     show: function() 
26562     {  
26563         this.el.show();   
26564     }
26565     
26566 });
26567
26568  
26569 /*
26570 * Licence: LGPL
26571 */
26572
26573 /**
26574  * @class Roo.bootstrap.UploadCropbox
26575  * @extends Roo.bootstrap.Component
26576  * Bootstrap UploadCropbox class
26577  * @cfg {String} emptyText show when image has been loaded
26578  * @cfg {String} rotateNotify show when image too small to rotate
26579  * @cfg {Number} errorTimeout default 3000
26580  * @cfg {Number} minWidth default 300
26581  * @cfg {Number} minHeight default 300
26582  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26583  * @cfg {Boolean} isDocument (true|false) default false
26584  * @cfg {String} url action url
26585  * @cfg {String} paramName default 'imageUpload'
26586  * @cfg {String} method default POST
26587  * @cfg {Boolean} loadMask (true|false) default true
26588  * @cfg {Boolean} loadingText default 'Loading...'
26589  * 
26590  * @constructor
26591  * Create a new UploadCropbox
26592  * @param {Object} config The config object
26593  */
26594
26595 Roo.bootstrap.UploadCropbox = function(config){
26596     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26597     
26598     this.addEvents({
26599         /**
26600          * @event beforeselectfile
26601          * Fire before select file
26602          * @param {Roo.bootstrap.UploadCropbox} this
26603          */
26604         "beforeselectfile" : true,
26605         /**
26606          * @event initial
26607          * Fire after initEvent
26608          * @param {Roo.bootstrap.UploadCropbox} this
26609          */
26610         "initial" : true,
26611         /**
26612          * @event crop
26613          * Fire after initEvent
26614          * @param {Roo.bootstrap.UploadCropbox} this
26615          * @param {String} data
26616          */
26617         "crop" : true,
26618         /**
26619          * @event prepare
26620          * Fire when preparing the file data
26621          * @param {Roo.bootstrap.UploadCropbox} this
26622          * @param {Object} file
26623          */
26624         "prepare" : true,
26625         /**
26626          * @event exception
26627          * Fire when get exception
26628          * @param {Roo.bootstrap.UploadCropbox} this
26629          * @param {XMLHttpRequest} xhr
26630          */
26631         "exception" : true,
26632         /**
26633          * @event beforeloadcanvas
26634          * Fire before load the canvas
26635          * @param {Roo.bootstrap.UploadCropbox} this
26636          * @param {String} src
26637          */
26638         "beforeloadcanvas" : true,
26639         /**
26640          * @event trash
26641          * Fire when trash image
26642          * @param {Roo.bootstrap.UploadCropbox} this
26643          */
26644         "trash" : true,
26645         /**
26646          * @event download
26647          * Fire when download the image
26648          * @param {Roo.bootstrap.UploadCropbox} this
26649          */
26650         "download" : true,
26651         /**
26652          * @event footerbuttonclick
26653          * Fire when footerbuttonclick
26654          * @param {Roo.bootstrap.UploadCropbox} this
26655          * @param {String} type
26656          */
26657         "footerbuttonclick" : true,
26658         /**
26659          * @event resize
26660          * Fire when resize
26661          * @param {Roo.bootstrap.UploadCropbox} this
26662          */
26663         "resize" : true,
26664         /**
26665          * @event rotate
26666          * Fire when rotate the image
26667          * @param {Roo.bootstrap.UploadCropbox} this
26668          * @param {String} pos
26669          */
26670         "rotate" : true,
26671         /**
26672          * @event inspect
26673          * Fire when inspect the file
26674          * @param {Roo.bootstrap.UploadCropbox} this
26675          * @param {Object} file
26676          */
26677         "inspect" : true,
26678         /**
26679          * @event upload
26680          * Fire when xhr upload the file
26681          * @param {Roo.bootstrap.UploadCropbox} this
26682          * @param {Object} data
26683          */
26684         "upload" : true,
26685         /**
26686          * @event arrange
26687          * Fire when arrange the file data
26688          * @param {Roo.bootstrap.UploadCropbox} this
26689          * @param {Object} formData
26690          */
26691         "arrange" : true
26692     });
26693     
26694     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26695 };
26696
26697 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26698     
26699     emptyText : 'Click to upload image',
26700     rotateNotify : 'Image is too small to rotate',
26701     errorTimeout : 3000,
26702     scale : 0,
26703     baseScale : 1,
26704     rotate : 0,
26705     dragable : false,
26706     pinching : false,
26707     mouseX : 0,
26708     mouseY : 0,
26709     cropData : false,
26710     minWidth : 300,
26711     minHeight : 300,
26712     file : false,
26713     exif : {},
26714     baseRotate : 1,
26715     cropType : 'image/jpeg',
26716     buttons : false,
26717     canvasLoaded : false,
26718     isDocument : false,
26719     method : 'POST',
26720     paramName : 'imageUpload',
26721     loadMask : true,
26722     loadingText : 'Loading...',
26723     maskEl : false,
26724     
26725     getAutoCreate : function()
26726     {
26727         var cfg = {
26728             tag : 'div',
26729             cls : 'roo-upload-cropbox',
26730             cn : [
26731                 {
26732                     tag : 'input',
26733                     cls : 'roo-upload-cropbox-selector',
26734                     type : 'file'
26735                 },
26736                 {
26737                     tag : 'div',
26738                     cls : 'roo-upload-cropbox-body',
26739                     style : 'cursor:pointer',
26740                     cn : [
26741                         {
26742                             tag : 'div',
26743                             cls : 'roo-upload-cropbox-preview'
26744                         },
26745                         {
26746                             tag : 'div',
26747                             cls : 'roo-upload-cropbox-thumb'
26748                         },
26749                         {
26750                             tag : 'div',
26751                             cls : 'roo-upload-cropbox-empty-notify',
26752                             html : this.emptyText
26753                         },
26754                         {
26755                             tag : 'div',
26756                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26757                             html : this.rotateNotify
26758                         }
26759                     ]
26760                 },
26761                 {
26762                     tag : 'div',
26763                     cls : 'roo-upload-cropbox-footer',
26764                     cn : {
26765                         tag : 'div',
26766                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26767                         cn : []
26768                     }
26769                 }
26770             ]
26771         };
26772         
26773         return cfg;
26774     },
26775     
26776     onRender : function(ct, position)
26777     {
26778         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26779         
26780         if (this.buttons.length) {
26781             
26782             Roo.each(this.buttons, function(bb) {
26783                 
26784                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26785                 
26786                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26787                 
26788             }, this);
26789         }
26790         
26791         if(this.loadMask){
26792             this.maskEl = this.el;
26793         }
26794     },
26795     
26796     initEvents : function()
26797     {
26798         this.urlAPI = (window.createObjectURL && window) || 
26799                                 (window.URL && URL.revokeObjectURL && URL) || 
26800                                 (window.webkitURL && webkitURL);
26801                         
26802         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26803         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26804         
26805         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26806         this.selectorEl.hide();
26807         
26808         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26809         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26810         
26811         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26812         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26813         this.thumbEl.hide();
26814         
26815         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26816         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26817         
26818         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26819         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26820         this.errorEl.hide();
26821         
26822         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26823         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26824         this.footerEl.hide();
26825         
26826         this.setThumbBoxSize();
26827         
26828         this.bind();
26829         
26830         this.resize();
26831         
26832         this.fireEvent('initial', this);
26833     },
26834
26835     bind : function()
26836     {
26837         var _this = this;
26838         
26839         window.addEventListener("resize", function() { _this.resize(); } );
26840         
26841         this.bodyEl.on('click', this.beforeSelectFile, this);
26842         
26843         if(Roo.isTouch){
26844             this.bodyEl.on('touchstart', this.onTouchStart, this);
26845             this.bodyEl.on('touchmove', this.onTouchMove, this);
26846             this.bodyEl.on('touchend', this.onTouchEnd, this);
26847         }
26848         
26849         if(!Roo.isTouch){
26850             this.bodyEl.on('mousedown', this.onMouseDown, this);
26851             this.bodyEl.on('mousemove', this.onMouseMove, this);
26852             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26853             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26854             Roo.get(document).on('mouseup', this.onMouseUp, this);
26855         }
26856         
26857         this.selectorEl.on('change', this.onFileSelected, this);
26858     },
26859     
26860     reset : function()
26861     {    
26862         this.scale = 0;
26863         this.baseScale = 1;
26864         this.rotate = 0;
26865         this.baseRotate = 1;
26866         this.dragable = false;
26867         this.pinching = false;
26868         this.mouseX = 0;
26869         this.mouseY = 0;
26870         this.cropData = false;
26871         this.notifyEl.dom.innerHTML = this.emptyText;
26872         
26873         this.selectorEl.dom.value = '';
26874         
26875     },
26876     
26877     resize : function()
26878     {
26879         if(this.fireEvent('resize', this) != false){
26880             this.setThumbBoxPosition();
26881             this.setCanvasPosition();
26882         }
26883     },
26884     
26885     onFooterButtonClick : function(e, el, o, type)
26886     {
26887         switch (type) {
26888             case 'rotate-left' :
26889                 this.onRotateLeft(e);
26890                 break;
26891             case 'rotate-right' :
26892                 this.onRotateRight(e);
26893                 break;
26894             case 'picture' :
26895                 this.beforeSelectFile(e);
26896                 break;
26897             case 'trash' :
26898                 this.trash(e);
26899                 break;
26900             case 'crop' :
26901                 this.crop(e);
26902                 break;
26903             case 'download' :
26904                 this.download(e);
26905                 break;
26906             default :
26907                 break;
26908         }
26909         
26910         this.fireEvent('footerbuttonclick', this, type);
26911     },
26912     
26913     beforeSelectFile : function(e)
26914     {
26915         e.preventDefault();
26916         
26917         if(this.fireEvent('beforeselectfile', this) != false){
26918             this.selectorEl.dom.click();
26919         }
26920     },
26921     
26922     onFileSelected : function(e)
26923     {
26924         e.preventDefault();
26925         
26926         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26927             return;
26928         }
26929         
26930         var file = this.selectorEl.dom.files[0];
26931         
26932         if(this.fireEvent('inspect', this, file) != false){
26933             this.prepare(file);
26934         }
26935         
26936     },
26937     
26938     trash : function(e)
26939     {
26940         this.fireEvent('trash', this);
26941     },
26942     
26943     download : function(e)
26944     {
26945         this.fireEvent('download', this);
26946     },
26947     
26948     loadCanvas : function(src)
26949     {   
26950         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26951             
26952             this.reset();
26953             
26954             this.imageEl = document.createElement('img');
26955             
26956             var _this = this;
26957             
26958             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26959             
26960             this.imageEl.src = src;
26961         }
26962     },
26963     
26964     onLoadCanvas : function()
26965     {   
26966         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26967         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26968         
26969         this.bodyEl.un('click', this.beforeSelectFile, this);
26970         
26971         this.notifyEl.hide();
26972         this.thumbEl.show();
26973         this.footerEl.show();
26974         
26975         this.baseRotateLevel();
26976         
26977         if(this.isDocument){
26978             this.setThumbBoxSize();
26979         }
26980         
26981         this.setThumbBoxPosition();
26982         
26983         this.baseScaleLevel();
26984         
26985         this.draw();
26986         
26987         this.resize();
26988         
26989         this.canvasLoaded = true;
26990         
26991         if(this.loadMask){
26992             this.maskEl.unmask();
26993         }
26994         
26995     },
26996     
26997     setCanvasPosition : function()
26998     {   
26999         if(!this.canvasEl){
27000             return;
27001         }
27002         
27003         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27004         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27005         
27006         this.previewEl.setLeft(pw);
27007         this.previewEl.setTop(ph);
27008         
27009     },
27010     
27011     onMouseDown : function(e)
27012     {   
27013         e.stopEvent();
27014         
27015         this.dragable = true;
27016         this.pinching = false;
27017         
27018         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27019             this.dragable = false;
27020             return;
27021         }
27022         
27023         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27024         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27025         
27026     },
27027     
27028     onMouseMove : function(e)
27029     {   
27030         e.stopEvent();
27031         
27032         if(!this.canvasLoaded){
27033             return;
27034         }
27035         
27036         if (!this.dragable){
27037             return;
27038         }
27039         
27040         var minX = Math.ceil(this.thumbEl.getLeft(true));
27041         var minY = Math.ceil(this.thumbEl.getTop(true));
27042         
27043         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27044         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27045         
27046         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27047         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27048         
27049         x = x - this.mouseX;
27050         y = y - this.mouseY;
27051         
27052         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27053         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27054         
27055         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27056         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27057         
27058         this.previewEl.setLeft(bgX);
27059         this.previewEl.setTop(bgY);
27060         
27061         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27062         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27063     },
27064     
27065     onMouseUp : function(e)
27066     {   
27067         e.stopEvent();
27068         
27069         this.dragable = false;
27070     },
27071     
27072     onMouseWheel : function(e)
27073     {   
27074         e.stopEvent();
27075         
27076         this.startScale = this.scale;
27077         
27078         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27079         
27080         if(!this.zoomable()){
27081             this.scale = this.startScale;
27082             return;
27083         }
27084         
27085         this.draw();
27086         
27087         return;
27088     },
27089     
27090     zoomable : function()
27091     {
27092         var minScale = this.thumbEl.getWidth() / this.minWidth;
27093         
27094         if(this.minWidth < this.minHeight){
27095             minScale = this.thumbEl.getHeight() / this.minHeight;
27096         }
27097         
27098         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27099         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27100         
27101         if(
27102                 this.isDocument &&
27103                 (this.rotate == 0 || this.rotate == 180) && 
27104                 (
27105                     width > this.imageEl.OriginWidth || 
27106                     height > this.imageEl.OriginHeight ||
27107                     (width < this.minWidth && height < this.minHeight)
27108                 )
27109         ){
27110             return false;
27111         }
27112         
27113         if(
27114                 this.isDocument &&
27115                 (this.rotate == 90 || this.rotate == 270) && 
27116                 (
27117                     width > this.imageEl.OriginWidth || 
27118                     height > this.imageEl.OriginHeight ||
27119                     (width < this.minHeight && height < this.minWidth)
27120                 )
27121         ){
27122             return false;
27123         }
27124         
27125         if(
27126                 !this.isDocument &&
27127                 (this.rotate == 0 || this.rotate == 180) && 
27128                 (
27129                     width < this.minWidth || 
27130                     width > this.imageEl.OriginWidth || 
27131                     height < this.minHeight || 
27132                     height > this.imageEl.OriginHeight
27133                 )
27134         ){
27135             return false;
27136         }
27137         
27138         if(
27139                 !this.isDocument &&
27140                 (this.rotate == 90 || this.rotate == 270) && 
27141                 (
27142                     width < this.minHeight || 
27143                     width > this.imageEl.OriginWidth || 
27144                     height < this.minWidth || 
27145                     height > this.imageEl.OriginHeight
27146                 )
27147         ){
27148             return false;
27149         }
27150         
27151         return true;
27152         
27153     },
27154     
27155     onRotateLeft : function(e)
27156     {   
27157         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27158             
27159             var minScale = this.thumbEl.getWidth() / this.minWidth;
27160             
27161             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27162             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27163             
27164             this.startScale = this.scale;
27165             
27166             while (this.getScaleLevel() < minScale){
27167             
27168                 this.scale = this.scale + 1;
27169                 
27170                 if(!this.zoomable()){
27171                     break;
27172                 }
27173                 
27174                 if(
27175                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27176                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27177                 ){
27178                     continue;
27179                 }
27180                 
27181                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27182
27183                 this.draw();
27184                 
27185                 return;
27186             }
27187             
27188             this.scale = this.startScale;
27189             
27190             this.onRotateFail();
27191             
27192             return false;
27193         }
27194         
27195         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27196
27197         if(this.isDocument){
27198             this.setThumbBoxSize();
27199             this.setThumbBoxPosition();
27200             this.setCanvasPosition();
27201         }
27202         
27203         this.draw();
27204         
27205         this.fireEvent('rotate', this, 'left');
27206         
27207     },
27208     
27209     onRotateRight : function(e)
27210     {
27211         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27212             
27213             var minScale = this.thumbEl.getWidth() / this.minWidth;
27214         
27215             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27216             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27217             
27218             this.startScale = this.scale;
27219             
27220             while (this.getScaleLevel() < minScale){
27221             
27222                 this.scale = this.scale + 1;
27223                 
27224                 if(!this.zoomable()){
27225                     break;
27226                 }
27227                 
27228                 if(
27229                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27230                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27231                 ){
27232                     continue;
27233                 }
27234                 
27235                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27236
27237                 this.draw();
27238                 
27239                 return;
27240             }
27241             
27242             this.scale = this.startScale;
27243             
27244             this.onRotateFail();
27245             
27246             return false;
27247         }
27248         
27249         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27250
27251         if(this.isDocument){
27252             this.setThumbBoxSize();
27253             this.setThumbBoxPosition();
27254             this.setCanvasPosition();
27255         }
27256         
27257         this.draw();
27258         
27259         this.fireEvent('rotate', this, 'right');
27260     },
27261     
27262     onRotateFail : function()
27263     {
27264         this.errorEl.show(true);
27265         
27266         var _this = this;
27267         
27268         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27269     },
27270     
27271     draw : function()
27272     {
27273         this.previewEl.dom.innerHTML = '';
27274         
27275         var canvasEl = document.createElement("canvas");
27276         
27277         var contextEl = canvasEl.getContext("2d");
27278         
27279         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27280         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27281         var center = this.imageEl.OriginWidth / 2;
27282         
27283         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27284             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27285             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27286             center = this.imageEl.OriginHeight / 2;
27287         }
27288         
27289         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27290         
27291         contextEl.translate(center, center);
27292         contextEl.rotate(this.rotate * Math.PI / 180);
27293
27294         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27295         
27296         this.canvasEl = document.createElement("canvas");
27297         
27298         this.contextEl = this.canvasEl.getContext("2d");
27299         
27300         switch (this.rotate) {
27301             case 0 :
27302                 
27303                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27304                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27305                 
27306                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27307                 
27308                 break;
27309             case 90 : 
27310                 
27311                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27312                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27313                 
27314                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27315                     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);
27316                     break;
27317                 }
27318                 
27319                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27320                 
27321                 break;
27322             case 180 :
27323                 
27324                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27325                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27326                 
27327                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27328                     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);
27329                     break;
27330                 }
27331                 
27332                 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);
27333                 
27334                 break;
27335             case 270 :
27336                 
27337                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27338                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27339         
27340                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27341                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27342                     break;
27343                 }
27344                 
27345                 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);
27346                 
27347                 break;
27348             default : 
27349                 break;
27350         }
27351         
27352         this.previewEl.appendChild(this.canvasEl);
27353         
27354         this.setCanvasPosition();
27355     },
27356     
27357     crop : function()
27358     {
27359         if(!this.canvasLoaded){
27360             return;
27361         }
27362         
27363         var imageCanvas = document.createElement("canvas");
27364         
27365         var imageContext = imageCanvas.getContext("2d");
27366         
27367         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27368         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27369         
27370         var center = imageCanvas.width / 2;
27371         
27372         imageContext.translate(center, center);
27373         
27374         imageContext.rotate(this.rotate * Math.PI / 180);
27375         
27376         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27377         
27378         var canvas = document.createElement("canvas");
27379         
27380         var context = canvas.getContext("2d");
27381                 
27382         canvas.width = this.minWidth;
27383         canvas.height = this.minHeight;
27384
27385         switch (this.rotate) {
27386             case 0 :
27387                 
27388                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27389                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27390                 
27391                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27392                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27393                 
27394                 var targetWidth = this.minWidth - 2 * x;
27395                 var targetHeight = this.minHeight - 2 * y;
27396                 
27397                 var scale = 1;
27398                 
27399                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27400                     scale = targetWidth / width;
27401                 }
27402                 
27403                 if(x > 0 && y == 0){
27404                     scale = targetHeight / height;
27405                 }
27406                 
27407                 if(x > 0 && y > 0){
27408                     scale = targetWidth / width;
27409                     
27410                     if(width < height){
27411                         scale = targetHeight / height;
27412                     }
27413                 }
27414                 
27415                 context.scale(scale, scale);
27416                 
27417                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27418                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27419
27420                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27421                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27422
27423                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27424                 
27425                 break;
27426             case 90 : 
27427                 
27428                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27429                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27430                 
27431                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27432                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27433                 
27434                 var targetWidth = this.minWidth - 2 * x;
27435                 var targetHeight = this.minHeight - 2 * y;
27436                 
27437                 var scale = 1;
27438                 
27439                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27440                     scale = targetWidth / width;
27441                 }
27442                 
27443                 if(x > 0 && y == 0){
27444                     scale = targetHeight / height;
27445                 }
27446                 
27447                 if(x > 0 && y > 0){
27448                     scale = targetWidth / width;
27449                     
27450                     if(width < height){
27451                         scale = targetHeight / height;
27452                     }
27453                 }
27454                 
27455                 context.scale(scale, scale);
27456                 
27457                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27458                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27459
27460                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27461                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27462                 
27463                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27464                 
27465                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27466                 
27467                 break;
27468             case 180 :
27469                 
27470                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27471                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27472                 
27473                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27474                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27475                 
27476                 var targetWidth = this.minWidth - 2 * x;
27477                 var targetHeight = this.minHeight - 2 * y;
27478                 
27479                 var scale = 1;
27480                 
27481                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27482                     scale = targetWidth / width;
27483                 }
27484                 
27485                 if(x > 0 && y == 0){
27486                     scale = targetHeight / height;
27487                 }
27488                 
27489                 if(x > 0 && y > 0){
27490                     scale = targetWidth / width;
27491                     
27492                     if(width < height){
27493                         scale = targetHeight / height;
27494                     }
27495                 }
27496                 
27497                 context.scale(scale, scale);
27498                 
27499                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27500                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27501
27502                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27503                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27504
27505                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27506                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27507                 
27508                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27509                 
27510                 break;
27511             case 270 :
27512                 
27513                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27514                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27515                 
27516                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27517                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27518                 
27519                 var targetWidth = this.minWidth - 2 * x;
27520                 var targetHeight = this.minHeight - 2 * y;
27521                 
27522                 var scale = 1;
27523                 
27524                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27525                     scale = targetWidth / width;
27526                 }
27527                 
27528                 if(x > 0 && y == 0){
27529                     scale = targetHeight / height;
27530                 }
27531                 
27532                 if(x > 0 && y > 0){
27533                     scale = targetWidth / width;
27534                     
27535                     if(width < height){
27536                         scale = targetHeight / height;
27537                     }
27538                 }
27539                 
27540                 context.scale(scale, scale);
27541                 
27542                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27543                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27544
27545                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27546                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27547                 
27548                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27549                 
27550                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27551                 
27552                 break;
27553             default : 
27554                 break;
27555         }
27556         
27557         this.cropData = canvas.toDataURL(this.cropType);
27558         
27559         if(this.fireEvent('crop', this, this.cropData) !== false){
27560             this.process(this.file, this.cropData);
27561         }
27562         
27563         return;
27564         
27565     },
27566     
27567     setThumbBoxSize : function()
27568     {
27569         var width, height;
27570         
27571         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27572             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27573             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27574             
27575             this.minWidth = width;
27576             this.minHeight = height;
27577             
27578             if(this.rotate == 90 || this.rotate == 270){
27579                 this.minWidth = height;
27580                 this.minHeight = width;
27581             }
27582         }
27583         
27584         height = 300;
27585         width = Math.ceil(this.minWidth * height / this.minHeight);
27586         
27587         if(this.minWidth > this.minHeight){
27588             width = 300;
27589             height = Math.ceil(this.minHeight * width / this.minWidth);
27590         }
27591         
27592         this.thumbEl.setStyle({
27593             width : width + 'px',
27594             height : height + 'px'
27595         });
27596
27597         return;
27598             
27599     },
27600     
27601     setThumbBoxPosition : function()
27602     {
27603         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27604         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27605         
27606         this.thumbEl.setLeft(x);
27607         this.thumbEl.setTop(y);
27608         
27609     },
27610     
27611     baseRotateLevel : function()
27612     {
27613         this.baseRotate = 1;
27614         
27615         if(
27616                 typeof(this.exif) != 'undefined' &&
27617                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27618                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27619         ){
27620             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27621         }
27622         
27623         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27624         
27625     },
27626     
27627     baseScaleLevel : function()
27628     {
27629         var width, height;
27630         
27631         if(this.isDocument){
27632             
27633             if(this.baseRotate == 6 || this.baseRotate == 8){
27634             
27635                 height = this.thumbEl.getHeight();
27636                 this.baseScale = height / this.imageEl.OriginWidth;
27637
27638                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27639                     width = this.thumbEl.getWidth();
27640                     this.baseScale = width / this.imageEl.OriginHeight;
27641                 }
27642
27643                 return;
27644             }
27645
27646             height = this.thumbEl.getHeight();
27647             this.baseScale = height / this.imageEl.OriginHeight;
27648
27649             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27650                 width = this.thumbEl.getWidth();
27651                 this.baseScale = width / this.imageEl.OriginWidth;
27652             }
27653
27654             return;
27655         }
27656         
27657         if(this.baseRotate == 6 || this.baseRotate == 8){
27658             
27659             width = this.thumbEl.getHeight();
27660             this.baseScale = width / this.imageEl.OriginHeight;
27661             
27662             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27663                 height = this.thumbEl.getWidth();
27664                 this.baseScale = height / this.imageEl.OriginHeight;
27665             }
27666             
27667             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27668                 height = this.thumbEl.getWidth();
27669                 this.baseScale = height / this.imageEl.OriginHeight;
27670                 
27671                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27672                     width = this.thumbEl.getHeight();
27673                     this.baseScale = width / this.imageEl.OriginWidth;
27674                 }
27675             }
27676             
27677             return;
27678         }
27679         
27680         width = this.thumbEl.getWidth();
27681         this.baseScale = width / this.imageEl.OriginWidth;
27682         
27683         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27684             height = this.thumbEl.getHeight();
27685             this.baseScale = height / this.imageEl.OriginHeight;
27686         }
27687         
27688         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27689             
27690             height = this.thumbEl.getHeight();
27691             this.baseScale = height / this.imageEl.OriginHeight;
27692             
27693             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27694                 width = this.thumbEl.getWidth();
27695                 this.baseScale = width / this.imageEl.OriginWidth;
27696             }
27697             
27698         }
27699         
27700         return;
27701     },
27702     
27703     getScaleLevel : function()
27704     {
27705         return this.baseScale * Math.pow(1.1, this.scale);
27706     },
27707     
27708     onTouchStart : function(e)
27709     {
27710         if(!this.canvasLoaded){
27711             this.beforeSelectFile(e);
27712             return;
27713         }
27714         
27715         var touches = e.browserEvent.touches;
27716         
27717         if(!touches){
27718             return;
27719         }
27720         
27721         if(touches.length == 1){
27722             this.onMouseDown(e);
27723             return;
27724         }
27725         
27726         if(touches.length != 2){
27727             return;
27728         }
27729         
27730         var coords = [];
27731         
27732         for(var i = 0, finger; finger = touches[i]; i++){
27733             coords.push(finger.pageX, finger.pageY);
27734         }
27735         
27736         var x = Math.pow(coords[0] - coords[2], 2);
27737         var y = Math.pow(coords[1] - coords[3], 2);
27738         
27739         this.startDistance = Math.sqrt(x + y);
27740         
27741         this.startScale = this.scale;
27742         
27743         this.pinching = true;
27744         this.dragable = false;
27745         
27746     },
27747     
27748     onTouchMove : function(e)
27749     {
27750         if(!this.pinching && !this.dragable){
27751             return;
27752         }
27753         
27754         var touches = e.browserEvent.touches;
27755         
27756         if(!touches){
27757             return;
27758         }
27759         
27760         if(this.dragable){
27761             this.onMouseMove(e);
27762             return;
27763         }
27764         
27765         var coords = [];
27766         
27767         for(var i = 0, finger; finger = touches[i]; i++){
27768             coords.push(finger.pageX, finger.pageY);
27769         }
27770         
27771         var x = Math.pow(coords[0] - coords[2], 2);
27772         var y = Math.pow(coords[1] - coords[3], 2);
27773         
27774         this.endDistance = Math.sqrt(x + y);
27775         
27776         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27777         
27778         if(!this.zoomable()){
27779             this.scale = this.startScale;
27780             return;
27781         }
27782         
27783         this.draw();
27784         
27785     },
27786     
27787     onTouchEnd : function(e)
27788     {
27789         this.pinching = false;
27790         this.dragable = false;
27791         
27792     },
27793     
27794     process : function(file, crop)
27795     {
27796         if(this.loadMask){
27797             this.maskEl.mask(this.loadingText);
27798         }
27799         
27800         this.xhr = new XMLHttpRequest();
27801         
27802         file.xhr = this.xhr;
27803
27804         this.xhr.open(this.method, this.url, true);
27805         
27806         var headers = {
27807             "Accept": "application/json",
27808             "Cache-Control": "no-cache",
27809             "X-Requested-With": "XMLHttpRequest"
27810         };
27811         
27812         for (var headerName in headers) {
27813             var headerValue = headers[headerName];
27814             if (headerValue) {
27815                 this.xhr.setRequestHeader(headerName, headerValue);
27816             }
27817         }
27818         
27819         var _this = this;
27820         
27821         this.xhr.onload = function()
27822         {
27823             _this.xhrOnLoad(_this.xhr);
27824         }
27825         
27826         this.xhr.onerror = function()
27827         {
27828             _this.xhrOnError(_this.xhr);
27829         }
27830         
27831         var formData = new FormData();
27832
27833         formData.append('returnHTML', 'NO');
27834         
27835         if(crop){
27836             formData.append('crop', crop);
27837         }
27838         
27839         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27840             formData.append(this.paramName, file, file.name);
27841         }
27842         
27843         if(typeof(file.filename) != 'undefined'){
27844             formData.append('filename', file.filename);
27845         }
27846         
27847         if(typeof(file.mimetype) != 'undefined'){
27848             formData.append('mimetype', file.mimetype);
27849         }
27850         
27851         if(this.fireEvent('arrange', this, formData) != false){
27852             this.xhr.send(formData);
27853         };
27854     },
27855     
27856     xhrOnLoad : function(xhr)
27857     {
27858         if(this.loadMask){
27859             this.maskEl.unmask();
27860         }
27861         
27862         if (xhr.readyState !== 4) {
27863             this.fireEvent('exception', this, xhr);
27864             return;
27865         }
27866
27867         var response = Roo.decode(xhr.responseText);
27868         
27869         if(!response.success){
27870             this.fireEvent('exception', this, xhr);
27871             return;
27872         }
27873         
27874         var response = Roo.decode(xhr.responseText);
27875         
27876         this.fireEvent('upload', this, response);
27877         
27878     },
27879     
27880     xhrOnError : function()
27881     {
27882         if(this.loadMask){
27883             this.maskEl.unmask();
27884         }
27885         
27886         Roo.log('xhr on error');
27887         
27888         var response = Roo.decode(xhr.responseText);
27889           
27890         Roo.log(response);
27891         
27892     },
27893     
27894     prepare : function(file)
27895     {   
27896         if(this.loadMask){
27897             this.maskEl.mask(this.loadingText);
27898         }
27899         
27900         this.file = false;
27901         this.exif = {};
27902         
27903         if(typeof(file) === 'string'){
27904             this.loadCanvas(file);
27905             return;
27906         }
27907         
27908         if(!file || !this.urlAPI){
27909             return;
27910         }
27911         
27912         this.file = file;
27913         this.cropType = file.type;
27914         
27915         var _this = this;
27916         
27917         if(this.fireEvent('prepare', this, this.file) != false){
27918             
27919             var reader = new FileReader();
27920             
27921             reader.onload = function (e) {
27922                 if (e.target.error) {
27923                     Roo.log(e.target.error);
27924                     return;
27925                 }
27926                 
27927                 var buffer = e.target.result,
27928                     dataView = new DataView(buffer),
27929                     offset = 2,
27930                     maxOffset = dataView.byteLength - 4,
27931                     markerBytes,
27932                     markerLength;
27933                 
27934                 if (dataView.getUint16(0) === 0xffd8) {
27935                     while (offset < maxOffset) {
27936                         markerBytes = dataView.getUint16(offset);
27937                         
27938                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27939                             markerLength = dataView.getUint16(offset + 2) + 2;
27940                             if (offset + markerLength > dataView.byteLength) {
27941                                 Roo.log('Invalid meta data: Invalid segment size.');
27942                                 break;
27943                             }
27944                             
27945                             if(markerBytes == 0xffe1){
27946                                 _this.parseExifData(
27947                                     dataView,
27948                                     offset,
27949                                     markerLength
27950                                 );
27951                             }
27952                             
27953                             offset += markerLength;
27954                             
27955                             continue;
27956                         }
27957                         
27958                         break;
27959                     }
27960                     
27961                 }
27962                 
27963                 var url = _this.urlAPI.createObjectURL(_this.file);
27964                 
27965                 _this.loadCanvas(url);
27966                 
27967                 return;
27968             }
27969             
27970             reader.readAsArrayBuffer(this.file);
27971             
27972         }
27973         
27974     },
27975     
27976     parseExifData : function(dataView, offset, length)
27977     {
27978         var tiffOffset = offset + 10,
27979             littleEndian,
27980             dirOffset;
27981     
27982         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27983             // No Exif data, might be XMP data instead
27984             return;
27985         }
27986         
27987         // Check for the ASCII code for "Exif" (0x45786966):
27988         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27989             // No Exif data, might be XMP data instead
27990             return;
27991         }
27992         if (tiffOffset + 8 > dataView.byteLength) {
27993             Roo.log('Invalid Exif data: Invalid segment size.');
27994             return;
27995         }
27996         // Check for the two null bytes:
27997         if (dataView.getUint16(offset + 8) !== 0x0000) {
27998             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27999             return;
28000         }
28001         // Check the byte alignment:
28002         switch (dataView.getUint16(tiffOffset)) {
28003         case 0x4949:
28004             littleEndian = true;
28005             break;
28006         case 0x4D4D:
28007             littleEndian = false;
28008             break;
28009         default:
28010             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28011             return;
28012         }
28013         // Check for the TIFF tag marker (0x002A):
28014         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28015             Roo.log('Invalid Exif data: Missing TIFF marker.');
28016             return;
28017         }
28018         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28019         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28020         
28021         this.parseExifTags(
28022             dataView,
28023             tiffOffset,
28024             tiffOffset + dirOffset,
28025             littleEndian
28026         );
28027     },
28028     
28029     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28030     {
28031         var tagsNumber,
28032             dirEndOffset,
28033             i;
28034         if (dirOffset + 6 > dataView.byteLength) {
28035             Roo.log('Invalid Exif data: Invalid directory offset.');
28036             return;
28037         }
28038         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28039         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28040         if (dirEndOffset + 4 > dataView.byteLength) {
28041             Roo.log('Invalid Exif data: Invalid directory size.');
28042             return;
28043         }
28044         for (i = 0; i < tagsNumber; i += 1) {
28045             this.parseExifTag(
28046                 dataView,
28047                 tiffOffset,
28048                 dirOffset + 2 + 12 * i, // tag offset
28049                 littleEndian
28050             );
28051         }
28052         // Return the offset to the next directory:
28053         return dataView.getUint32(dirEndOffset, littleEndian);
28054     },
28055     
28056     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28057     {
28058         var tag = dataView.getUint16(offset, littleEndian);
28059         
28060         this.exif[tag] = this.getExifValue(
28061             dataView,
28062             tiffOffset,
28063             offset,
28064             dataView.getUint16(offset + 2, littleEndian), // tag type
28065             dataView.getUint32(offset + 4, littleEndian), // tag length
28066             littleEndian
28067         );
28068     },
28069     
28070     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28071     {
28072         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28073             tagSize,
28074             dataOffset,
28075             values,
28076             i,
28077             str,
28078             c;
28079     
28080         if (!tagType) {
28081             Roo.log('Invalid Exif data: Invalid tag type.');
28082             return;
28083         }
28084         
28085         tagSize = tagType.size * length;
28086         // Determine if the value is contained in the dataOffset bytes,
28087         // or if the value at the dataOffset is a pointer to the actual data:
28088         dataOffset = tagSize > 4 ?
28089                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28090         if (dataOffset + tagSize > dataView.byteLength) {
28091             Roo.log('Invalid Exif data: Invalid data offset.');
28092             return;
28093         }
28094         if (length === 1) {
28095             return tagType.getValue(dataView, dataOffset, littleEndian);
28096         }
28097         values = [];
28098         for (i = 0; i < length; i += 1) {
28099             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28100         }
28101         
28102         if (tagType.ascii) {
28103             str = '';
28104             // Concatenate the chars:
28105             for (i = 0; i < values.length; i += 1) {
28106                 c = values[i];
28107                 // Ignore the terminating NULL byte(s):
28108                 if (c === '\u0000') {
28109                     break;
28110                 }
28111                 str += c;
28112             }
28113             return str;
28114         }
28115         return values;
28116     }
28117     
28118 });
28119
28120 Roo.apply(Roo.bootstrap.UploadCropbox, {
28121     tags : {
28122         'Orientation': 0x0112
28123     },
28124     
28125     Orientation: {
28126             1: 0, //'top-left',
28127 //            2: 'top-right',
28128             3: 180, //'bottom-right',
28129 //            4: 'bottom-left',
28130 //            5: 'left-top',
28131             6: 90, //'right-top',
28132 //            7: 'right-bottom',
28133             8: 270 //'left-bottom'
28134     },
28135     
28136     exifTagTypes : {
28137         // byte, 8-bit unsigned int:
28138         1: {
28139             getValue: function (dataView, dataOffset) {
28140                 return dataView.getUint8(dataOffset);
28141             },
28142             size: 1
28143         },
28144         // ascii, 8-bit byte:
28145         2: {
28146             getValue: function (dataView, dataOffset) {
28147                 return String.fromCharCode(dataView.getUint8(dataOffset));
28148             },
28149             size: 1,
28150             ascii: true
28151         },
28152         // short, 16 bit int:
28153         3: {
28154             getValue: function (dataView, dataOffset, littleEndian) {
28155                 return dataView.getUint16(dataOffset, littleEndian);
28156             },
28157             size: 2
28158         },
28159         // long, 32 bit int:
28160         4: {
28161             getValue: function (dataView, dataOffset, littleEndian) {
28162                 return dataView.getUint32(dataOffset, littleEndian);
28163             },
28164             size: 4
28165         },
28166         // rational = two long values, first is numerator, second is denominator:
28167         5: {
28168             getValue: function (dataView, dataOffset, littleEndian) {
28169                 return dataView.getUint32(dataOffset, littleEndian) /
28170                     dataView.getUint32(dataOffset + 4, littleEndian);
28171             },
28172             size: 8
28173         },
28174         // slong, 32 bit signed int:
28175         9: {
28176             getValue: function (dataView, dataOffset, littleEndian) {
28177                 return dataView.getInt32(dataOffset, littleEndian);
28178             },
28179             size: 4
28180         },
28181         // srational, two slongs, first is numerator, second is denominator:
28182         10: {
28183             getValue: function (dataView, dataOffset, littleEndian) {
28184                 return dataView.getInt32(dataOffset, littleEndian) /
28185                     dataView.getInt32(dataOffset + 4, littleEndian);
28186             },
28187             size: 8
28188         }
28189     },
28190     
28191     footer : {
28192         STANDARD : [
28193             {
28194                 tag : 'div',
28195                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28196                 action : 'rotate-left',
28197                 cn : [
28198                     {
28199                         tag : 'button',
28200                         cls : 'btn btn-default',
28201                         html : '<i class="fa fa-undo"></i>'
28202                     }
28203                 ]
28204             },
28205             {
28206                 tag : 'div',
28207                 cls : 'btn-group roo-upload-cropbox-picture',
28208                 action : 'picture',
28209                 cn : [
28210                     {
28211                         tag : 'button',
28212                         cls : 'btn btn-default',
28213                         html : '<i class="fa fa-picture-o"></i>'
28214                     }
28215                 ]
28216             },
28217             {
28218                 tag : 'div',
28219                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28220                 action : 'rotate-right',
28221                 cn : [
28222                     {
28223                         tag : 'button',
28224                         cls : 'btn btn-default',
28225                         html : '<i class="fa fa-repeat"></i>'
28226                     }
28227                 ]
28228             }
28229         ],
28230         DOCUMENT : [
28231             {
28232                 tag : 'div',
28233                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28234                 action : 'rotate-left',
28235                 cn : [
28236                     {
28237                         tag : 'button',
28238                         cls : 'btn btn-default',
28239                         html : '<i class="fa fa-undo"></i>'
28240                     }
28241                 ]
28242             },
28243             {
28244                 tag : 'div',
28245                 cls : 'btn-group roo-upload-cropbox-download',
28246                 action : 'download',
28247                 cn : [
28248                     {
28249                         tag : 'button',
28250                         cls : 'btn btn-default',
28251                         html : '<i class="fa fa-download"></i>'
28252                     }
28253                 ]
28254             },
28255             {
28256                 tag : 'div',
28257                 cls : 'btn-group roo-upload-cropbox-crop',
28258                 action : 'crop',
28259                 cn : [
28260                     {
28261                         tag : 'button',
28262                         cls : 'btn btn-default',
28263                         html : '<i class="fa fa-crop"></i>'
28264                     }
28265                 ]
28266             },
28267             {
28268                 tag : 'div',
28269                 cls : 'btn-group roo-upload-cropbox-trash',
28270                 action : 'trash',
28271                 cn : [
28272                     {
28273                         tag : 'button',
28274                         cls : 'btn btn-default',
28275                         html : '<i class="fa fa-trash"></i>'
28276                     }
28277                 ]
28278             },
28279             {
28280                 tag : 'div',
28281                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28282                 action : 'rotate-right',
28283                 cn : [
28284                     {
28285                         tag : 'button',
28286                         cls : 'btn btn-default',
28287                         html : '<i class="fa fa-repeat"></i>'
28288                     }
28289                 ]
28290             }
28291         ],
28292         ROTATOR : [
28293             {
28294                 tag : 'div',
28295                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28296                 action : 'rotate-left',
28297                 cn : [
28298                     {
28299                         tag : 'button',
28300                         cls : 'btn btn-default',
28301                         html : '<i class="fa fa-undo"></i>'
28302                     }
28303                 ]
28304             },
28305             {
28306                 tag : 'div',
28307                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28308                 action : 'rotate-right',
28309                 cn : [
28310                     {
28311                         tag : 'button',
28312                         cls : 'btn btn-default',
28313                         html : '<i class="fa fa-repeat"></i>'
28314                     }
28315                 ]
28316             }
28317         ]
28318     }
28319 });
28320
28321 /*
28322 * Licence: LGPL
28323 */
28324
28325 /**
28326  * @class Roo.bootstrap.DocumentManager
28327  * @extends Roo.bootstrap.Component
28328  * Bootstrap DocumentManager class
28329  * @cfg {String} paramName default 'imageUpload'
28330  * @cfg {String} toolTipName default 'filename'
28331  * @cfg {String} method default POST
28332  * @cfg {String} url action url
28333  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28334  * @cfg {Boolean} multiple multiple upload default true
28335  * @cfg {Number} thumbSize default 300
28336  * @cfg {String} fieldLabel
28337  * @cfg {Number} labelWidth default 4
28338  * @cfg {String} labelAlign (left|top) default left
28339  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28340 * @cfg {Number} labellg set the width of label (1-12)
28341  * @cfg {Number} labelmd set the width of label (1-12)
28342  * @cfg {Number} labelsm set the width of label (1-12)
28343  * @cfg {Number} labelxs set the width of label (1-12)
28344  * 
28345  * @constructor
28346  * Create a new DocumentManager
28347  * @param {Object} config The config object
28348  */
28349
28350 Roo.bootstrap.DocumentManager = function(config){
28351     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28352     
28353     this.files = [];
28354     this.delegates = [];
28355     
28356     this.addEvents({
28357         /**
28358          * @event initial
28359          * Fire when initial the DocumentManager
28360          * @param {Roo.bootstrap.DocumentManager} this
28361          */
28362         "initial" : true,
28363         /**
28364          * @event inspect
28365          * inspect selected file
28366          * @param {Roo.bootstrap.DocumentManager} this
28367          * @param {File} file
28368          */
28369         "inspect" : true,
28370         /**
28371          * @event exception
28372          * Fire when xhr load exception
28373          * @param {Roo.bootstrap.DocumentManager} this
28374          * @param {XMLHttpRequest} xhr
28375          */
28376         "exception" : true,
28377         /**
28378          * @event afterupload
28379          * Fire when xhr load exception
28380          * @param {Roo.bootstrap.DocumentManager} this
28381          * @param {XMLHttpRequest} xhr
28382          */
28383         "afterupload" : true,
28384         /**
28385          * @event prepare
28386          * prepare the form data
28387          * @param {Roo.bootstrap.DocumentManager} this
28388          * @param {Object} formData
28389          */
28390         "prepare" : true,
28391         /**
28392          * @event remove
28393          * Fire when remove the file
28394          * @param {Roo.bootstrap.DocumentManager} this
28395          * @param {Object} file
28396          */
28397         "remove" : true,
28398         /**
28399          * @event refresh
28400          * Fire after refresh the file
28401          * @param {Roo.bootstrap.DocumentManager} this
28402          */
28403         "refresh" : true,
28404         /**
28405          * @event click
28406          * Fire after click the image
28407          * @param {Roo.bootstrap.DocumentManager} this
28408          * @param {Object} file
28409          */
28410         "click" : true,
28411         /**
28412          * @event edit
28413          * Fire when upload a image and editable set to true
28414          * @param {Roo.bootstrap.DocumentManager} this
28415          * @param {Object} file
28416          */
28417         "edit" : true,
28418         /**
28419          * @event beforeselectfile
28420          * Fire before select file
28421          * @param {Roo.bootstrap.DocumentManager} this
28422          */
28423         "beforeselectfile" : true,
28424         /**
28425          * @event process
28426          * Fire before process file
28427          * @param {Roo.bootstrap.DocumentManager} this
28428          * @param {Object} file
28429          */
28430         "process" : true
28431         
28432     });
28433 };
28434
28435 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28436     
28437     boxes : 0,
28438     inputName : '',
28439     thumbSize : 300,
28440     multiple : true,
28441     files : false,
28442     method : 'POST',
28443     url : '',
28444     paramName : 'imageUpload',
28445     toolTipName : 'filename',
28446     fieldLabel : '',
28447     labelWidth : 4,
28448     labelAlign : 'left',
28449     editable : true,
28450     delegates : false,
28451     xhr : false, 
28452     
28453     labellg : 0,
28454     labelmd : 0,
28455     labelsm : 0,
28456     labelxs : 0,
28457     
28458     getAutoCreate : function()
28459     {   
28460         var managerWidget = {
28461             tag : 'div',
28462             cls : 'roo-document-manager',
28463             cn : [
28464                 {
28465                     tag : 'input',
28466                     cls : 'roo-document-manager-selector',
28467                     type : 'file'
28468                 },
28469                 {
28470                     tag : 'div',
28471                     cls : 'roo-document-manager-uploader',
28472                     cn : [
28473                         {
28474                             tag : 'div',
28475                             cls : 'roo-document-manager-upload-btn',
28476                             html : '<i class="fa fa-plus"></i>'
28477                         }
28478                     ]
28479                     
28480                 }
28481             ]
28482         };
28483         
28484         var content = [
28485             {
28486                 tag : 'div',
28487                 cls : 'column col-md-12',
28488                 cn : managerWidget
28489             }
28490         ];
28491         
28492         if(this.fieldLabel.length){
28493             
28494             content = [
28495                 {
28496                     tag : 'div',
28497                     cls : 'column col-md-12',
28498                     html : this.fieldLabel
28499                 },
28500                 {
28501                     tag : 'div',
28502                     cls : 'column col-md-12',
28503                     cn : managerWidget
28504                 }
28505             ];
28506
28507             if(this.labelAlign == 'left'){
28508                 content = [
28509                     {
28510                         tag : 'div',
28511                         cls : 'column',
28512                         html : this.fieldLabel
28513                     },
28514                     {
28515                         tag : 'div',
28516                         cls : 'column',
28517                         cn : managerWidget
28518                     }
28519                 ];
28520                 
28521                 if(this.labelWidth > 12){
28522                     content[0].style = "width: " + this.labelWidth + 'px';
28523                 }
28524
28525                 if(this.labelWidth < 13 && this.labelmd == 0){
28526                     this.labelmd = this.labelWidth;
28527                 }
28528
28529                 if(this.labellg > 0){
28530                     content[0].cls += ' col-lg-' + this.labellg;
28531                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28532                 }
28533
28534                 if(this.labelmd > 0){
28535                     content[0].cls += ' col-md-' + this.labelmd;
28536                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28537                 }
28538
28539                 if(this.labelsm > 0){
28540                     content[0].cls += ' col-sm-' + this.labelsm;
28541                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28542                 }
28543
28544                 if(this.labelxs > 0){
28545                     content[0].cls += ' col-xs-' + this.labelxs;
28546                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28547                 }
28548                 
28549             }
28550         }
28551         
28552         var cfg = {
28553             tag : 'div',
28554             cls : 'row clearfix',
28555             cn : content
28556         };
28557         
28558         return cfg;
28559         
28560     },
28561     
28562     initEvents : function()
28563     {
28564         this.managerEl = this.el.select('.roo-document-manager', true).first();
28565         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28566         
28567         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28568         this.selectorEl.hide();
28569         
28570         if(this.multiple){
28571             this.selectorEl.attr('multiple', 'multiple');
28572         }
28573         
28574         this.selectorEl.on('change', this.onFileSelected, this);
28575         
28576         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28577         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28578         
28579         this.uploader.on('click', this.onUploaderClick, this);
28580         
28581         this.renderProgressDialog();
28582         
28583         var _this = this;
28584         
28585         window.addEventListener("resize", function() { _this.refresh(); } );
28586         
28587         this.fireEvent('initial', this);
28588     },
28589     
28590     renderProgressDialog : function()
28591     {
28592         var _this = this;
28593         
28594         this.progressDialog = new Roo.bootstrap.Modal({
28595             cls : 'roo-document-manager-progress-dialog',
28596             allow_close : false,
28597             title : '',
28598             buttons : [
28599                 {
28600                     name  :'cancel',
28601                     weight : 'danger',
28602                     html : 'Cancel'
28603                 }
28604             ], 
28605             listeners : { 
28606                 btnclick : function() {
28607                     _this.uploadCancel();
28608                     this.hide();
28609                 }
28610             }
28611         });
28612          
28613         this.progressDialog.render(Roo.get(document.body));
28614          
28615         this.progress = new Roo.bootstrap.Progress({
28616             cls : 'roo-document-manager-progress',
28617             active : true,
28618             striped : true
28619         });
28620         
28621         this.progress.render(this.progressDialog.getChildContainer());
28622         
28623         this.progressBar = new Roo.bootstrap.ProgressBar({
28624             cls : 'roo-document-manager-progress-bar',
28625             aria_valuenow : 0,
28626             aria_valuemin : 0,
28627             aria_valuemax : 12,
28628             panel : 'success'
28629         });
28630         
28631         this.progressBar.render(this.progress.getChildContainer());
28632     },
28633     
28634     onUploaderClick : function(e)
28635     {
28636         e.preventDefault();
28637      
28638         if(this.fireEvent('beforeselectfile', this) != false){
28639             this.selectorEl.dom.click();
28640         }
28641         
28642     },
28643     
28644     onFileSelected : function(e)
28645     {
28646         e.preventDefault();
28647         
28648         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28649             return;
28650         }
28651         
28652         Roo.each(this.selectorEl.dom.files, function(file){
28653             if(this.fireEvent('inspect', this, file) != false){
28654                 this.files.push(file);
28655             }
28656         }, this);
28657         
28658         this.queue();
28659         
28660     },
28661     
28662     queue : function()
28663     {
28664         this.selectorEl.dom.value = '';
28665         
28666         if(!this.files.length){
28667             return;
28668         }
28669         
28670         if(this.boxes > 0 && this.files.length > this.boxes){
28671             this.files = this.files.slice(0, this.boxes);
28672         }
28673         
28674         this.uploader.show();
28675         
28676         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28677             this.uploader.hide();
28678         }
28679         
28680         var _this = this;
28681         
28682         var files = [];
28683         
28684         var docs = [];
28685         
28686         Roo.each(this.files, function(file){
28687             
28688             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28689                 var f = this.renderPreview(file);
28690                 files.push(f);
28691                 return;
28692             }
28693             
28694             if(file.type.indexOf('image') != -1){
28695                 this.delegates.push(
28696                     (function(){
28697                         _this.process(file);
28698                     }).createDelegate(this)
28699                 );
28700         
28701                 return;
28702             }
28703             
28704             docs.push(
28705                 (function(){
28706                     _this.process(file);
28707                 }).createDelegate(this)
28708             );
28709             
28710         }, this);
28711         
28712         this.files = files;
28713         
28714         this.delegates = this.delegates.concat(docs);
28715         
28716         if(!this.delegates.length){
28717             this.refresh();
28718             return;
28719         }
28720         
28721         this.progressBar.aria_valuemax = this.delegates.length;
28722         
28723         this.arrange();
28724         
28725         return;
28726     },
28727     
28728     arrange : function()
28729     {
28730         if(!this.delegates.length){
28731             this.progressDialog.hide();
28732             this.refresh();
28733             return;
28734         }
28735         
28736         var delegate = this.delegates.shift();
28737         
28738         this.progressDialog.show();
28739         
28740         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28741         
28742         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28743         
28744         delegate();
28745     },
28746     
28747     refresh : function()
28748     {
28749         this.uploader.show();
28750         
28751         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28752             this.uploader.hide();
28753         }
28754         
28755         Roo.isTouch ? this.closable(false) : this.closable(true);
28756         
28757         this.fireEvent('refresh', this);
28758     },
28759     
28760     onRemove : function(e, el, o)
28761     {
28762         e.preventDefault();
28763         
28764         this.fireEvent('remove', this, o);
28765         
28766     },
28767     
28768     remove : function(o)
28769     {
28770         var files = [];
28771         
28772         Roo.each(this.files, function(file){
28773             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28774                 files.push(file);
28775                 return;
28776             }
28777
28778             o.target.remove();
28779
28780         }, this);
28781         
28782         this.files = files;
28783         
28784         this.refresh();
28785     },
28786     
28787     clear : function()
28788     {
28789         Roo.each(this.files, function(file){
28790             if(!file.target){
28791                 return;
28792             }
28793             
28794             file.target.remove();
28795
28796         }, this);
28797         
28798         this.files = [];
28799         
28800         this.refresh();
28801     },
28802     
28803     onClick : function(e, el, o)
28804     {
28805         e.preventDefault();
28806         
28807         this.fireEvent('click', this, o);
28808         
28809     },
28810     
28811     closable : function(closable)
28812     {
28813         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28814             
28815             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28816             
28817             if(closable){
28818                 el.show();
28819                 return;
28820             }
28821             
28822             el.hide();
28823             
28824         }, this);
28825     },
28826     
28827     xhrOnLoad : function(xhr)
28828     {
28829         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28830             el.remove();
28831         }, this);
28832         
28833         if (xhr.readyState !== 4) {
28834             this.arrange();
28835             this.fireEvent('exception', this, xhr);
28836             return;
28837         }
28838
28839         var response = Roo.decode(xhr.responseText);
28840         
28841         if(!response.success){
28842             this.arrange();
28843             this.fireEvent('exception', this, xhr);
28844             return;
28845         }
28846         
28847         var file = this.renderPreview(response.data);
28848         
28849         this.files.push(file);
28850         
28851         this.arrange();
28852         
28853         this.fireEvent('afterupload', this, xhr);
28854         
28855     },
28856     
28857     xhrOnError : function(xhr)
28858     {
28859         Roo.log('xhr on error');
28860         
28861         var response = Roo.decode(xhr.responseText);
28862           
28863         Roo.log(response);
28864         
28865         this.arrange();
28866     },
28867     
28868     process : function(file)
28869     {
28870         if(this.fireEvent('process', this, file) !== false){
28871             if(this.editable && file.type.indexOf('image') != -1){
28872                 this.fireEvent('edit', this, file);
28873                 return;
28874             }
28875
28876             this.uploadStart(file, false);
28877
28878             return;
28879         }
28880         
28881     },
28882     
28883     uploadStart : function(file, crop)
28884     {
28885         this.xhr = new XMLHttpRequest();
28886         
28887         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28888             this.arrange();
28889             return;
28890         }
28891         
28892         file.xhr = this.xhr;
28893             
28894         this.managerEl.createChild({
28895             tag : 'div',
28896             cls : 'roo-document-manager-loading',
28897             cn : [
28898                 {
28899                     tag : 'div',
28900                     tooltip : file.name,
28901                     cls : 'roo-document-manager-thumb',
28902                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28903                 }
28904             ]
28905
28906         });
28907
28908         this.xhr.open(this.method, this.url, true);
28909         
28910         var headers = {
28911             "Accept": "application/json",
28912             "Cache-Control": "no-cache",
28913             "X-Requested-With": "XMLHttpRequest"
28914         };
28915         
28916         for (var headerName in headers) {
28917             var headerValue = headers[headerName];
28918             if (headerValue) {
28919                 this.xhr.setRequestHeader(headerName, headerValue);
28920             }
28921         }
28922         
28923         var _this = this;
28924         
28925         this.xhr.onload = function()
28926         {
28927             _this.xhrOnLoad(_this.xhr);
28928         }
28929         
28930         this.xhr.onerror = function()
28931         {
28932             _this.xhrOnError(_this.xhr);
28933         }
28934         
28935         var formData = new FormData();
28936
28937         formData.append('returnHTML', 'NO');
28938         
28939         if(crop){
28940             formData.append('crop', crop);
28941         }
28942         
28943         formData.append(this.paramName, file, file.name);
28944         
28945         var options = {
28946             file : file, 
28947             manually : false
28948         };
28949         
28950         if(this.fireEvent('prepare', this, formData, options) != false){
28951             
28952             if(options.manually){
28953                 return;
28954             }
28955             
28956             this.xhr.send(formData);
28957             return;
28958         };
28959         
28960         this.uploadCancel();
28961     },
28962     
28963     uploadCancel : function()
28964     {
28965         if (this.xhr) {
28966             this.xhr.abort();
28967         }
28968         
28969         this.delegates = [];
28970         
28971         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28972             el.remove();
28973         }, this);
28974         
28975         this.arrange();
28976     },
28977     
28978     renderPreview : function(file)
28979     {
28980         if(typeof(file.target) != 'undefined' && file.target){
28981             return file;
28982         }
28983         
28984         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28985         
28986         var previewEl = this.managerEl.createChild({
28987             tag : 'div',
28988             cls : 'roo-document-manager-preview',
28989             cn : [
28990                 {
28991                     tag : 'div',
28992                     tooltip : file[this.toolTipName],
28993                     cls : 'roo-document-manager-thumb',
28994                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28995                 },
28996                 {
28997                     tag : 'button',
28998                     cls : 'close',
28999                     html : '<i class="fa fa-times-circle"></i>'
29000                 }
29001             ]
29002         });
29003
29004         var close = previewEl.select('button.close', true).first();
29005
29006         close.on('click', this.onRemove, this, file);
29007
29008         file.target = previewEl;
29009
29010         var image = previewEl.select('img', true).first();
29011         
29012         var _this = this;
29013         
29014         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29015         
29016         image.on('click', this.onClick, this, file);
29017         
29018         return file;
29019         
29020     },
29021     
29022     onPreviewLoad : function(file, image)
29023     {
29024         if(typeof(file.target) == 'undefined' || !file.target){
29025             return;
29026         }
29027         
29028         var width = image.dom.naturalWidth || image.dom.width;
29029         var height = image.dom.naturalHeight || image.dom.height;
29030         
29031         if(width > height){
29032             file.target.addClass('wide');
29033             return;
29034         }
29035         
29036         file.target.addClass('tall');
29037         return;
29038         
29039     },
29040     
29041     uploadFromSource : function(file, crop)
29042     {
29043         this.xhr = new XMLHttpRequest();
29044         
29045         this.managerEl.createChild({
29046             tag : 'div',
29047             cls : 'roo-document-manager-loading',
29048             cn : [
29049                 {
29050                     tag : 'div',
29051                     tooltip : file.name,
29052                     cls : 'roo-document-manager-thumb',
29053                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29054                 }
29055             ]
29056
29057         });
29058
29059         this.xhr.open(this.method, this.url, true);
29060         
29061         var headers = {
29062             "Accept": "application/json",
29063             "Cache-Control": "no-cache",
29064             "X-Requested-With": "XMLHttpRequest"
29065         };
29066         
29067         for (var headerName in headers) {
29068             var headerValue = headers[headerName];
29069             if (headerValue) {
29070                 this.xhr.setRequestHeader(headerName, headerValue);
29071             }
29072         }
29073         
29074         var _this = this;
29075         
29076         this.xhr.onload = function()
29077         {
29078             _this.xhrOnLoad(_this.xhr);
29079         }
29080         
29081         this.xhr.onerror = function()
29082         {
29083             _this.xhrOnError(_this.xhr);
29084         }
29085         
29086         var formData = new FormData();
29087
29088         formData.append('returnHTML', 'NO');
29089         
29090         formData.append('crop', crop);
29091         
29092         if(typeof(file.filename) != 'undefined'){
29093             formData.append('filename', file.filename);
29094         }
29095         
29096         if(typeof(file.mimetype) != 'undefined'){
29097             formData.append('mimetype', file.mimetype);
29098         }
29099         
29100         Roo.log(formData);
29101         
29102         if(this.fireEvent('prepare', this, formData) != false){
29103             this.xhr.send(formData);
29104         };
29105     }
29106 });
29107
29108 /*
29109 * Licence: LGPL
29110 */
29111
29112 /**
29113  * @class Roo.bootstrap.DocumentViewer
29114  * @extends Roo.bootstrap.Component
29115  * Bootstrap DocumentViewer class
29116  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29117  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29118  * 
29119  * @constructor
29120  * Create a new DocumentViewer
29121  * @param {Object} config The config object
29122  */
29123
29124 Roo.bootstrap.DocumentViewer = function(config){
29125     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29126     
29127     this.addEvents({
29128         /**
29129          * @event initial
29130          * Fire after initEvent
29131          * @param {Roo.bootstrap.DocumentViewer} this
29132          */
29133         "initial" : true,
29134         /**
29135          * @event click
29136          * Fire after click
29137          * @param {Roo.bootstrap.DocumentViewer} this
29138          */
29139         "click" : true,
29140         /**
29141          * @event download
29142          * Fire after download button
29143          * @param {Roo.bootstrap.DocumentViewer} this
29144          */
29145         "download" : true,
29146         /**
29147          * @event trash
29148          * Fire after trash button
29149          * @param {Roo.bootstrap.DocumentViewer} this
29150          */
29151         "trash" : true
29152         
29153     });
29154 };
29155
29156 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29157     
29158     showDownload : true,
29159     
29160     showTrash : true,
29161     
29162     getAutoCreate : function()
29163     {
29164         var cfg = {
29165             tag : 'div',
29166             cls : 'roo-document-viewer',
29167             cn : [
29168                 {
29169                     tag : 'div',
29170                     cls : 'roo-document-viewer-body',
29171                     cn : [
29172                         {
29173                             tag : 'div',
29174                             cls : 'roo-document-viewer-thumb',
29175                             cn : [
29176                                 {
29177                                     tag : 'img',
29178                                     cls : 'roo-document-viewer-image'
29179                                 }
29180                             ]
29181                         }
29182                     ]
29183                 },
29184                 {
29185                     tag : 'div',
29186                     cls : 'roo-document-viewer-footer',
29187                     cn : {
29188                         tag : 'div',
29189                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29190                         cn : [
29191                             {
29192                                 tag : 'div',
29193                                 cls : 'btn-group roo-document-viewer-download',
29194                                 cn : [
29195                                     {
29196                                         tag : 'button',
29197                                         cls : 'btn btn-default',
29198                                         html : '<i class="fa fa-download"></i>'
29199                                     }
29200                                 ]
29201                             },
29202                             {
29203                                 tag : 'div',
29204                                 cls : 'btn-group roo-document-viewer-trash',
29205                                 cn : [
29206                                     {
29207                                         tag : 'button',
29208                                         cls : 'btn btn-default',
29209                                         html : '<i class="fa fa-trash"></i>'
29210                                     }
29211                                 ]
29212                             }
29213                         ]
29214                     }
29215                 }
29216             ]
29217         };
29218         
29219         return cfg;
29220     },
29221     
29222     initEvents : function()
29223     {
29224         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29225         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29226         
29227         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29228         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29229         
29230         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29231         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29232         
29233         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29234         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29235         
29236         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29237         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29238         
29239         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29240         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29241         
29242         this.bodyEl.on('click', this.onClick, this);
29243         this.downloadBtn.on('click', this.onDownload, this);
29244         this.trashBtn.on('click', this.onTrash, this);
29245         
29246         this.downloadBtn.hide();
29247         this.trashBtn.hide();
29248         
29249         if(this.showDownload){
29250             this.downloadBtn.show();
29251         }
29252         
29253         if(this.showTrash){
29254             this.trashBtn.show();
29255         }
29256         
29257         if(!this.showDownload && !this.showTrash) {
29258             this.footerEl.hide();
29259         }
29260         
29261     },
29262     
29263     initial : function()
29264     {
29265         this.fireEvent('initial', this);
29266         
29267     },
29268     
29269     onClick : function(e)
29270     {
29271         e.preventDefault();
29272         
29273         this.fireEvent('click', this);
29274     },
29275     
29276     onDownload : function(e)
29277     {
29278         e.preventDefault();
29279         
29280         this.fireEvent('download', this);
29281     },
29282     
29283     onTrash : function(e)
29284     {
29285         e.preventDefault();
29286         
29287         this.fireEvent('trash', this);
29288     }
29289     
29290 });
29291 /*
29292  * - LGPL
29293  *
29294  * nav progress bar
29295  * 
29296  */
29297
29298 /**
29299  * @class Roo.bootstrap.NavProgressBar
29300  * @extends Roo.bootstrap.Component
29301  * Bootstrap NavProgressBar class
29302  * 
29303  * @constructor
29304  * Create a new nav progress bar
29305  * @param {Object} config The config object
29306  */
29307
29308 Roo.bootstrap.NavProgressBar = function(config){
29309     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29310
29311     this.bullets = this.bullets || [];
29312    
29313 //    Roo.bootstrap.NavProgressBar.register(this);
29314      this.addEvents({
29315         /**
29316              * @event changed
29317              * Fires when the active item changes
29318              * @param {Roo.bootstrap.NavProgressBar} this
29319              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29320              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29321          */
29322         'changed': true
29323      });
29324     
29325 };
29326
29327 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29328     
29329     bullets : [],
29330     barItems : [],
29331     
29332     getAutoCreate : function()
29333     {
29334         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29335         
29336         cfg = {
29337             tag : 'div',
29338             cls : 'roo-navigation-bar-group',
29339             cn : [
29340                 {
29341                     tag : 'div',
29342                     cls : 'roo-navigation-top-bar'
29343                 },
29344                 {
29345                     tag : 'div',
29346                     cls : 'roo-navigation-bullets-bar',
29347                     cn : [
29348                         {
29349                             tag : 'ul',
29350                             cls : 'roo-navigation-bar'
29351                         }
29352                     ]
29353                 },
29354                 
29355                 {
29356                     tag : 'div',
29357                     cls : 'roo-navigation-bottom-bar'
29358                 }
29359             ]
29360             
29361         };
29362         
29363         return cfg;
29364         
29365     },
29366     
29367     initEvents: function() 
29368     {
29369         
29370     },
29371     
29372     onRender : function(ct, position) 
29373     {
29374         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29375         
29376         if(this.bullets.length){
29377             Roo.each(this.bullets, function(b){
29378                this.addItem(b);
29379             }, this);
29380         }
29381         
29382         this.format();
29383         
29384     },
29385     
29386     addItem : function(cfg)
29387     {
29388         var item = new Roo.bootstrap.NavProgressItem(cfg);
29389         
29390         item.parentId = this.id;
29391         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29392         
29393         if(cfg.html){
29394             var top = new Roo.bootstrap.Element({
29395                 tag : 'div',
29396                 cls : 'roo-navigation-bar-text'
29397             });
29398             
29399             var bottom = new Roo.bootstrap.Element({
29400                 tag : 'div',
29401                 cls : 'roo-navigation-bar-text'
29402             });
29403             
29404             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29405             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29406             
29407             var topText = new Roo.bootstrap.Element({
29408                 tag : 'span',
29409                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29410             });
29411             
29412             var bottomText = new Roo.bootstrap.Element({
29413                 tag : 'span',
29414                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29415             });
29416             
29417             topText.onRender(top.el, null);
29418             bottomText.onRender(bottom.el, null);
29419             
29420             item.topEl = top;
29421             item.bottomEl = bottom;
29422         }
29423         
29424         this.barItems.push(item);
29425         
29426         return item;
29427     },
29428     
29429     getActive : function()
29430     {
29431         var active = false;
29432         
29433         Roo.each(this.barItems, function(v){
29434             
29435             if (!v.isActive()) {
29436                 return;
29437             }
29438             
29439             active = v;
29440             return false;
29441             
29442         });
29443         
29444         return active;
29445     },
29446     
29447     setActiveItem : function(item)
29448     {
29449         var prev = false;
29450         
29451         Roo.each(this.barItems, function(v){
29452             if (v.rid == item.rid) {
29453                 return ;
29454             }
29455             
29456             if (v.isActive()) {
29457                 v.setActive(false);
29458                 prev = v;
29459             }
29460         });
29461
29462         item.setActive(true);
29463         
29464         this.fireEvent('changed', this, item, prev);
29465     },
29466     
29467     getBarItem: function(rid)
29468     {
29469         var ret = false;
29470         
29471         Roo.each(this.barItems, function(e) {
29472             if (e.rid != rid) {
29473                 return;
29474             }
29475             
29476             ret =  e;
29477             return false;
29478         });
29479         
29480         return ret;
29481     },
29482     
29483     indexOfItem : function(item)
29484     {
29485         var index = false;
29486         
29487         Roo.each(this.barItems, function(v, i){
29488             
29489             if (v.rid != item.rid) {
29490                 return;
29491             }
29492             
29493             index = i;
29494             return false
29495         });
29496         
29497         return index;
29498     },
29499     
29500     setActiveNext : function()
29501     {
29502         var i = this.indexOfItem(this.getActive());
29503         
29504         if (i > this.barItems.length) {
29505             return;
29506         }
29507         
29508         this.setActiveItem(this.barItems[i+1]);
29509     },
29510     
29511     setActivePrev : function()
29512     {
29513         var i = this.indexOfItem(this.getActive());
29514         
29515         if (i  < 1) {
29516             return;
29517         }
29518         
29519         this.setActiveItem(this.barItems[i-1]);
29520     },
29521     
29522     format : function()
29523     {
29524         if(!this.barItems.length){
29525             return;
29526         }
29527      
29528         var width = 100 / this.barItems.length;
29529         
29530         Roo.each(this.barItems, function(i){
29531             i.el.setStyle('width', width + '%');
29532             i.topEl.el.setStyle('width', width + '%');
29533             i.bottomEl.el.setStyle('width', width + '%');
29534         }, this);
29535         
29536     }
29537     
29538 });
29539 /*
29540  * - LGPL
29541  *
29542  * Nav Progress Item
29543  * 
29544  */
29545
29546 /**
29547  * @class Roo.bootstrap.NavProgressItem
29548  * @extends Roo.bootstrap.Component
29549  * Bootstrap NavProgressItem class
29550  * @cfg {String} rid the reference id
29551  * @cfg {Boolean} active (true|false) Is item active default false
29552  * @cfg {Boolean} disabled (true|false) Is item active default false
29553  * @cfg {String} html
29554  * @cfg {String} position (top|bottom) text position default bottom
29555  * @cfg {String} icon show icon instead of number
29556  * 
29557  * @constructor
29558  * Create a new NavProgressItem
29559  * @param {Object} config The config object
29560  */
29561 Roo.bootstrap.NavProgressItem = function(config){
29562     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29563     this.addEvents({
29564         // raw events
29565         /**
29566          * @event click
29567          * The raw click event for the entire grid.
29568          * @param {Roo.bootstrap.NavProgressItem} this
29569          * @param {Roo.EventObject} e
29570          */
29571         "click" : true
29572     });
29573    
29574 };
29575
29576 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29577     
29578     rid : '',
29579     active : false,
29580     disabled : false,
29581     html : '',
29582     position : 'bottom',
29583     icon : false,
29584     
29585     getAutoCreate : function()
29586     {
29587         var iconCls = 'roo-navigation-bar-item-icon';
29588         
29589         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29590         
29591         var cfg = {
29592             tag: 'li',
29593             cls: 'roo-navigation-bar-item',
29594             cn : [
29595                 {
29596                     tag : 'i',
29597                     cls : iconCls
29598                 }
29599             ]
29600         };
29601         
29602         if(this.active){
29603             cfg.cls += ' active';
29604         }
29605         if(this.disabled){
29606             cfg.cls += ' disabled';
29607         }
29608         
29609         return cfg;
29610     },
29611     
29612     disable : function()
29613     {
29614         this.setDisabled(true);
29615     },
29616     
29617     enable : function()
29618     {
29619         this.setDisabled(false);
29620     },
29621     
29622     initEvents: function() 
29623     {
29624         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29625         
29626         this.iconEl.on('click', this.onClick, this);
29627     },
29628     
29629     onClick : function(e)
29630     {
29631         e.preventDefault();
29632         
29633         if(this.disabled){
29634             return;
29635         }
29636         
29637         if(this.fireEvent('click', this, e) === false){
29638             return;
29639         };
29640         
29641         this.parent().setActiveItem(this);
29642     },
29643     
29644     isActive: function () 
29645     {
29646         return this.active;
29647     },
29648     
29649     setActive : function(state)
29650     {
29651         if(this.active == state){
29652             return;
29653         }
29654         
29655         this.active = state;
29656         
29657         if (state) {
29658             this.el.addClass('active');
29659             return;
29660         }
29661         
29662         this.el.removeClass('active');
29663         
29664         return;
29665     },
29666     
29667     setDisabled : function(state)
29668     {
29669         if(this.disabled == state){
29670             return;
29671         }
29672         
29673         this.disabled = state;
29674         
29675         if (state) {
29676             this.el.addClass('disabled');
29677             return;
29678         }
29679         
29680         this.el.removeClass('disabled');
29681     },
29682     
29683     tooltipEl : function()
29684     {
29685         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29686     }
29687 });
29688  
29689
29690  /*
29691  * - LGPL
29692  *
29693  * FieldLabel
29694  * 
29695  */
29696
29697 /**
29698  * @class Roo.bootstrap.FieldLabel
29699  * @extends Roo.bootstrap.Component
29700  * Bootstrap FieldLabel class
29701  * @cfg {String} html contents of the element
29702  * @cfg {String} tag tag of the element default label
29703  * @cfg {String} cls class of the element
29704  * @cfg {String} target label target 
29705  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29706  * @cfg {String} invalidClass default "text-warning"
29707  * @cfg {String} validClass default "text-success"
29708  * @cfg {String} iconTooltip default "This field is required"
29709  * @cfg {String} indicatorpos (left|right) default left
29710  * 
29711  * @constructor
29712  * Create a new FieldLabel
29713  * @param {Object} config The config object
29714  */
29715
29716 Roo.bootstrap.FieldLabel = function(config){
29717     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29718     
29719     this.addEvents({
29720             /**
29721              * @event invalid
29722              * Fires after the field has been marked as invalid.
29723              * @param {Roo.form.FieldLabel} this
29724              * @param {String} msg The validation message
29725              */
29726             invalid : true,
29727             /**
29728              * @event valid
29729              * Fires after the field has been validated with no errors.
29730              * @param {Roo.form.FieldLabel} this
29731              */
29732             valid : true
29733         });
29734 };
29735
29736 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29737     
29738     tag: 'label',
29739     cls: '',
29740     html: '',
29741     target: '',
29742     allowBlank : true,
29743     invalidClass : 'has-warning',
29744     validClass : 'has-success',
29745     iconTooltip : 'This field is required',
29746     indicatorpos : 'left',
29747     
29748     getAutoCreate : function(){
29749         
29750         var cfg = {
29751             tag : this.tag,
29752             cls : 'roo-bootstrap-field-label ' + this.cls,
29753             for : this.target,
29754             cn : [
29755                 {
29756                     tag : 'i',
29757                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29758                     tooltip : this.iconTooltip
29759                 },
29760                 {
29761                     tag : 'span',
29762                     html : this.html
29763                 }
29764             ] 
29765         };
29766         
29767         if(this.indicatorpos == 'right'){
29768             var cfg = {
29769                 tag : this.tag,
29770                 cls : 'roo-bootstrap-field-label ' + this.cls,
29771                 for : this.target,
29772                 cn : [
29773                     {
29774                         tag : 'span',
29775                         html : this.html
29776                     },
29777                     {
29778                         tag : 'i',
29779                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29780                         tooltip : this.iconTooltip
29781                     }
29782                 ] 
29783             };
29784         }
29785         
29786         return cfg;
29787     },
29788     
29789     initEvents: function() 
29790     {
29791         Roo.bootstrap.Element.superclass.initEvents.call(this);
29792         
29793         this.indicator = this.indicatorEl();
29794         
29795         if(this.indicator){
29796             this.indicator.removeClass('visible');
29797             this.indicator.addClass('invisible');
29798         }
29799         
29800         Roo.bootstrap.FieldLabel.register(this);
29801     },
29802     
29803     indicatorEl : function()
29804     {
29805         var indicator = this.el.select('i.roo-required-indicator',true).first();
29806         
29807         if(!indicator){
29808             return false;
29809         }
29810         
29811         return indicator;
29812         
29813     },
29814     
29815     /**
29816      * Mark this field as valid
29817      */
29818     markValid : function()
29819     {
29820         if(this.indicator){
29821             this.indicator.removeClass('visible');
29822             this.indicator.addClass('invisible');
29823         }
29824         
29825         this.el.removeClass(this.invalidClass);
29826         
29827         this.el.addClass(this.validClass);
29828         
29829         this.fireEvent('valid', this);
29830     },
29831     
29832     /**
29833      * Mark this field as invalid
29834      * @param {String} msg The validation message
29835      */
29836     markInvalid : function(msg)
29837     {
29838         if(this.indicator){
29839             this.indicator.removeClass('invisible');
29840             this.indicator.addClass('visible');
29841         }
29842         
29843         this.el.removeClass(this.validClass);
29844         
29845         this.el.addClass(this.invalidClass);
29846         
29847         this.fireEvent('invalid', this, msg);
29848     }
29849     
29850    
29851 });
29852
29853 Roo.apply(Roo.bootstrap.FieldLabel, {
29854     
29855     groups: {},
29856     
29857      /**
29858     * register a FieldLabel Group
29859     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29860     */
29861     register : function(label)
29862     {
29863         if(this.groups.hasOwnProperty(label.target)){
29864             return;
29865         }
29866      
29867         this.groups[label.target] = label;
29868         
29869     },
29870     /**
29871     * fetch a FieldLabel Group based on the target
29872     * @param {string} target
29873     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29874     */
29875     get: function(target) {
29876         if (typeof(this.groups[target]) == 'undefined') {
29877             return false;
29878         }
29879         
29880         return this.groups[target] ;
29881     }
29882 });
29883
29884  
29885
29886  /*
29887  * - LGPL
29888  *
29889  * page DateSplitField.
29890  * 
29891  */
29892
29893
29894 /**
29895  * @class Roo.bootstrap.DateSplitField
29896  * @extends Roo.bootstrap.Component
29897  * Bootstrap DateSplitField class
29898  * @cfg {string} fieldLabel - the label associated
29899  * @cfg {Number} labelWidth set the width of label (0-12)
29900  * @cfg {String} labelAlign (top|left)
29901  * @cfg {Boolean} dayAllowBlank (true|false) default false
29902  * @cfg {Boolean} monthAllowBlank (true|false) default false
29903  * @cfg {Boolean} yearAllowBlank (true|false) default false
29904  * @cfg {string} dayPlaceholder 
29905  * @cfg {string} monthPlaceholder
29906  * @cfg {string} yearPlaceholder
29907  * @cfg {string} dayFormat default 'd'
29908  * @cfg {string} monthFormat default 'm'
29909  * @cfg {string} yearFormat default 'Y'
29910  * @cfg {Number} labellg set the width of label (1-12)
29911  * @cfg {Number} labelmd set the width of label (1-12)
29912  * @cfg {Number} labelsm set the width of label (1-12)
29913  * @cfg {Number} labelxs set the width of label (1-12)
29914
29915  *     
29916  * @constructor
29917  * Create a new DateSplitField
29918  * @param {Object} config The config object
29919  */
29920
29921 Roo.bootstrap.DateSplitField = function(config){
29922     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29923     
29924     this.addEvents({
29925         // raw events
29926          /**
29927          * @event years
29928          * getting the data of years
29929          * @param {Roo.bootstrap.DateSplitField} this
29930          * @param {Object} years
29931          */
29932         "years" : true,
29933         /**
29934          * @event days
29935          * getting the data of days
29936          * @param {Roo.bootstrap.DateSplitField} this
29937          * @param {Object} days
29938          */
29939         "days" : true,
29940         /**
29941          * @event invalid
29942          * Fires after the field has been marked as invalid.
29943          * @param {Roo.form.Field} this
29944          * @param {String} msg The validation message
29945          */
29946         invalid : true,
29947        /**
29948          * @event valid
29949          * Fires after the field has been validated with no errors.
29950          * @param {Roo.form.Field} this
29951          */
29952         valid : true
29953     });
29954 };
29955
29956 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29957     
29958     fieldLabel : '',
29959     labelAlign : 'top',
29960     labelWidth : 3,
29961     dayAllowBlank : false,
29962     monthAllowBlank : false,
29963     yearAllowBlank : false,
29964     dayPlaceholder : '',
29965     monthPlaceholder : '',
29966     yearPlaceholder : '',
29967     dayFormat : 'd',
29968     monthFormat : 'm',
29969     yearFormat : 'Y',
29970     isFormField : true,
29971     labellg : 0,
29972     labelmd : 0,
29973     labelsm : 0,
29974     labelxs : 0,
29975     
29976     getAutoCreate : function()
29977     {
29978         var cfg = {
29979             tag : 'div',
29980             cls : 'row roo-date-split-field-group',
29981             cn : [
29982                 {
29983                     tag : 'input',
29984                     type : 'hidden',
29985                     cls : 'form-hidden-field roo-date-split-field-group-value',
29986                     name : this.name
29987                 }
29988             ]
29989         };
29990         
29991         var labelCls = 'col-md-12';
29992         var contentCls = 'col-md-4';
29993         
29994         if(this.fieldLabel){
29995             
29996             var label = {
29997                 tag : 'div',
29998                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29999                 cn : [
30000                     {
30001                         tag : 'label',
30002                         html : this.fieldLabel
30003                     }
30004                 ]
30005             };
30006             
30007             if(this.labelAlign == 'left'){
30008             
30009                 if(this.labelWidth > 12){
30010                     label.style = "width: " + this.labelWidth + 'px';
30011                 }
30012
30013                 if(this.labelWidth < 13 && this.labelmd == 0){
30014                     this.labelmd = this.labelWidth;
30015                 }
30016
30017                 if(this.labellg > 0){
30018                     labelCls = ' col-lg-' + this.labellg;
30019                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30020                 }
30021
30022                 if(this.labelmd > 0){
30023                     labelCls = ' col-md-' + this.labelmd;
30024                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30025                 }
30026
30027                 if(this.labelsm > 0){
30028                     labelCls = ' col-sm-' + this.labelsm;
30029                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30030                 }
30031
30032                 if(this.labelxs > 0){
30033                     labelCls = ' col-xs-' + this.labelxs;
30034                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30035                 }
30036             }
30037             
30038             label.cls += ' ' + labelCls;
30039             
30040             cfg.cn.push(label);
30041         }
30042         
30043         Roo.each(['day', 'month', 'year'], function(t){
30044             cfg.cn.push({
30045                 tag : 'div',
30046                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30047             });
30048         }, this);
30049         
30050         return cfg;
30051     },
30052     
30053     inputEl: function ()
30054     {
30055         return this.el.select('.roo-date-split-field-group-value', true).first();
30056     },
30057     
30058     onRender : function(ct, position) 
30059     {
30060         var _this = this;
30061         
30062         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30063         
30064         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30065         
30066         this.dayField = new Roo.bootstrap.ComboBox({
30067             allowBlank : this.dayAllowBlank,
30068             alwaysQuery : true,
30069             displayField : 'value',
30070             editable : false,
30071             fieldLabel : '',
30072             forceSelection : true,
30073             mode : 'local',
30074             placeholder : this.dayPlaceholder,
30075             selectOnFocus : true,
30076             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30077             triggerAction : 'all',
30078             typeAhead : true,
30079             valueField : 'value',
30080             store : new Roo.data.SimpleStore({
30081                 data : (function() {    
30082                     var days = [];
30083                     _this.fireEvent('days', _this, days);
30084                     return days;
30085                 })(),
30086                 fields : [ 'value' ]
30087             }),
30088             listeners : {
30089                 select : function (_self, record, index)
30090                 {
30091                     _this.setValue(_this.getValue());
30092                 }
30093             }
30094         });
30095
30096         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30097         
30098         this.monthField = new Roo.bootstrap.MonthField({
30099             after : '<i class=\"fa fa-calendar\"></i>',
30100             allowBlank : this.monthAllowBlank,
30101             placeholder : this.monthPlaceholder,
30102             readOnly : true,
30103             listeners : {
30104                 render : function (_self)
30105                 {
30106                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30107                         e.preventDefault();
30108                         _self.focus();
30109                     });
30110                 },
30111                 select : function (_self, oldvalue, newvalue)
30112                 {
30113                     _this.setValue(_this.getValue());
30114                 }
30115             }
30116         });
30117         
30118         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30119         
30120         this.yearField = new Roo.bootstrap.ComboBox({
30121             allowBlank : this.yearAllowBlank,
30122             alwaysQuery : true,
30123             displayField : 'value',
30124             editable : false,
30125             fieldLabel : '',
30126             forceSelection : true,
30127             mode : 'local',
30128             placeholder : this.yearPlaceholder,
30129             selectOnFocus : true,
30130             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30131             triggerAction : 'all',
30132             typeAhead : true,
30133             valueField : 'value',
30134             store : new Roo.data.SimpleStore({
30135                 data : (function() {
30136                     var years = [];
30137                     _this.fireEvent('years', _this, years);
30138                     return years;
30139                 })(),
30140                 fields : [ 'value' ]
30141             }),
30142             listeners : {
30143                 select : function (_self, record, index)
30144                 {
30145                     _this.setValue(_this.getValue());
30146                 }
30147             }
30148         });
30149
30150         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30151     },
30152     
30153     setValue : function(v, format)
30154     {
30155         this.inputEl.dom.value = v;
30156         
30157         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30158         
30159         var d = Date.parseDate(v, f);
30160         
30161         if(!d){
30162             this.validate();
30163             return;
30164         }
30165         
30166         this.setDay(d.format(this.dayFormat));
30167         this.setMonth(d.format(this.monthFormat));
30168         this.setYear(d.format(this.yearFormat));
30169         
30170         this.validate();
30171         
30172         return;
30173     },
30174     
30175     setDay : function(v)
30176     {
30177         this.dayField.setValue(v);
30178         this.inputEl.dom.value = this.getValue();
30179         this.validate();
30180         return;
30181     },
30182     
30183     setMonth : function(v)
30184     {
30185         this.monthField.setValue(v, true);
30186         this.inputEl.dom.value = this.getValue();
30187         this.validate();
30188         return;
30189     },
30190     
30191     setYear : function(v)
30192     {
30193         this.yearField.setValue(v);
30194         this.inputEl.dom.value = this.getValue();
30195         this.validate();
30196         return;
30197     },
30198     
30199     getDay : function()
30200     {
30201         return this.dayField.getValue();
30202     },
30203     
30204     getMonth : function()
30205     {
30206         return this.monthField.getValue();
30207     },
30208     
30209     getYear : function()
30210     {
30211         return this.yearField.getValue();
30212     },
30213     
30214     getValue : function()
30215     {
30216         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30217         
30218         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30219         
30220         return date;
30221     },
30222     
30223     reset : function()
30224     {
30225         this.setDay('');
30226         this.setMonth('');
30227         this.setYear('');
30228         this.inputEl.dom.value = '';
30229         this.validate();
30230         return;
30231     },
30232     
30233     validate : function()
30234     {
30235         var d = this.dayField.validate();
30236         var m = this.monthField.validate();
30237         var y = this.yearField.validate();
30238         
30239         var valid = true;
30240         
30241         if(
30242                 (!this.dayAllowBlank && !d) ||
30243                 (!this.monthAllowBlank && !m) ||
30244                 (!this.yearAllowBlank && !y)
30245         ){
30246             valid = false;
30247         }
30248         
30249         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30250             return valid;
30251         }
30252         
30253         if(valid){
30254             this.markValid();
30255             return valid;
30256         }
30257         
30258         this.markInvalid();
30259         
30260         return valid;
30261     },
30262     
30263     markValid : function()
30264     {
30265         
30266         var label = this.el.select('label', true).first();
30267         var icon = this.el.select('i.fa-star', true).first();
30268
30269         if(label && icon){
30270             icon.remove();
30271         }
30272         
30273         this.fireEvent('valid', this);
30274     },
30275     
30276      /**
30277      * Mark this field as invalid
30278      * @param {String} msg The validation message
30279      */
30280     markInvalid : function(msg)
30281     {
30282         
30283         var label = this.el.select('label', true).first();
30284         var icon = this.el.select('i.fa-star', true).first();
30285
30286         if(label && !icon){
30287             this.el.select('.roo-date-split-field-label', true).createChild({
30288                 tag : 'i',
30289                 cls : 'text-danger fa fa-lg fa-star',
30290                 tooltip : 'This field is required',
30291                 style : 'margin-right:5px;'
30292             }, label, true);
30293         }
30294         
30295         this.fireEvent('invalid', this, msg);
30296     },
30297     
30298     clearInvalid : function()
30299     {
30300         var label = this.el.select('label', true).first();
30301         var icon = this.el.select('i.fa-star', true).first();
30302
30303         if(label && icon){
30304             icon.remove();
30305         }
30306         
30307         this.fireEvent('valid', this);
30308     },
30309     
30310     getName: function()
30311     {
30312         return this.name;
30313     }
30314     
30315 });
30316
30317  /**
30318  *
30319  * This is based on 
30320  * http://masonry.desandro.com
30321  *
30322  * The idea is to render all the bricks based on vertical width...
30323  *
30324  * The original code extends 'outlayer' - we might need to use that....
30325  * 
30326  */
30327
30328
30329 /**
30330  * @class Roo.bootstrap.LayoutMasonry
30331  * @extends Roo.bootstrap.Component
30332  * Bootstrap Layout Masonry class
30333  * 
30334  * @constructor
30335  * Create a new Element
30336  * @param {Object} config The config object
30337  */
30338
30339 Roo.bootstrap.LayoutMasonry = function(config){
30340     
30341     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30342     
30343     this.bricks = [];
30344     
30345     Roo.bootstrap.LayoutMasonry.register(this);
30346     
30347     this.addEvents({
30348         // raw events
30349         /**
30350          * @event layout
30351          * Fire after layout the items
30352          * @param {Roo.bootstrap.LayoutMasonry} this
30353          * @param {Roo.EventObject} e
30354          */
30355         "layout" : true
30356     });
30357     
30358 };
30359
30360 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30361     
30362     /**
30363      * @cfg {Boolean} isLayoutInstant = no animation?
30364      */   
30365     isLayoutInstant : false, // needed?
30366    
30367     /**
30368      * @cfg {Number} boxWidth  width of the columns
30369      */   
30370     boxWidth : 450,
30371     
30372       /**
30373      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30374      */   
30375     boxHeight : 0,
30376     
30377     /**
30378      * @cfg {Number} padWidth padding below box..
30379      */   
30380     padWidth : 10, 
30381     
30382     /**
30383      * @cfg {Number} gutter gutter width..
30384      */   
30385     gutter : 10,
30386     
30387      /**
30388      * @cfg {Number} maxCols maximum number of columns
30389      */   
30390     
30391     maxCols: 0,
30392     
30393     /**
30394      * @cfg {Boolean} isAutoInitial defalut true
30395      */   
30396     isAutoInitial : true, 
30397     
30398     containerWidth: 0,
30399     
30400     /**
30401      * @cfg {Boolean} isHorizontal defalut false
30402      */   
30403     isHorizontal : false, 
30404
30405     currentSize : null,
30406     
30407     tag: 'div',
30408     
30409     cls: '',
30410     
30411     bricks: null, //CompositeElement
30412     
30413     cols : 1,
30414     
30415     _isLayoutInited : false,
30416     
30417 //    isAlternative : false, // only use for vertical layout...
30418     
30419     /**
30420      * @cfg {Number} alternativePadWidth padding below box..
30421      */   
30422     alternativePadWidth : 50,
30423     
30424     selectedBrick : [],
30425     
30426     getAutoCreate : function(){
30427         
30428         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30429         
30430         var cfg = {
30431             tag: this.tag,
30432             cls: 'blog-masonary-wrapper ' + this.cls,
30433             cn : {
30434                 cls : 'mas-boxes masonary'
30435             }
30436         };
30437         
30438         return cfg;
30439     },
30440     
30441     getChildContainer: function( )
30442     {
30443         if (this.boxesEl) {
30444             return this.boxesEl;
30445         }
30446         
30447         this.boxesEl = this.el.select('.mas-boxes').first();
30448         
30449         return this.boxesEl;
30450     },
30451     
30452     
30453     initEvents : function()
30454     {
30455         var _this = this;
30456         
30457         if(this.isAutoInitial){
30458             Roo.log('hook children rendered');
30459             this.on('childrenrendered', function() {
30460                 Roo.log('children rendered');
30461                 _this.initial();
30462             } ,this);
30463         }
30464     },
30465     
30466     initial : function()
30467     {
30468         this.selectedBrick = [];
30469         
30470         this.currentSize = this.el.getBox(true);
30471         
30472         Roo.EventManager.onWindowResize(this.resize, this); 
30473
30474         if(!this.isAutoInitial){
30475             this.layout();
30476             return;
30477         }
30478         
30479         this.layout();
30480         
30481         return;
30482         //this.layout.defer(500,this);
30483         
30484     },
30485     
30486     resize : function()
30487     {
30488         var cs = this.el.getBox(true);
30489         
30490         if (
30491                 this.currentSize.width == cs.width && 
30492                 this.currentSize.x == cs.x && 
30493                 this.currentSize.height == cs.height && 
30494                 this.currentSize.y == cs.y 
30495         ) {
30496             Roo.log("no change in with or X or Y");
30497             return;
30498         }
30499         
30500         this.currentSize = cs;
30501         
30502         this.layout();
30503         
30504     },
30505     
30506     layout : function()
30507     {   
30508         this._resetLayout();
30509         
30510         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30511         
30512         this.layoutItems( isInstant );
30513       
30514         this._isLayoutInited = true;
30515         
30516         this.fireEvent('layout', this);
30517         
30518     },
30519     
30520     _resetLayout : function()
30521     {
30522         if(this.isHorizontal){
30523             this.horizontalMeasureColumns();
30524             return;
30525         }
30526         
30527         this.verticalMeasureColumns();
30528         
30529     },
30530     
30531     verticalMeasureColumns : function()
30532     {
30533         this.getContainerWidth();
30534         
30535 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30536 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30537 //            return;
30538 //        }
30539         
30540         var boxWidth = this.boxWidth + this.padWidth;
30541         
30542         if(this.containerWidth < this.boxWidth){
30543             boxWidth = this.containerWidth
30544         }
30545         
30546         var containerWidth = this.containerWidth;
30547         
30548         var cols = Math.floor(containerWidth / boxWidth);
30549         
30550         this.cols = Math.max( cols, 1 );
30551         
30552         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30553         
30554         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30555         
30556         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30557         
30558         this.colWidth = boxWidth + avail - this.padWidth;
30559         
30560         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30561         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30562     },
30563     
30564     horizontalMeasureColumns : function()
30565     {
30566         this.getContainerWidth();
30567         
30568         var boxWidth = this.boxWidth;
30569         
30570         if(this.containerWidth < boxWidth){
30571             boxWidth = this.containerWidth;
30572         }
30573         
30574         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30575         
30576         this.el.setHeight(boxWidth);
30577         
30578     },
30579     
30580     getContainerWidth : function()
30581     {
30582         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30583     },
30584     
30585     layoutItems : function( isInstant )
30586     {
30587         Roo.log(this.bricks);
30588         
30589         var items = Roo.apply([], this.bricks);
30590         
30591         if(this.isHorizontal){
30592             this._horizontalLayoutItems( items , isInstant );
30593             return;
30594         }
30595         
30596 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30597 //            this._verticalAlternativeLayoutItems( items , isInstant );
30598 //            return;
30599 //        }
30600         
30601         this._verticalLayoutItems( items , isInstant );
30602         
30603     },
30604     
30605     _verticalLayoutItems : function ( items , isInstant)
30606     {
30607         if ( !items || !items.length ) {
30608             return;
30609         }
30610         
30611         var standard = [
30612             ['xs', 'xs', 'xs', 'tall'],
30613             ['xs', 'xs', 'tall'],
30614             ['xs', 'xs', 'sm'],
30615             ['xs', 'xs', 'xs'],
30616             ['xs', 'tall'],
30617             ['xs', 'sm'],
30618             ['xs', 'xs'],
30619             ['xs'],
30620             
30621             ['sm', 'xs', 'xs'],
30622             ['sm', 'xs'],
30623             ['sm'],
30624             
30625             ['tall', 'xs', 'xs', 'xs'],
30626             ['tall', 'xs', 'xs'],
30627             ['tall', 'xs'],
30628             ['tall']
30629             
30630         ];
30631         
30632         var queue = [];
30633         
30634         var boxes = [];
30635         
30636         var box = [];
30637         
30638         Roo.each(items, function(item, k){
30639             
30640             switch (item.size) {
30641                 // these layouts take up a full box,
30642                 case 'md' :
30643                 case 'md-left' :
30644                 case 'md-right' :
30645                 case 'wide' :
30646                     
30647                     if(box.length){
30648                         boxes.push(box);
30649                         box = [];
30650                     }
30651                     
30652                     boxes.push([item]);
30653                     
30654                     break;
30655                     
30656                 case 'xs' :
30657                 case 'sm' :
30658                 case 'tall' :
30659                     
30660                     box.push(item);
30661                     
30662                     break;
30663                 default :
30664                     break;
30665                     
30666             }
30667             
30668         }, this);
30669         
30670         if(box.length){
30671             boxes.push(box);
30672             box = [];
30673         }
30674         
30675         var filterPattern = function(box, length)
30676         {
30677             if(!box.length){
30678                 return;
30679             }
30680             
30681             var match = false;
30682             
30683             var pattern = box.slice(0, length);
30684             
30685             var format = [];
30686             
30687             Roo.each(pattern, function(i){
30688                 format.push(i.size);
30689             }, this);
30690             
30691             Roo.each(standard, function(s){
30692                 
30693                 if(String(s) != String(format)){
30694                     return;
30695                 }
30696                 
30697                 match = true;
30698                 return false;
30699                 
30700             }, this);
30701             
30702             if(!match && length == 1){
30703                 return;
30704             }
30705             
30706             if(!match){
30707                 filterPattern(box, length - 1);
30708                 return;
30709             }
30710                 
30711             queue.push(pattern);
30712
30713             box = box.slice(length, box.length);
30714
30715             filterPattern(box, 4);
30716
30717             return;
30718             
30719         }
30720         
30721         Roo.each(boxes, function(box, k){
30722             
30723             if(!box.length){
30724                 return;
30725             }
30726             
30727             if(box.length == 1){
30728                 queue.push(box);
30729                 return;
30730             }
30731             
30732             filterPattern(box, 4);
30733             
30734         }, this);
30735         
30736         this._processVerticalLayoutQueue( queue, isInstant );
30737         
30738     },
30739     
30740 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30741 //    {
30742 //        if ( !items || !items.length ) {
30743 //            return;
30744 //        }
30745 //
30746 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30747 //        
30748 //    },
30749     
30750     _horizontalLayoutItems : function ( items , isInstant)
30751     {
30752         if ( !items || !items.length || items.length < 3) {
30753             return;
30754         }
30755         
30756         items.reverse();
30757         
30758         var eItems = items.slice(0, 3);
30759         
30760         items = items.slice(3, items.length);
30761         
30762         var standard = [
30763             ['xs', 'xs', 'xs', 'wide'],
30764             ['xs', 'xs', 'wide'],
30765             ['xs', 'xs', 'sm'],
30766             ['xs', 'xs', 'xs'],
30767             ['xs', 'wide'],
30768             ['xs', 'sm'],
30769             ['xs', 'xs'],
30770             ['xs'],
30771             
30772             ['sm', 'xs', 'xs'],
30773             ['sm', 'xs'],
30774             ['sm'],
30775             
30776             ['wide', 'xs', 'xs', 'xs'],
30777             ['wide', 'xs', 'xs'],
30778             ['wide', 'xs'],
30779             ['wide'],
30780             
30781             ['wide-thin']
30782         ];
30783         
30784         var queue = [];
30785         
30786         var boxes = [];
30787         
30788         var box = [];
30789         
30790         Roo.each(items, function(item, k){
30791             
30792             switch (item.size) {
30793                 case 'md' :
30794                 case 'md-left' :
30795                 case 'md-right' :
30796                 case 'tall' :
30797                     
30798                     if(box.length){
30799                         boxes.push(box);
30800                         box = [];
30801                     }
30802                     
30803                     boxes.push([item]);
30804                     
30805                     break;
30806                     
30807                 case 'xs' :
30808                 case 'sm' :
30809                 case 'wide' :
30810                 case 'wide-thin' :
30811                     
30812                     box.push(item);
30813                     
30814                     break;
30815                 default :
30816                     break;
30817                     
30818             }
30819             
30820         }, this);
30821         
30822         if(box.length){
30823             boxes.push(box);
30824             box = [];
30825         }
30826         
30827         var filterPattern = function(box, length)
30828         {
30829             if(!box.length){
30830                 return;
30831             }
30832             
30833             var match = false;
30834             
30835             var pattern = box.slice(0, length);
30836             
30837             var format = [];
30838             
30839             Roo.each(pattern, function(i){
30840                 format.push(i.size);
30841             }, this);
30842             
30843             Roo.each(standard, function(s){
30844                 
30845                 if(String(s) != String(format)){
30846                     return;
30847                 }
30848                 
30849                 match = true;
30850                 return false;
30851                 
30852             }, this);
30853             
30854             if(!match && length == 1){
30855                 return;
30856             }
30857             
30858             if(!match){
30859                 filterPattern(box, length - 1);
30860                 return;
30861             }
30862                 
30863             queue.push(pattern);
30864
30865             box = box.slice(length, box.length);
30866
30867             filterPattern(box, 4);
30868
30869             return;
30870             
30871         }
30872         
30873         Roo.each(boxes, function(box, k){
30874             
30875             if(!box.length){
30876                 return;
30877             }
30878             
30879             if(box.length == 1){
30880                 queue.push(box);
30881                 return;
30882             }
30883             
30884             filterPattern(box, 4);
30885             
30886         }, this);
30887         
30888         
30889         var prune = [];
30890         
30891         var pos = this.el.getBox(true);
30892         
30893         var minX = pos.x;
30894         
30895         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30896         
30897         var hit_end = false;
30898         
30899         Roo.each(queue, function(box){
30900             
30901             if(hit_end){
30902                 
30903                 Roo.each(box, function(b){
30904                 
30905                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30906                     b.el.hide();
30907
30908                 }, this);
30909
30910                 return;
30911             }
30912             
30913             var mx = 0;
30914             
30915             Roo.each(box, function(b){
30916                 
30917                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30918                 b.el.show();
30919
30920                 mx = Math.max(mx, b.x);
30921                 
30922             }, this);
30923             
30924             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30925             
30926             if(maxX < minX){
30927                 
30928                 Roo.each(box, function(b){
30929                 
30930                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30931                     b.el.hide();
30932                     
30933                 }, this);
30934                 
30935                 hit_end = true;
30936                 
30937                 return;
30938             }
30939             
30940             prune.push(box);
30941             
30942         }, this);
30943         
30944         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30945     },
30946     
30947     /** Sets position of item in DOM
30948     * @param {Element} item
30949     * @param {Number} x - horizontal position
30950     * @param {Number} y - vertical position
30951     * @param {Boolean} isInstant - disables transitions
30952     */
30953     _processVerticalLayoutQueue : function( queue, isInstant )
30954     {
30955         var pos = this.el.getBox(true);
30956         var x = pos.x;
30957         var y = pos.y;
30958         var maxY = [];
30959         
30960         for (var i = 0; i < this.cols; i++){
30961             maxY[i] = pos.y;
30962         }
30963         
30964         Roo.each(queue, function(box, k){
30965             
30966             var col = k % this.cols;
30967             
30968             Roo.each(box, function(b,kk){
30969                 
30970                 b.el.position('absolute');
30971                 
30972                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30973                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30974                 
30975                 if(b.size == 'md-left' || b.size == 'md-right'){
30976                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30977                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30978                 }
30979                 
30980                 b.el.setWidth(width);
30981                 b.el.setHeight(height);
30982                 // iframe?
30983                 b.el.select('iframe',true).setSize(width,height);
30984                 
30985             }, this);
30986             
30987             for (var i = 0; i < this.cols; i++){
30988                 
30989                 if(maxY[i] < maxY[col]){
30990                     col = i;
30991                     continue;
30992                 }
30993                 
30994                 col = Math.min(col, i);
30995                 
30996             }
30997             
30998             x = pos.x + col * (this.colWidth + this.padWidth);
30999             
31000             y = maxY[col];
31001             
31002             var positions = [];
31003             
31004             switch (box.length){
31005                 case 1 :
31006                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31007                     break;
31008                 case 2 :
31009                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31010                     break;
31011                 case 3 :
31012                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31013                     break;
31014                 case 4 :
31015                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31016                     break;
31017                 default :
31018                     break;
31019             }
31020             
31021             Roo.each(box, function(b,kk){
31022                 
31023                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31024                 
31025                 var sz = b.el.getSize();
31026                 
31027                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31028                 
31029             }, this);
31030             
31031         }, this);
31032         
31033         var mY = 0;
31034         
31035         for (var i = 0; i < this.cols; i++){
31036             mY = Math.max(mY, maxY[i]);
31037         }
31038         
31039         this.el.setHeight(mY - pos.y);
31040         
31041     },
31042     
31043 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31044 //    {
31045 //        var pos = this.el.getBox(true);
31046 //        var x = pos.x;
31047 //        var y = pos.y;
31048 //        var maxX = pos.right;
31049 //        
31050 //        var maxHeight = 0;
31051 //        
31052 //        Roo.each(items, function(item, k){
31053 //            
31054 //            var c = k % 2;
31055 //            
31056 //            item.el.position('absolute');
31057 //                
31058 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31059 //
31060 //            item.el.setWidth(width);
31061 //
31062 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31063 //
31064 //            item.el.setHeight(height);
31065 //            
31066 //            if(c == 0){
31067 //                item.el.setXY([x, y], isInstant ? false : true);
31068 //            } else {
31069 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31070 //            }
31071 //            
31072 //            y = y + height + this.alternativePadWidth;
31073 //            
31074 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31075 //            
31076 //        }, this);
31077 //        
31078 //        this.el.setHeight(maxHeight);
31079 //        
31080 //    },
31081     
31082     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31083     {
31084         var pos = this.el.getBox(true);
31085         
31086         var minX = pos.x;
31087         var minY = pos.y;
31088         
31089         var maxX = pos.right;
31090         
31091         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31092         
31093         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31094         
31095         Roo.each(queue, function(box, k){
31096             
31097             Roo.each(box, function(b, kk){
31098                 
31099                 b.el.position('absolute');
31100                 
31101                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31102                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31103                 
31104                 if(b.size == 'md-left' || b.size == 'md-right'){
31105                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31106                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31107                 }
31108                 
31109                 b.el.setWidth(width);
31110                 b.el.setHeight(height);
31111                 
31112             }, this);
31113             
31114             if(!box.length){
31115                 return;
31116             }
31117             
31118             var positions = [];
31119             
31120             switch (box.length){
31121                 case 1 :
31122                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31123                     break;
31124                 case 2 :
31125                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31126                     break;
31127                 case 3 :
31128                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31129                     break;
31130                 case 4 :
31131                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31132                     break;
31133                 default :
31134                     break;
31135             }
31136             
31137             Roo.each(box, function(b,kk){
31138                 
31139                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31140                 
31141                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31142                 
31143             }, this);
31144             
31145         }, this);
31146         
31147     },
31148     
31149     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31150     {
31151         Roo.each(eItems, function(b,k){
31152             
31153             b.size = (k == 0) ? 'sm' : 'xs';
31154             b.x = (k == 0) ? 2 : 1;
31155             b.y = (k == 0) ? 2 : 1;
31156             
31157             b.el.position('absolute');
31158             
31159             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31160                 
31161             b.el.setWidth(width);
31162             
31163             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31164             
31165             b.el.setHeight(height);
31166             
31167         }, this);
31168
31169         var positions = [];
31170         
31171         positions.push({
31172             x : maxX - this.unitWidth * 2 - this.gutter,
31173             y : minY
31174         });
31175         
31176         positions.push({
31177             x : maxX - this.unitWidth,
31178             y : minY + (this.unitWidth + this.gutter) * 2
31179         });
31180         
31181         positions.push({
31182             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31183             y : minY
31184         });
31185         
31186         Roo.each(eItems, function(b,k){
31187             
31188             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31189
31190         }, this);
31191         
31192     },
31193     
31194     getVerticalOneBoxColPositions : function(x, y, box)
31195     {
31196         var pos = [];
31197         
31198         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31199         
31200         if(box[0].size == 'md-left'){
31201             rand = 0;
31202         }
31203         
31204         if(box[0].size == 'md-right'){
31205             rand = 1;
31206         }
31207         
31208         pos.push({
31209             x : x + (this.unitWidth + this.gutter) * rand,
31210             y : y
31211         });
31212         
31213         return pos;
31214     },
31215     
31216     getVerticalTwoBoxColPositions : function(x, y, box)
31217     {
31218         var pos = [];
31219         
31220         if(box[0].size == 'xs'){
31221             
31222             pos.push({
31223                 x : x,
31224                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31225             });
31226
31227             pos.push({
31228                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31229                 y : y
31230             });
31231             
31232             return pos;
31233             
31234         }
31235         
31236         pos.push({
31237             x : x,
31238             y : y
31239         });
31240
31241         pos.push({
31242             x : x + (this.unitWidth + this.gutter) * 2,
31243             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31244         });
31245         
31246         return pos;
31247         
31248     },
31249     
31250     getVerticalThreeBoxColPositions : function(x, y, box)
31251     {
31252         var pos = [];
31253         
31254         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31255             
31256             pos.push({
31257                 x : x,
31258                 y : y
31259             });
31260
31261             pos.push({
31262                 x : x + (this.unitWidth + this.gutter) * 1,
31263                 y : y
31264             });
31265             
31266             pos.push({
31267                 x : x + (this.unitWidth + this.gutter) * 2,
31268                 y : y
31269             });
31270             
31271             return pos;
31272             
31273         }
31274         
31275         if(box[0].size == 'xs' && box[1].size == 'xs'){
31276             
31277             pos.push({
31278                 x : x,
31279                 y : y
31280             });
31281
31282             pos.push({
31283                 x : x,
31284                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31285             });
31286             
31287             pos.push({
31288                 x : x + (this.unitWidth + this.gutter) * 1,
31289                 y : y
31290             });
31291             
31292             return pos;
31293             
31294         }
31295         
31296         pos.push({
31297             x : x,
31298             y : y
31299         });
31300
31301         pos.push({
31302             x : x + (this.unitWidth + this.gutter) * 2,
31303             y : y
31304         });
31305
31306         pos.push({
31307             x : x + (this.unitWidth + this.gutter) * 2,
31308             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31309         });
31310             
31311         return pos;
31312         
31313     },
31314     
31315     getVerticalFourBoxColPositions : function(x, y, box)
31316     {
31317         var pos = [];
31318         
31319         if(box[0].size == 'xs'){
31320             
31321             pos.push({
31322                 x : x,
31323                 y : y
31324             });
31325
31326             pos.push({
31327                 x : x,
31328                 y : y + (this.unitHeight + this.gutter) * 1
31329             });
31330             
31331             pos.push({
31332                 x : x,
31333                 y : y + (this.unitHeight + this.gutter) * 2
31334             });
31335             
31336             pos.push({
31337                 x : x + (this.unitWidth + this.gutter) * 1,
31338                 y : y
31339             });
31340             
31341             return pos;
31342             
31343         }
31344         
31345         pos.push({
31346             x : x,
31347             y : y
31348         });
31349
31350         pos.push({
31351             x : x + (this.unitWidth + this.gutter) * 2,
31352             y : y
31353         });
31354
31355         pos.push({
31356             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31357             y : y + (this.unitHeight + this.gutter) * 1
31358         });
31359
31360         pos.push({
31361             x : x + (this.unitWidth + this.gutter) * 2,
31362             y : y + (this.unitWidth + this.gutter) * 2
31363         });
31364
31365         return pos;
31366         
31367     },
31368     
31369     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31370     {
31371         var pos = [];
31372         
31373         if(box[0].size == 'md-left'){
31374             pos.push({
31375                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31376                 y : minY
31377             });
31378             
31379             return pos;
31380         }
31381         
31382         if(box[0].size == 'md-right'){
31383             pos.push({
31384                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31385                 y : minY + (this.unitWidth + this.gutter) * 1
31386             });
31387             
31388             return pos;
31389         }
31390         
31391         var rand = Math.floor(Math.random() * (4 - box[0].y));
31392         
31393         pos.push({
31394             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31395             y : minY + (this.unitWidth + this.gutter) * rand
31396         });
31397         
31398         return pos;
31399         
31400     },
31401     
31402     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31403     {
31404         var pos = [];
31405         
31406         if(box[0].size == 'xs'){
31407             
31408             pos.push({
31409                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31410                 y : minY
31411             });
31412
31413             pos.push({
31414                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31415                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31416             });
31417             
31418             return pos;
31419             
31420         }
31421         
31422         pos.push({
31423             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31424             y : minY
31425         });
31426
31427         pos.push({
31428             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31429             y : minY + (this.unitWidth + this.gutter) * 2
31430         });
31431         
31432         return pos;
31433         
31434     },
31435     
31436     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31437     {
31438         var pos = [];
31439         
31440         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31441             
31442             pos.push({
31443                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31444                 y : minY
31445             });
31446
31447             pos.push({
31448                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31449                 y : minY + (this.unitWidth + this.gutter) * 1
31450             });
31451             
31452             pos.push({
31453                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31454                 y : minY + (this.unitWidth + this.gutter) * 2
31455             });
31456             
31457             return pos;
31458             
31459         }
31460         
31461         if(box[0].size == 'xs' && box[1].size == 'xs'){
31462             
31463             pos.push({
31464                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31465                 y : minY
31466             });
31467
31468             pos.push({
31469                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31470                 y : minY
31471             });
31472             
31473             pos.push({
31474                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31475                 y : minY + (this.unitWidth + this.gutter) * 1
31476             });
31477             
31478             return pos;
31479             
31480         }
31481         
31482         pos.push({
31483             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31484             y : minY
31485         });
31486
31487         pos.push({
31488             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31489             y : minY + (this.unitWidth + this.gutter) * 2
31490         });
31491
31492         pos.push({
31493             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31494             y : minY + (this.unitWidth + this.gutter) * 2
31495         });
31496             
31497         return pos;
31498         
31499     },
31500     
31501     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31502     {
31503         var pos = [];
31504         
31505         if(box[0].size == 'xs'){
31506             
31507             pos.push({
31508                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31509                 y : minY
31510             });
31511
31512             pos.push({
31513                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31514                 y : minY
31515             });
31516             
31517             pos.push({
31518                 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),
31519                 y : minY
31520             });
31521             
31522             pos.push({
31523                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31524                 y : minY + (this.unitWidth + this.gutter) * 1
31525             });
31526             
31527             return pos;
31528             
31529         }
31530         
31531         pos.push({
31532             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31533             y : minY
31534         });
31535         
31536         pos.push({
31537             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31538             y : minY + (this.unitWidth + this.gutter) * 2
31539         });
31540         
31541         pos.push({
31542             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31543             y : minY + (this.unitWidth + this.gutter) * 2
31544         });
31545         
31546         pos.push({
31547             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),
31548             y : minY + (this.unitWidth + this.gutter) * 2
31549         });
31550
31551         return pos;
31552         
31553     },
31554     
31555     /**
31556     * remove a Masonry Brick
31557     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31558     */
31559     removeBrick : function(brick_id)
31560     {
31561         if (!brick_id) {
31562             return;
31563         }
31564         
31565         for (var i = 0; i<this.bricks.length; i++) {
31566             if (this.bricks[i].id == brick_id) {
31567                 this.bricks.splice(i,1);
31568                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31569                 this.initial();
31570             }
31571         }
31572     },
31573     
31574     /**
31575     * adds a Masonry Brick
31576     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31577     */
31578     addBrick : function(cfg)
31579     {
31580         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31581         //this.register(cn);
31582         cn.parentId = this.id;
31583         cn.onRender(this.el, null);
31584         return cn;
31585     },
31586     
31587     /**
31588     * register a Masonry Brick
31589     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31590     */
31591     
31592     register : function(brick)
31593     {
31594         this.bricks.push(brick);
31595         brick.masonryId = this.id;
31596     },
31597     
31598     /**
31599     * clear all the Masonry Brick
31600     */
31601     clearAll : function()
31602     {
31603         this.bricks = [];
31604         //this.getChildContainer().dom.innerHTML = "";
31605         this.el.dom.innerHTML = '';
31606     },
31607     
31608     getSelected : function()
31609     {
31610         if (!this.selectedBrick) {
31611             return false;
31612         }
31613         
31614         return this.selectedBrick;
31615     }
31616 });
31617
31618 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31619     
31620     groups: {},
31621      /**
31622     * register a Masonry Layout
31623     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31624     */
31625     
31626     register : function(layout)
31627     {
31628         this.groups[layout.id] = layout;
31629     },
31630     /**
31631     * fetch a  Masonry Layout based on the masonry layout ID
31632     * @param {string} the masonry layout to add
31633     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31634     */
31635     
31636     get: function(layout_id) {
31637         if (typeof(this.groups[layout_id]) == 'undefined') {
31638             return false;
31639         }
31640         return this.groups[layout_id] ;
31641     }
31642     
31643     
31644     
31645 });
31646
31647  
31648
31649  /**
31650  *
31651  * This is based on 
31652  * http://masonry.desandro.com
31653  *
31654  * The idea is to render all the bricks based on vertical width...
31655  *
31656  * The original code extends 'outlayer' - we might need to use that....
31657  * 
31658  */
31659
31660
31661 /**
31662  * @class Roo.bootstrap.LayoutMasonryAuto
31663  * @extends Roo.bootstrap.Component
31664  * Bootstrap Layout Masonry class
31665  * 
31666  * @constructor
31667  * Create a new Element
31668  * @param {Object} config The config object
31669  */
31670
31671 Roo.bootstrap.LayoutMasonryAuto = function(config){
31672     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31673 };
31674
31675 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31676     
31677       /**
31678      * @cfg {Boolean} isFitWidth  - resize the width..
31679      */   
31680     isFitWidth : false,  // options..
31681     /**
31682      * @cfg {Boolean} isOriginLeft = left align?
31683      */   
31684     isOriginLeft : true,
31685     /**
31686      * @cfg {Boolean} isOriginTop = top align?
31687      */   
31688     isOriginTop : false,
31689     /**
31690      * @cfg {Boolean} isLayoutInstant = no animation?
31691      */   
31692     isLayoutInstant : false, // needed?
31693     /**
31694      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31695      */   
31696     isResizingContainer : true,
31697     /**
31698      * @cfg {Number} columnWidth  width of the columns 
31699      */   
31700     
31701     columnWidth : 0,
31702     
31703     /**
31704      * @cfg {Number} maxCols maximum number of columns
31705      */   
31706     
31707     maxCols: 0,
31708     /**
31709      * @cfg {Number} padHeight padding below box..
31710      */   
31711     
31712     padHeight : 10, 
31713     
31714     /**
31715      * @cfg {Boolean} isAutoInitial defalut true
31716      */   
31717     
31718     isAutoInitial : true, 
31719     
31720     // private?
31721     gutter : 0,
31722     
31723     containerWidth: 0,
31724     initialColumnWidth : 0,
31725     currentSize : null,
31726     
31727     colYs : null, // array.
31728     maxY : 0,
31729     padWidth: 10,
31730     
31731     
31732     tag: 'div',
31733     cls: '',
31734     bricks: null, //CompositeElement
31735     cols : 0, // array?
31736     // element : null, // wrapped now this.el
31737     _isLayoutInited : null, 
31738     
31739     
31740     getAutoCreate : function(){
31741         
31742         var cfg = {
31743             tag: this.tag,
31744             cls: 'blog-masonary-wrapper ' + this.cls,
31745             cn : {
31746                 cls : 'mas-boxes masonary'
31747             }
31748         };
31749         
31750         return cfg;
31751     },
31752     
31753     getChildContainer: function( )
31754     {
31755         if (this.boxesEl) {
31756             return this.boxesEl;
31757         }
31758         
31759         this.boxesEl = this.el.select('.mas-boxes').first();
31760         
31761         return this.boxesEl;
31762     },
31763     
31764     
31765     initEvents : function()
31766     {
31767         var _this = this;
31768         
31769         if(this.isAutoInitial){
31770             Roo.log('hook children rendered');
31771             this.on('childrenrendered', function() {
31772                 Roo.log('children rendered');
31773                 _this.initial();
31774             } ,this);
31775         }
31776         
31777     },
31778     
31779     initial : function()
31780     {
31781         this.reloadItems();
31782
31783         this.currentSize = this.el.getBox(true);
31784
31785         /// was window resize... - let's see if this works..
31786         Roo.EventManager.onWindowResize(this.resize, this); 
31787
31788         if(!this.isAutoInitial){
31789             this.layout();
31790             return;
31791         }
31792         
31793         this.layout.defer(500,this);
31794     },
31795     
31796     reloadItems: function()
31797     {
31798         this.bricks = this.el.select('.masonry-brick', true);
31799         
31800         this.bricks.each(function(b) {
31801             //Roo.log(b.getSize());
31802             if (!b.attr('originalwidth')) {
31803                 b.attr('originalwidth',  b.getSize().width);
31804             }
31805             
31806         });
31807         
31808         Roo.log(this.bricks.elements.length);
31809     },
31810     
31811     resize : function()
31812     {
31813         Roo.log('resize');
31814         var cs = this.el.getBox(true);
31815         
31816         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31817             Roo.log("no change in with or X");
31818             return;
31819         }
31820         this.currentSize = cs;
31821         this.layout();
31822     },
31823     
31824     layout : function()
31825     {
31826          Roo.log('layout');
31827         this._resetLayout();
31828         //this._manageStamps();
31829       
31830         // don't animate first layout
31831         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31832         this.layoutItems( isInstant );
31833       
31834         // flag for initalized
31835         this._isLayoutInited = true;
31836     },
31837     
31838     layoutItems : function( isInstant )
31839     {
31840         //var items = this._getItemsForLayout( this.items );
31841         // original code supports filtering layout items.. we just ignore it..
31842         
31843         this._layoutItems( this.bricks , isInstant );
31844       
31845         this._postLayout();
31846     },
31847     _layoutItems : function ( items , isInstant)
31848     {
31849        //this.fireEvent( 'layout', this, items );
31850     
31851
31852         if ( !items || !items.elements.length ) {
31853           // no items, emit event with empty array
31854             return;
31855         }
31856
31857         var queue = [];
31858         items.each(function(item) {
31859             Roo.log("layout item");
31860             Roo.log(item);
31861             // get x/y object from method
31862             var position = this._getItemLayoutPosition( item );
31863             // enqueue
31864             position.item = item;
31865             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31866             queue.push( position );
31867         }, this);
31868       
31869         this._processLayoutQueue( queue );
31870     },
31871     /** Sets position of item in DOM
31872     * @param {Element} item
31873     * @param {Number} x - horizontal position
31874     * @param {Number} y - vertical position
31875     * @param {Boolean} isInstant - disables transitions
31876     */
31877     _processLayoutQueue : function( queue )
31878     {
31879         for ( var i=0, len = queue.length; i < len; i++ ) {
31880             var obj = queue[i];
31881             obj.item.position('absolute');
31882             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31883         }
31884     },
31885       
31886     
31887     /**
31888     * Any logic you want to do after each layout,
31889     * i.e. size the container
31890     */
31891     _postLayout : function()
31892     {
31893         this.resizeContainer();
31894     },
31895     
31896     resizeContainer : function()
31897     {
31898         if ( !this.isResizingContainer ) {
31899             return;
31900         }
31901         var size = this._getContainerSize();
31902         if ( size ) {
31903             this.el.setSize(size.width,size.height);
31904             this.boxesEl.setSize(size.width,size.height);
31905         }
31906     },
31907     
31908     
31909     
31910     _resetLayout : function()
31911     {
31912         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31913         this.colWidth = this.el.getWidth();
31914         //this.gutter = this.el.getWidth(); 
31915         
31916         this.measureColumns();
31917
31918         // reset column Y
31919         var i = this.cols;
31920         this.colYs = [];
31921         while (i--) {
31922             this.colYs.push( 0 );
31923         }
31924     
31925         this.maxY = 0;
31926     },
31927
31928     measureColumns : function()
31929     {
31930         this.getContainerWidth();
31931       // if columnWidth is 0, default to outerWidth of first item
31932         if ( !this.columnWidth ) {
31933             var firstItem = this.bricks.first();
31934             Roo.log(firstItem);
31935             this.columnWidth  = this.containerWidth;
31936             if (firstItem && firstItem.attr('originalwidth') ) {
31937                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31938             }
31939             // columnWidth fall back to item of first element
31940             Roo.log("set column width?");
31941                         this.initialColumnWidth = this.columnWidth  ;
31942
31943             // if first elem has no width, default to size of container
31944             
31945         }
31946         
31947         
31948         if (this.initialColumnWidth) {
31949             this.columnWidth = this.initialColumnWidth;
31950         }
31951         
31952         
31953             
31954         // column width is fixed at the top - however if container width get's smaller we should
31955         // reduce it...
31956         
31957         // this bit calcs how man columns..
31958             
31959         var columnWidth = this.columnWidth += this.gutter;
31960       
31961         // calculate columns
31962         var containerWidth = this.containerWidth + this.gutter;
31963         
31964         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31965         // fix rounding errors, typically with gutters
31966         var excess = columnWidth - containerWidth % columnWidth;
31967         
31968         
31969         // if overshoot is less than a pixel, round up, otherwise floor it
31970         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31971         cols = Math[ mathMethod ]( cols );
31972         this.cols = Math.max( cols, 1 );
31973         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31974         
31975          // padding positioning..
31976         var totalColWidth = this.cols * this.columnWidth;
31977         var padavail = this.containerWidth - totalColWidth;
31978         // so for 2 columns - we need 3 'pads'
31979         
31980         var padNeeded = (1+this.cols) * this.padWidth;
31981         
31982         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31983         
31984         this.columnWidth += padExtra
31985         //this.padWidth = Math.floor(padavail /  ( this.cols));
31986         
31987         // adjust colum width so that padding is fixed??
31988         
31989         // we have 3 columns ... total = width * 3
31990         // we have X left over... that should be used by 
31991         
31992         //if (this.expandC) {
31993             
31994         //}
31995         
31996         
31997         
31998     },
31999     
32000     getContainerWidth : function()
32001     {
32002        /* // container is parent if fit width
32003         var container = this.isFitWidth ? this.element.parentNode : this.element;
32004         // check that this.size and size are there
32005         // IE8 triggers resize on body size change, so they might not be
32006         
32007         var size = getSize( container );  //FIXME
32008         this.containerWidth = size && size.innerWidth; //FIXME
32009         */
32010          
32011         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32012         
32013     },
32014     
32015     _getItemLayoutPosition : function( item )  // what is item?
32016     {
32017         // we resize the item to our columnWidth..
32018       
32019         item.setWidth(this.columnWidth);
32020         item.autoBoxAdjust  = false;
32021         
32022         var sz = item.getSize();
32023  
32024         // how many columns does this brick span
32025         var remainder = this.containerWidth % this.columnWidth;
32026         
32027         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32028         // round if off by 1 pixel, otherwise use ceil
32029         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32030         colSpan = Math.min( colSpan, this.cols );
32031         
32032         // normally this should be '1' as we dont' currently allow multi width columns..
32033         
32034         var colGroup = this._getColGroup( colSpan );
32035         // get the minimum Y value from the columns
32036         var minimumY = Math.min.apply( Math, colGroup );
32037         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32038         
32039         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32040          
32041         // position the brick
32042         var position = {
32043             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32044             y: this.currentSize.y + minimumY + this.padHeight
32045         };
32046         
32047         Roo.log(position);
32048         // apply setHeight to necessary columns
32049         var setHeight = minimumY + sz.height + this.padHeight;
32050         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32051         
32052         var setSpan = this.cols + 1 - colGroup.length;
32053         for ( var i = 0; i < setSpan; i++ ) {
32054           this.colYs[ shortColIndex + i ] = setHeight ;
32055         }
32056       
32057         return position;
32058     },
32059     
32060     /**
32061      * @param {Number} colSpan - number of columns the element spans
32062      * @returns {Array} colGroup
32063      */
32064     _getColGroup : function( colSpan )
32065     {
32066         if ( colSpan < 2 ) {
32067           // if brick spans only one column, use all the column Ys
32068           return this.colYs;
32069         }
32070       
32071         var colGroup = [];
32072         // how many different places could this brick fit horizontally
32073         var groupCount = this.cols + 1 - colSpan;
32074         // for each group potential horizontal position
32075         for ( var i = 0; i < groupCount; i++ ) {
32076           // make an array of colY values for that one group
32077           var groupColYs = this.colYs.slice( i, i + colSpan );
32078           // and get the max value of the array
32079           colGroup[i] = Math.max.apply( Math, groupColYs );
32080         }
32081         return colGroup;
32082     },
32083     /*
32084     _manageStamp : function( stamp )
32085     {
32086         var stampSize =  stamp.getSize();
32087         var offset = stamp.getBox();
32088         // get the columns that this stamp affects
32089         var firstX = this.isOriginLeft ? offset.x : offset.right;
32090         var lastX = firstX + stampSize.width;
32091         var firstCol = Math.floor( firstX / this.columnWidth );
32092         firstCol = Math.max( 0, firstCol );
32093         
32094         var lastCol = Math.floor( lastX / this.columnWidth );
32095         // lastCol should not go over if multiple of columnWidth #425
32096         lastCol -= lastX % this.columnWidth ? 0 : 1;
32097         lastCol = Math.min( this.cols - 1, lastCol );
32098         
32099         // set colYs to bottom of the stamp
32100         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32101             stampSize.height;
32102             
32103         for ( var i = firstCol; i <= lastCol; i++ ) {
32104           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32105         }
32106     },
32107     */
32108     
32109     _getContainerSize : function()
32110     {
32111         this.maxY = Math.max.apply( Math, this.colYs );
32112         var size = {
32113             height: this.maxY
32114         };
32115       
32116         if ( this.isFitWidth ) {
32117             size.width = this._getContainerFitWidth();
32118         }
32119       
32120         return size;
32121     },
32122     
32123     _getContainerFitWidth : function()
32124     {
32125         var unusedCols = 0;
32126         // count unused columns
32127         var i = this.cols;
32128         while ( --i ) {
32129           if ( this.colYs[i] !== 0 ) {
32130             break;
32131           }
32132           unusedCols++;
32133         }
32134         // fit container to columns that have been used
32135         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32136     },
32137     
32138     needsResizeLayout : function()
32139     {
32140         var previousWidth = this.containerWidth;
32141         this.getContainerWidth();
32142         return previousWidth !== this.containerWidth;
32143     }
32144  
32145 });
32146
32147  
32148
32149  /*
32150  * - LGPL
32151  *
32152  * element
32153  * 
32154  */
32155
32156 /**
32157  * @class Roo.bootstrap.MasonryBrick
32158  * @extends Roo.bootstrap.Component
32159  * Bootstrap MasonryBrick class
32160  * 
32161  * @constructor
32162  * Create a new MasonryBrick
32163  * @param {Object} config The config object
32164  */
32165
32166 Roo.bootstrap.MasonryBrick = function(config){
32167     
32168     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32169     
32170     Roo.bootstrap.MasonryBrick.register(this);
32171     
32172     this.addEvents({
32173         // raw events
32174         /**
32175          * @event click
32176          * When a MasonryBrick is clcik
32177          * @param {Roo.bootstrap.MasonryBrick} this
32178          * @param {Roo.EventObject} e
32179          */
32180         "click" : true
32181     });
32182 };
32183
32184 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32185     
32186     /**
32187      * @cfg {String} title
32188      */   
32189     title : '',
32190     /**
32191      * @cfg {String} html
32192      */   
32193     html : '',
32194     /**
32195      * @cfg {String} bgimage
32196      */   
32197     bgimage : '',
32198     /**
32199      * @cfg {String} videourl
32200      */   
32201     videourl : '',
32202     /**
32203      * @cfg {String} cls
32204      */   
32205     cls : '',
32206     /**
32207      * @cfg {String} href
32208      */   
32209     href : '',
32210     /**
32211      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32212      */   
32213     size : 'xs',
32214     
32215     /**
32216      * @cfg {String} placetitle (center|bottom)
32217      */   
32218     placetitle : '',
32219     
32220     /**
32221      * @cfg {Boolean} isFitContainer defalut true
32222      */   
32223     isFitContainer : true, 
32224     
32225     /**
32226      * @cfg {Boolean} preventDefault defalut false
32227      */   
32228     preventDefault : false, 
32229     
32230     /**
32231      * @cfg {Boolean} inverse defalut false
32232      */   
32233     maskInverse : false, 
32234     
32235     getAutoCreate : function()
32236     {
32237         if(!this.isFitContainer){
32238             return this.getSplitAutoCreate();
32239         }
32240         
32241         var cls = 'masonry-brick masonry-brick-full';
32242         
32243         if(this.href.length){
32244             cls += ' masonry-brick-link';
32245         }
32246         
32247         if(this.bgimage.length){
32248             cls += ' masonry-brick-image';
32249         }
32250         
32251         if(this.maskInverse){
32252             cls += ' mask-inverse';
32253         }
32254         
32255         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32256             cls += ' enable-mask';
32257         }
32258         
32259         if(this.size){
32260             cls += ' masonry-' + this.size + '-brick';
32261         }
32262         
32263         if(this.placetitle.length){
32264             
32265             switch (this.placetitle) {
32266                 case 'center' :
32267                     cls += ' masonry-center-title';
32268                     break;
32269                 case 'bottom' :
32270                     cls += ' masonry-bottom-title';
32271                     break;
32272                 default:
32273                     break;
32274             }
32275             
32276         } else {
32277             if(!this.html.length && !this.bgimage.length){
32278                 cls += ' masonry-center-title';
32279             }
32280
32281             if(!this.html.length && this.bgimage.length){
32282                 cls += ' masonry-bottom-title';
32283             }
32284         }
32285         
32286         if(this.cls){
32287             cls += ' ' + this.cls;
32288         }
32289         
32290         var cfg = {
32291             tag: (this.href.length) ? 'a' : 'div',
32292             cls: cls,
32293             cn: [
32294                 {
32295                     tag: 'div',
32296                     cls: 'masonry-brick-mask'
32297                 },
32298                 {
32299                     tag: 'div',
32300                     cls: 'masonry-brick-paragraph',
32301                     cn: []
32302                 }
32303             ]
32304         };
32305         
32306         if(this.href.length){
32307             cfg.href = this.href;
32308         }
32309         
32310         var cn = cfg.cn[1].cn;
32311         
32312         if(this.title.length){
32313             cn.push({
32314                 tag: 'h4',
32315                 cls: 'masonry-brick-title',
32316                 html: this.title
32317             });
32318         }
32319         
32320         if(this.html.length){
32321             cn.push({
32322                 tag: 'p',
32323                 cls: 'masonry-brick-text',
32324                 html: this.html
32325             });
32326         }
32327         
32328         if (!this.title.length && !this.html.length) {
32329             cfg.cn[1].cls += ' hide';
32330         }
32331         
32332         if(this.bgimage.length){
32333             cfg.cn.push({
32334                 tag: 'img',
32335                 cls: 'masonry-brick-image-view',
32336                 src: this.bgimage
32337             });
32338         }
32339         
32340         if(this.videourl.length){
32341             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32342             // youtube support only?
32343             cfg.cn.push({
32344                 tag: 'iframe',
32345                 cls: 'masonry-brick-image-view',
32346                 src: vurl,
32347                 frameborder : 0,
32348                 allowfullscreen : true
32349             });
32350         }
32351         
32352         return cfg;
32353         
32354     },
32355     
32356     getSplitAutoCreate : function()
32357     {
32358         var cls = 'masonry-brick masonry-brick-split';
32359         
32360         if(this.href.length){
32361             cls += ' masonry-brick-link';
32362         }
32363         
32364         if(this.bgimage.length){
32365             cls += ' masonry-brick-image';
32366         }
32367         
32368         if(this.size){
32369             cls += ' masonry-' + this.size + '-brick';
32370         }
32371         
32372         switch (this.placetitle) {
32373             case 'center' :
32374                 cls += ' masonry-center-title';
32375                 break;
32376             case 'bottom' :
32377                 cls += ' masonry-bottom-title';
32378                 break;
32379             default:
32380                 if(!this.bgimage.length){
32381                     cls += ' masonry-center-title';
32382                 }
32383
32384                 if(this.bgimage.length){
32385                     cls += ' masonry-bottom-title';
32386                 }
32387                 break;
32388         }
32389         
32390         if(this.cls){
32391             cls += ' ' + this.cls;
32392         }
32393         
32394         var cfg = {
32395             tag: (this.href.length) ? 'a' : 'div',
32396             cls: cls,
32397             cn: [
32398                 {
32399                     tag: 'div',
32400                     cls: 'masonry-brick-split-head',
32401                     cn: [
32402                         {
32403                             tag: 'div',
32404                             cls: 'masonry-brick-paragraph',
32405                             cn: []
32406                         }
32407                     ]
32408                 },
32409                 {
32410                     tag: 'div',
32411                     cls: 'masonry-brick-split-body',
32412                     cn: []
32413                 }
32414             ]
32415         };
32416         
32417         if(this.href.length){
32418             cfg.href = this.href;
32419         }
32420         
32421         if(this.title.length){
32422             cfg.cn[0].cn[0].cn.push({
32423                 tag: 'h4',
32424                 cls: 'masonry-brick-title',
32425                 html: this.title
32426             });
32427         }
32428         
32429         if(this.html.length){
32430             cfg.cn[1].cn.push({
32431                 tag: 'p',
32432                 cls: 'masonry-brick-text',
32433                 html: this.html
32434             });
32435         }
32436
32437         if(this.bgimage.length){
32438             cfg.cn[0].cn.push({
32439                 tag: 'img',
32440                 cls: 'masonry-brick-image-view',
32441                 src: this.bgimage
32442             });
32443         }
32444         
32445         if(this.videourl.length){
32446             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32447             // youtube support only?
32448             cfg.cn[0].cn.cn.push({
32449                 tag: 'iframe',
32450                 cls: 'masonry-brick-image-view',
32451                 src: vurl,
32452                 frameborder : 0,
32453                 allowfullscreen : true
32454             });
32455         }
32456         
32457         return cfg;
32458     },
32459     
32460     initEvents: function() 
32461     {
32462         switch (this.size) {
32463             case 'xs' :
32464                 this.x = 1;
32465                 this.y = 1;
32466                 break;
32467             case 'sm' :
32468                 this.x = 2;
32469                 this.y = 2;
32470                 break;
32471             case 'md' :
32472             case 'md-left' :
32473             case 'md-right' :
32474                 this.x = 3;
32475                 this.y = 3;
32476                 break;
32477             case 'tall' :
32478                 this.x = 2;
32479                 this.y = 3;
32480                 break;
32481             case 'wide' :
32482                 this.x = 3;
32483                 this.y = 2;
32484                 break;
32485             case 'wide-thin' :
32486                 this.x = 3;
32487                 this.y = 1;
32488                 break;
32489                         
32490             default :
32491                 break;
32492         }
32493         
32494         if(Roo.isTouch){
32495             this.el.on('touchstart', this.onTouchStart, this);
32496             this.el.on('touchmove', this.onTouchMove, this);
32497             this.el.on('touchend', this.onTouchEnd, this);
32498             this.el.on('contextmenu', this.onContextMenu, this);
32499         } else {
32500             this.el.on('mouseenter'  ,this.enter, this);
32501             this.el.on('mouseleave', this.leave, this);
32502             this.el.on('click', this.onClick, this);
32503         }
32504         
32505         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32506             this.parent().bricks.push(this);   
32507         }
32508         
32509     },
32510     
32511     onClick: function(e, el)
32512     {
32513         var time = this.endTimer - this.startTimer;
32514         // Roo.log(e.preventDefault());
32515         if(Roo.isTouch){
32516             if(time > 1000){
32517                 e.preventDefault();
32518                 return;
32519             }
32520         }
32521         
32522         if(!this.preventDefault){
32523             return;
32524         }
32525         
32526         e.preventDefault();
32527         
32528         if (this.activcClass != '') {
32529             this.selectBrick();
32530         }
32531         
32532         this.fireEvent('click', this);
32533     },
32534     
32535     enter: function(e, el)
32536     {
32537         e.preventDefault();
32538         
32539         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32540             return;
32541         }
32542         
32543         if(this.bgimage.length && this.html.length){
32544             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32545         }
32546     },
32547     
32548     leave: function(e, el)
32549     {
32550         e.preventDefault();
32551         
32552         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32553             return;
32554         }
32555         
32556         if(this.bgimage.length && this.html.length){
32557             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32558         }
32559     },
32560     
32561     onTouchStart: function(e, el)
32562     {
32563 //        e.preventDefault();
32564         
32565         this.touchmoved = false;
32566         
32567         if(!this.isFitContainer){
32568             return;
32569         }
32570         
32571         if(!this.bgimage.length || !this.html.length){
32572             return;
32573         }
32574         
32575         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32576         
32577         this.timer = new Date().getTime();
32578         
32579     },
32580     
32581     onTouchMove: function(e, el)
32582     {
32583         this.touchmoved = true;
32584     },
32585     
32586     onContextMenu : function(e,el)
32587     {
32588         e.preventDefault();
32589         e.stopPropagation();
32590         return false;
32591     },
32592     
32593     onTouchEnd: function(e, el)
32594     {
32595 //        e.preventDefault();
32596         
32597         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32598         
32599             this.leave(e,el);
32600             
32601             return;
32602         }
32603         
32604         if(!this.bgimage.length || !this.html.length){
32605             
32606             if(this.href.length){
32607                 window.location.href = this.href;
32608             }
32609             
32610             return;
32611         }
32612         
32613         if(!this.isFitContainer){
32614             return;
32615         }
32616         
32617         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32618         
32619         window.location.href = this.href;
32620     },
32621     
32622     //selection on single brick only
32623     selectBrick : function() {
32624         
32625         if (!this.parentId) {
32626             return;
32627         }
32628         
32629         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32630         var index = m.selectedBrick.indexOf(this.id);
32631         
32632         if ( index > -1) {
32633             m.selectedBrick.splice(index,1);
32634             this.el.removeClass(this.activeClass);
32635             return;
32636         }
32637         
32638         for(var i = 0; i < m.selectedBrick.length; i++) {
32639             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32640             b.el.removeClass(b.activeClass);
32641         }
32642         
32643         m.selectedBrick = [];
32644         
32645         m.selectedBrick.push(this.id);
32646         this.el.addClass(this.activeClass);
32647         return;
32648     }
32649     
32650 });
32651
32652 Roo.apply(Roo.bootstrap.MasonryBrick, {
32653     
32654     //groups: {},
32655     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32656      /**
32657     * register a Masonry Brick
32658     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32659     */
32660     
32661     register : function(brick)
32662     {
32663         //this.groups[brick.id] = brick;
32664         this.groups.add(brick.id, brick);
32665     },
32666     /**
32667     * fetch a  masonry brick based on the masonry brick ID
32668     * @param {string} the masonry brick to add
32669     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32670     */
32671     
32672     get: function(brick_id) 
32673     {
32674         // if (typeof(this.groups[brick_id]) == 'undefined') {
32675         //     return false;
32676         // }
32677         // return this.groups[brick_id] ;
32678         
32679         if(this.groups.key(brick_id)) {
32680             return this.groups.key(brick_id);
32681         }
32682         
32683         return false;
32684     }
32685     
32686     
32687     
32688 });
32689
32690  /*
32691  * - LGPL
32692  *
32693  * element
32694  * 
32695  */
32696
32697 /**
32698  * @class Roo.bootstrap.Brick
32699  * @extends Roo.bootstrap.Component
32700  * Bootstrap Brick class
32701  * 
32702  * @constructor
32703  * Create a new Brick
32704  * @param {Object} config The config object
32705  */
32706
32707 Roo.bootstrap.Brick = function(config){
32708     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32709     
32710     this.addEvents({
32711         // raw events
32712         /**
32713          * @event click
32714          * When a Brick is click
32715          * @param {Roo.bootstrap.Brick} this
32716          * @param {Roo.EventObject} e
32717          */
32718         "click" : true
32719     });
32720 };
32721
32722 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32723     
32724     /**
32725      * @cfg {String} title
32726      */   
32727     title : '',
32728     /**
32729      * @cfg {String} html
32730      */   
32731     html : '',
32732     /**
32733      * @cfg {String} bgimage
32734      */   
32735     bgimage : '',
32736     /**
32737      * @cfg {String} cls
32738      */   
32739     cls : '',
32740     /**
32741      * @cfg {String} href
32742      */   
32743     href : '',
32744     /**
32745      * @cfg {String} video
32746      */   
32747     video : '',
32748     /**
32749      * @cfg {Boolean} square
32750      */   
32751     square : true,
32752     
32753     getAutoCreate : function()
32754     {
32755         var cls = 'roo-brick';
32756         
32757         if(this.href.length){
32758             cls += ' roo-brick-link';
32759         }
32760         
32761         if(this.bgimage.length){
32762             cls += ' roo-brick-image';
32763         }
32764         
32765         if(!this.html.length && !this.bgimage.length){
32766             cls += ' roo-brick-center-title';
32767         }
32768         
32769         if(!this.html.length && this.bgimage.length){
32770             cls += ' roo-brick-bottom-title';
32771         }
32772         
32773         if(this.cls){
32774             cls += ' ' + this.cls;
32775         }
32776         
32777         var cfg = {
32778             tag: (this.href.length) ? 'a' : 'div',
32779             cls: cls,
32780             cn: [
32781                 {
32782                     tag: 'div',
32783                     cls: 'roo-brick-paragraph',
32784                     cn: []
32785                 }
32786             ]
32787         };
32788         
32789         if(this.href.length){
32790             cfg.href = this.href;
32791         }
32792         
32793         var cn = cfg.cn[0].cn;
32794         
32795         if(this.title.length){
32796             cn.push({
32797                 tag: 'h4',
32798                 cls: 'roo-brick-title',
32799                 html: this.title
32800             });
32801         }
32802         
32803         if(this.html.length){
32804             cn.push({
32805                 tag: 'p',
32806                 cls: 'roo-brick-text',
32807                 html: this.html
32808             });
32809         } else {
32810             cn.cls += ' hide';
32811         }
32812         
32813         if(this.bgimage.length){
32814             cfg.cn.push({
32815                 tag: 'img',
32816                 cls: 'roo-brick-image-view',
32817                 src: this.bgimage
32818             });
32819         }
32820         
32821         return cfg;
32822     },
32823     
32824     initEvents: function() 
32825     {
32826         if(this.title.length || this.html.length){
32827             this.el.on('mouseenter'  ,this.enter, this);
32828             this.el.on('mouseleave', this.leave, this);
32829         }
32830         
32831         Roo.EventManager.onWindowResize(this.resize, this); 
32832         
32833         if(this.bgimage.length){
32834             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32835             this.imageEl.on('load', this.onImageLoad, this);
32836             return;
32837         }
32838         
32839         this.resize();
32840     },
32841     
32842     onImageLoad : function()
32843     {
32844         this.resize();
32845     },
32846     
32847     resize : function()
32848     {
32849         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32850         
32851         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32852         
32853         if(this.bgimage.length){
32854             var image = this.el.select('.roo-brick-image-view', true).first();
32855             
32856             image.setWidth(paragraph.getWidth());
32857             
32858             if(this.square){
32859                 image.setHeight(paragraph.getWidth());
32860             }
32861             
32862             this.el.setHeight(image.getHeight());
32863             paragraph.setHeight(image.getHeight());
32864             
32865         }
32866         
32867     },
32868     
32869     enter: function(e, el)
32870     {
32871         e.preventDefault();
32872         
32873         if(this.bgimage.length){
32874             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32875             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32876         }
32877     },
32878     
32879     leave: function(e, el)
32880     {
32881         e.preventDefault();
32882         
32883         if(this.bgimage.length){
32884             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32885             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32886         }
32887     }
32888     
32889 });
32890
32891  
32892
32893  /*
32894  * - LGPL
32895  *
32896  * Input
32897  * 
32898  */
32899
32900 /**
32901  * @class Roo.bootstrap.NumberField
32902  * @extends Roo.bootstrap.Input
32903  * Bootstrap NumberField class
32904  * 
32905  * 
32906  * 
32907  * 
32908  * @constructor
32909  * Create a new NumberField
32910  * @param {Object} config The config object
32911  */
32912
32913 Roo.bootstrap.NumberField = function(config){
32914     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32915 };
32916
32917 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32918     
32919     /**
32920      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32921      */
32922     allowDecimals : true,
32923     /**
32924      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32925      */
32926     decimalSeparator : ".",
32927     /**
32928      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32929      */
32930     decimalPrecision : 2,
32931     /**
32932      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32933      */
32934     allowNegative : true,
32935     /**
32936      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32937      */
32938     minValue : Number.NEGATIVE_INFINITY,
32939     /**
32940      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32941      */
32942     maxValue : Number.MAX_VALUE,
32943     /**
32944      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32945      */
32946     minText : "The minimum value for this field is {0}",
32947     /**
32948      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32949      */
32950     maxText : "The maximum value for this field is {0}",
32951     /**
32952      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32953      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32954      */
32955     nanText : "{0} is not a valid number",
32956     /**
32957      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32958      */
32959     castInt : true,
32960
32961     // private
32962     initEvents : function()
32963     {   
32964         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32965         
32966         var allowed = "0123456789";
32967         
32968         if(this.allowDecimals){
32969             allowed += this.decimalSeparator;
32970         }
32971         
32972         if(this.allowNegative){
32973             allowed += "-";
32974         }
32975         
32976         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32977         
32978         var keyPress = function(e){
32979             
32980             var k = e.getKey();
32981             
32982             var c = e.getCharCode();
32983             
32984             if(
32985                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32986                     allowed.indexOf(String.fromCharCode(c)) === -1
32987             ){
32988                 e.stopEvent();
32989                 return;
32990             }
32991             
32992             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32993                 return;
32994             }
32995             
32996             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32997                 e.stopEvent();
32998             }
32999         };
33000         
33001         this.el.on("keypress", keyPress, this);
33002     },
33003     
33004     validateValue : function(value)
33005     {
33006         
33007         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33008             return false;
33009         }
33010         
33011         var num = this.parseValue(value);
33012         
33013         if(isNaN(num)){
33014             this.markInvalid(String.format(this.nanText, value));
33015             return false;
33016         }
33017         
33018         if(num < this.minValue){
33019             this.markInvalid(String.format(this.minText, this.minValue));
33020             return false;
33021         }
33022         
33023         if(num > this.maxValue){
33024             this.markInvalid(String.format(this.maxText, this.maxValue));
33025             return false;
33026         }
33027         
33028         return true;
33029     },
33030
33031     getValue : function()
33032     {
33033         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33034     },
33035
33036     parseValue : function(value)
33037     {
33038         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33039         return isNaN(value) ? '' : value;
33040     },
33041
33042     fixPrecision : function(value)
33043     {
33044         var nan = isNaN(value);
33045         
33046         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33047             return nan ? '' : value;
33048         }
33049         return parseFloat(value).toFixed(this.decimalPrecision);
33050     },
33051
33052     setValue : function(v)
33053     {
33054         v = this.fixPrecision(v);
33055         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33056     },
33057
33058     decimalPrecisionFcn : function(v)
33059     {
33060         return Math.floor(v);
33061     },
33062
33063     beforeBlur : function()
33064     {
33065         if(!this.castInt){
33066             return;
33067         }
33068         
33069         var v = this.parseValue(this.getRawValue());
33070         if(v){
33071             this.setValue(v);
33072         }
33073     }
33074     
33075 });
33076
33077  
33078
33079 /*
33080 * Licence: LGPL
33081 */
33082
33083 /**
33084  * @class Roo.bootstrap.DocumentSlider
33085  * @extends Roo.bootstrap.Component
33086  * Bootstrap DocumentSlider class
33087  * 
33088  * @constructor
33089  * Create a new DocumentViewer
33090  * @param {Object} config The config object
33091  */
33092
33093 Roo.bootstrap.DocumentSlider = function(config){
33094     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33095     
33096     this.files = [];
33097     
33098     this.addEvents({
33099         /**
33100          * @event initial
33101          * Fire after initEvent
33102          * @param {Roo.bootstrap.DocumentSlider} this
33103          */
33104         "initial" : true,
33105         /**
33106          * @event update
33107          * Fire after update
33108          * @param {Roo.bootstrap.DocumentSlider} this
33109          */
33110         "update" : true,
33111         /**
33112          * @event click
33113          * Fire after click
33114          * @param {Roo.bootstrap.DocumentSlider} this
33115          */
33116         "click" : true
33117     });
33118 };
33119
33120 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33121     
33122     files : false,
33123     
33124     indicator : 0,
33125     
33126     getAutoCreate : function()
33127     {
33128         var cfg = {
33129             tag : 'div',
33130             cls : 'roo-document-slider',
33131             cn : [
33132                 {
33133                     tag : 'div',
33134                     cls : 'roo-document-slider-header',
33135                     cn : [
33136                         {
33137                             tag : 'div',
33138                             cls : 'roo-document-slider-header-title'
33139                         }
33140                     ]
33141                 },
33142                 {
33143                     tag : 'div',
33144                     cls : 'roo-document-slider-body',
33145                     cn : [
33146                         {
33147                             tag : 'div',
33148                             cls : 'roo-document-slider-prev',
33149                             cn : [
33150                                 {
33151                                     tag : 'i',
33152                                     cls : 'fa fa-chevron-left'
33153                                 }
33154                             ]
33155                         },
33156                         {
33157                             tag : 'div',
33158                             cls : 'roo-document-slider-thumb',
33159                             cn : [
33160                                 {
33161                                     tag : 'img',
33162                                     cls : 'roo-document-slider-image'
33163                                 }
33164                             ]
33165                         },
33166                         {
33167                             tag : 'div',
33168                             cls : 'roo-document-slider-next',
33169                             cn : [
33170                                 {
33171                                     tag : 'i',
33172                                     cls : 'fa fa-chevron-right'
33173                                 }
33174                             ]
33175                         }
33176                     ]
33177                 }
33178             ]
33179         };
33180         
33181         return cfg;
33182     },
33183     
33184     initEvents : function()
33185     {
33186         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33187         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33188         
33189         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33190         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33191         
33192         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33193         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33194         
33195         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33196         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33197         
33198         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33199         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33200         
33201         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33202         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33203         
33204         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33205         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33206         
33207         this.thumbEl.on('click', this.onClick, this);
33208         
33209         this.prevIndicator.on('click', this.prev, this);
33210         
33211         this.nextIndicator.on('click', this.next, this);
33212         
33213     },
33214     
33215     initial : function()
33216     {
33217         if(this.files.length){
33218             this.indicator = 1;
33219             this.update()
33220         }
33221         
33222         this.fireEvent('initial', this);
33223     },
33224     
33225     update : function()
33226     {
33227         this.imageEl.attr('src', this.files[this.indicator - 1]);
33228         
33229         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33230         
33231         this.prevIndicator.show();
33232         
33233         if(this.indicator == 1){
33234             this.prevIndicator.hide();
33235         }
33236         
33237         this.nextIndicator.show();
33238         
33239         if(this.indicator == this.files.length){
33240             this.nextIndicator.hide();
33241         }
33242         
33243         this.thumbEl.scrollTo('top');
33244         
33245         this.fireEvent('update', this);
33246     },
33247     
33248     onClick : function(e)
33249     {
33250         e.preventDefault();
33251         
33252         this.fireEvent('click', this);
33253     },
33254     
33255     prev : function(e)
33256     {
33257         e.preventDefault();
33258         
33259         this.indicator = Math.max(1, this.indicator - 1);
33260         
33261         this.update();
33262     },
33263     
33264     next : function(e)
33265     {
33266         e.preventDefault();
33267         
33268         this.indicator = Math.min(this.files.length, this.indicator + 1);
33269         
33270         this.update();
33271     }
33272 });
33273 /*
33274  * - LGPL
33275  *
33276  * RadioSet
33277  *
33278  *
33279  */
33280
33281 /**
33282  * @class Roo.bootstrap.RadioSet
33283  * @extends Roo.bootstrap.Input
33284  * Bootstrap RadioSet class
33285  * @cfg {String} indicatorpos (left|right) default left
33286  * @cfg {Boolean} inline (true|false) inline the element (default true)
33287  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33288  * @constructor
33289  * Create a new RadioSet
33290  * @param {Object} config The config object
33291  */
33292
33293 Roo.bootstrap.RadioSet = function(config){
33294     
33295     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33296     
33297     this.radioes = [];
33298     
33299     Roo.bootstrap.RadioSet.register(this);
33300     
33301     this.addEvents({
33302         /**
33303         * @event check
33304         * Fires when the element is checked or unchecked.
33305         * @param {Roo.bootstrap.RadioSet} this This radio
33306         * @param {Roo.bootstrap.Radio} item The checked item
33307         */
33308        check : true
33309     });
33310     
33311 };
33312
33313 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33314
33315     radioes : false,
33316     
33317     inline : true,
33318     
33319     weight : '',
33320     
33321     indicatorpos : 'left',
33322     
33323     getAutoCreate : function()
33324     {
33325         var label = {
33326             tag : 'label',
33327             cls : 'roo-radio-set-label',
33328             cn : [
33329                 {
33330                     tag : 'span',
33331                     html : this.fieldLabel
33332                 }
33333             ]
33334         };
33335         
33336         if(this.indicatorpos == 'left'){
33337             label.cn.unshift({
33338                 tag : 'i',
33339                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33340                 tooltip : 'This field is required'
33341             });
33342         } else {
33343             label.cn.push({
33344                 tag : 'i',
33345                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33346                 tooltip : 'This field is required'
33347             });
33348         }
33349         
33350         var items = {
33351             tag : 'div',
33352             cls : 'roo-radio-set-items'
33353         };
33354         
33355         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33356         
33357         if (align === 'left' && this.fieldLabel.length) {
33358             
33359             items = {
33360                 cls : "roo-radio-set-right", 
33361                 cn: [
33362                     items
33363                 ]
33364             };
33365             
33366             if(this.labelWidth > 12){
33367                 label.style = "width: " + this.labelWidth + 'px';
33368             }
33369             
33370             if(this.labelWidth < 13 && this.labelmd == 0){
33371                 this.labelmd = this.labelWidth;
33372             }
33373             
33374             if(this.labellg > 0){
33375                 label.cls += ' col-lg-' + this.labellg;
33376                 items.cls += ' col-lg-' + (12 - this.labellg);
33377             }
33378             
33379             if(this.labelmd > 0){
33380                 label.cls += ' col-md-' + this.labelmd;
33381                 items.cls += ' col-md-' + (12 - this.labelmd);
33382             }
33383             
33384             if(this.labelsm > 0){
33385                 label.cls += ' col-sm-' + this.labelsm;
33386                 items.cls += ' col-sm-' + (12 - this.labelsm);
33387             }
33388             
33389             if(this.labelxs > 0){
33390                 label.cls += ' col-xs-' + this.labelxs;
33391                 items.cls += ' col-xs-' + (12 - this.labelxs);
33392             }
33393         }
33394         
33395         var cfg = {
33396             tag : 'div',
33397             cls : 'roo-radio-set',
33398             cn : [
33399                 {
33400                     tag : 'input',
33401                     cls : 'roo-radio-set-input',
33402                     type : 'hidden',
33403                     name : this.name,
33404                     value : this.value ? this.value :  ''
33405                 },
33406                 label,
33407                 items
33408             ]
33409         };
33410         
33411         if(this.weight.length){
33412             cfg.cls += ' roo-radio-' + this.weight;
33413         }
33414         
33415         if(this.inline) {
33416             cfg.cls += ' roo-radio-set-inline';
33417         }
33418         
33419         var settings=this;
33420         ['xs','sm','md','lg'].map(function(size){
33421             if (settings[size]) {
33422                 cfg.cls += ' col-' + size + '-' + settings[size];
33423             }
33424         });
33425         
33426         return cfg;
33427         
33428     },
33429
33430     initEvents : function()
33431     {
33432         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33433         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33434         
33435         if(!this.fieldLabel.length){
33436             this.labelEl.hide();
33437         }
33438         
33439         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33440         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33441         
33442         this.indicatorEl().addClass('invisible');
33443         
33444         this.originalValue = this.getValue();
33445         
33446     },
33447     
33448     inputEl: function ()
33449     {
33450         return this.el.select('.roo-radio-set-input', true).first();
33451     },
33452     
33453     getChildContainer : function()
33454     {
33455         return this.itemsEl;
33456     },
33457     
33458     register : function(item)
33459     {
33460         this.radioes.push(item);
33461         
33462     },
33463     
33464     validate : function()
33465     {   
33466         var valid = false;
33467         
33468         Roo.each(this.radioes, function(i){
33469             if(!i.checked){
33470                 return;
33471             }
33472             
33473             valid = true;
33474             return false;
33475         });
33476         
33477         if(this.allowBlank) {
33478             return true;
33479         }
33480         
33481         if(this.disabled || valid){
33482             this.markValid();
33483             return true;
33484         }
33485         
33486         this.markInvalid();
33487         return false;
33488         
33489     },
33490     
33491     markValid : function()
33492     {
33493         if(this.labelEl.isVisible(true)){
33494             this.indicatorEl().removeClass('visible');
33495             this.indicatorEl().addClass('invisible');
33496         }
33497         
33498         this.el.removeClass([this.invalidClass, this.validClass]);
33499         this.el.addClass(this.validClass);
33500         
33501         this.fireEvent('valid', this);
33502     },
33503     
33504     markInvalid : function(msg)
33505     {
33506         if(this.allowBlank || this.disabled){
33507             return;
33508         }
33509         
33510         if(this.labelEl.isVisible(true)){
33511             this.indicatorEl().removeClass('invisible');
33512             this.indicatorEl().addClass('visible');
33513         }
33514         
33515         this.el.removeClass([this.invalidClass, this.validClass]);
33516         this.el.addClass(this.invalidClass);
33517         
33518         this.fireEvent('invalid', this, msg);
33519         
33520     },
33521     
33522     setValue : function(v, suppressEvent)
33523     {   
33524         if(this.value === v){
33525             return;
33526         }
33527         
33528         this.value = v;
33529         
33530         if(this.rendered){
33531             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33532         }
33533         
33534         Roo.each(this.radioes, function(i){
33535             
33536             i.checked = false;
33537             i.el.removeClass('checked');
33538             
33539             if(i.value === v || i.value.toString() === v.toString()){
33540                 i.checked = true;
33541                 i.el.addClass('checked');
33542                 
33543                 if(suppressEvent !== true){
33544                     this.fireEvent('check', this, i);
33545                 }
33546             }
33547             
33548         }, this);
33549         
33550         this.validate();
33551     },
33552     
33553     clearInvalid : function(){
33554         
33555         if(!this.el || this.preventMark){
33556             return;
33557         }
33558         
33559         this.el.removeClass([this.invalidClass]);
33560         
33561         this.fireEvent('valid', this);
33562     }
33563     
33564 });
33565
33566 Roo.apply(Roo.bootstrap.RadioSet, {
33567     
33568     groups: {},
33569     
33570     register : function(set)
33571     {
33572         this.groups[set.name] = set;
33573     },
33574     
33575     get: function(name) 
33576     {
33577         if (typeof(this.groups[name]) == 'undefined') {
33578             return false;
33579         }
33580         
33581         return this.groups[name] ;
33582     }
33583     
33584 });
33585 /*
33586  * Based on:
33587  * Ext JS Library 1.1.1
33588  * Copyright(c) 2006-2007, Ext JS, LLC.
33589  *
33590  * Originally Released Under LGPL - original licence link has changed is not relivant.
33591  *
33592  * Fork - LGPL
33593  * <script type="text/javascript">
33594  */
33595
33596
33597 /**
33598  * @class Roo.bootstrap.SplitBar
33599  * @extends Roo.util.Observable
33600  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33601  * <br><br>
33602  * Usage:
33603  * <pre><code>
33604 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33605                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33606 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33607 split.minSize = 100;
33608 split.maxSize = 600;
33609 split.animate = true;
33610 split.on('moved', splitterMoved);
33611 </code></pre>
33612  * @constructor
33613  * Create a new SplitBar
33614  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33615  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33616  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33617  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33618                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33619                         position of the SplitBar).
33620  */
33621 Roo.bootstrap.SplitBar = function(cfg){
33622     
33623     /** @private */
33624     
33625     //{
33626     //  dragElement : elm
33627     //  resizingElement: el,
33628         // optional..
33629     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33630     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33631         // existingProxy ???
33632     //}
33633     
33634     this.el = Roo.get(cfg.dragElement, true);
33635     this.el.dom.unselectable = "on";
33636     /** @private */
33637     this.resizingEl = Roo.get(cfg.resizingElement, true);
33638
33639     /**
33640      * @private
33641      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33642      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33643      * @type Number
33644      */
33645     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33646     
33647     /**
33648      * The minimum size of the resizing element. (Defaults to 0)
33649      * @type Number
33650      */
33651     this.minSize = 0;
33652     
33653     /**
33654      * The maximum size of the resizing element. (Defaults to 2000)
33655      * @type Number
33656      */
33657     this.maxSize = 2000;
33658     
33659     /**
33660      * Whether to animate the transition to the new size
33661      * @type Boolean
33662      */
33663     this.animate = false;
33664     
33665     /**
33666      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33667      * @type Boolean
33668      */
33669     this.useShim = false;
33670     
33671     /** @private */
33672     this.shim = null;
33673     
33674     if(!cfg.existingProxy){
33675         /** @private */
33676         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33677     }else{
33678         this.proxy = Roo.get(cfg.existingProxy).dom;
33679     }
33680     /** @private */
33681     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33682     
33683     /** @private */
33684     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33685     
33686     /** @private */
33687     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33688     
33689     /** @private */
33690     this.dragSpecs = {};
33691     
33692     /**
33693      * @private The adapter to use to positon and resize elements
33694      */
33695     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33696     this.adapter.init(this);
33697     
33698     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33699         /** @private */
33700         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33701         this.el.addClass("roo-splitbar-h");
33702     }else{
33703         /** @private */
33704         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33705         this.el.addClass("roo-splitbar-v");
33706     }
33707     
33708     this.addEvents({
33709         /**
33710          * @event resize
33711          * Fires when the splitter is moved (alias for {@link #event-moved})
33712          * @param {Roo.bootstrap.SplitBar} this
33713          * @param {Number} newSize the new width or height
33714          */
33715         "resize" : true,
33716         /**
33717          * @event moved
33718          * Fires when the splitter is moved
33719          * @param {Roo.bootstrap.SplitBar} this
33720          * @param {Number} newSize the new width or height
33721          */
33722         "moved" : true,
33723         /**
33724          * @event beforeresize
33725          * Fires before the splitter is dragged
33726          * @param {Roo.bootstrap.SplitBar} this
33727          */
33728         "beforeresize" : true,
33729
33730         "beforeapply" : true
33731     });
33732
33733     Roo.util.Observable.call(this);
33734 };
33735
33736 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33737     onStartProxyDrag : function(x, y){
33738         this.fireEvent("beforeresize", this);
33739         if(!this.overlay){
33740             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33741             o.unselectable();
33742             o.enableDisplayMode("block");
33743             // all splitbars share the same overlay
33744             Roo.bootstrap.SplitBar.prototype.overlay = o;
33745         }
33746         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33747         this.overlay.show();
33748         Roo.get(this.proxy).setDisplayed("block");
33749         var size = this.adapter.getElementSize(this);
33750         this.activeMinSize = this.getMinimumSize();;
33751         this.activeMaxSize = this.getMaximumSize();;
33752         var c1 = size - this.activeMinSize;
33753         var c2 = Math.max(this.activeMaxSize - size, 0);
33754         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33755             this.dd.resetConstraints();
33756             this.dd.setXConstraint(
33757                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33758                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33759             );
33760             this.dd.setYConstraint(0, 0);
33761         }else{
33762             this.dd.resetConstraints();
33763             this.dd.setXConstraint(0, 0);
33764             this.dd.setYConstraint(
33765                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33766                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33767             );
33768          }
33769         this.dragSpecs.startSize = size;
33770         this.dragSpecs.startPoint = [x, y];
33771         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33772     },
33773     
33774     /** 
33775      * @private Called after the drag operation by the DDProxy
33776      */
33777     onEndProxyDrag : function(e){
33778         Roo.get(this.proxy).setDisplayed(false);
33779         var endPoint = Roo.lib.Event.getXY(e);
33780         if(this.overlay){
33781             this.overlay.hide();
33782         }
33783         var newSize;
33784         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33785             newSize = this.dragSpecs.startSize + 
33786                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33787                     endPoint[0] - this.dragSpecs.startPoint[0] :
33788                     this.dragSpecs.startPoint[0] - endPoint[0]
33789                 );
33790         }else{
33791             newSize = this.dragSpecs.startSize + 
33792                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33793                     endPoint[1] - this.dragSpecs.startPoint[1] :
33794                     this.dragSpecs.startPoint[1] - endPoint[1]
33795                 );
33796         }
33797         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33798         if(newSize != this.dragSpecs.startSize){
33799             if(this.fireEvent('beforeapply', this, newSize) !== false){
33800                 this.adapter.setElementSize(this, newSize);
33801                 this.fireEvent("moved", this, newSize);
33802                 this.fireEvent("resize", this, newSize);
33803             }
33804         }
33805     },
33806     
33807     /**
33808      * Get the adapter this SplitBar uses
33809      * @return The adapter object
33810      */
33811     getAdapter : function(){
33812         return this.adapter;
33813     },
33814     
33815     /**
33816      * Set the adapter this SplitBar uses
33817      * @param {Object} adapter A SplitBar adapter object
33818      */
33819     setAdapter : function(adapter){
33820         this.adapter = adapter;
33821         this.adapter.init(this);
33822     },
33823     
33824     /**
33825      * Gets the minimum size for the resizing element
33826      * @return {Number} The minimum size
33827      */
33828     getMinimumSize : function(){
33829         return this.minSize;
33830     },
33831     
33832     /**
33833      * Sets the minimum size for the resizing element
33834      * @param {Number} minSize The minimum size
33835      */
33836     setMinimumSize : function(minSize){
33837         this.minSize = minSize;
33838     },
33839     
33840     /**
33841      * Gets the maximum size for the resizing element
33842      * @return {Number} The maximum size
33843      */
33844     getMaximumSize : function(){
33845         return this.maxSize;
33846     },
33847     
33848     /**
33849      * Sets the maximum size for the resizing element
33850      * @param {Number} maxSize The maximum size
33851      */
33852     setMaximumSize : function(maxSize){
33853         this.maxSize = maxSize;
33854     },
33855     
33856     /**
33857      * Sets the initialize size for the resizing element
33858      * @param {Number} size The initial size
33859      */
33860     setCurrentSize : function(size){
33861         var oldAnimate = this.animate;
33862         this.animate = false;
33863         this.adapter.setElementSize(this, size);
33864         this.animate = oldAnimate;
33865     },
33866     
33867     /**
33868      * Destroy this splitbar. 
33869      * @param {Boolean} removeEl True to remove the element
33870      */
33871     destroy : function(removeEl){
33872         if(this.shim){
33873             this.shim.remove();
33874         }
33875         this.dd.unreg();
33876         this.proxy.parentNode.removeChild(this.proxy);
33877         if(removeEl){
33878             this.el.remove();
33879         }
33880     }
33881 });
33882
33883 /**
33884  * @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.
33885  */
33886 Roo.bootstrap.SplitBar.createProxy = function(dir){
33887     var proxy = new Roo.Element(document.createElement("div"));
33888     proxy.unselectable();
33889     var cls = 'roo-splitbar-proxy';
33890     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33891     document.body.appendChild(proxy.dom);
33892     return proxy.dom;
33893 };
33894
33895 /** 
33896  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33897  * Default Adapter. It assumes the splitter and resizing element are not positioned
33898  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33899  */
33900 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33901 };
33902
33903 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33904     // do nothing for now
33905     init : function(s){
33906     
33907     },
33908     /**
33909      * Called before drag operations to get the current size of the resizing element. 
33910      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33911      */
33912      getElementSize : function(s){
33913         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33914             return s.resizingEl.getWidth();
33915         }else{
33916             return s.resizingEl.getHeight();
33917         }
33918     },
33919     
33920     /**
33921      * Called after drag operations to set the size of the resizing element.
33922      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33923      * @param {Number} newSize The new size to set
33924      * @param {Function} onComplete A function to be invoked when resizing is complete
33925      */
33926     setElementSize : function(s, newSize, onComplete){
33927         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33928             if(!s.animate){
33929                 s.resizingEl.setWidth(newSize);
33930                 if(onComplete){
33931                     onComplete(s, newSize);
33932                 }
33933             }else{
33934                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33935             }
33936         }else{
33937             
33938             if(!s.animate){
33939                 s.resizingEl.setHeight(newSize);
33940                 if(onComplete){
33941                     onComplete(s, newSize);
33942                 }
33943             }else{
33944                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33945             }
33946         }
33947     }
33948 };
33949
33950 /** 
33951  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33952  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33953  * Adapter that  moves the splitter element to align with the resized sizing element. 
33954  * Used with an absolute positioned SplitBar.
33955  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33956  * document.body, make sure you assign an id to the body element.
33957  */
33958 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33959     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33960     this.container = Roo.get(container);
33961 };
33962
33963 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33964     init : function(s){
33965         this.basic.init(s);
33966     },
33967     
33968     getElementSize : function(s){
33969         return this.basic.getElementSize(s);
33970     },
33971     
33972     setElementSize : function(s, newSize, onComplete){
33973         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33974     },
33975     
33976     moveSplitter : function(s){
33977         var yes = Roo.bootstrap.SplitBar;
33978         switch(s.placement){
33979             case yes.LEFT:
33980                 s.el.setX(s.resizingEl.getRight());
33981                 break;
33982             case yes.RIGHT:
33983                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33984                 break;
33985             case yes.TOP:
33986                 s.el.setY(s.resizingEl.getBottom());
33987                 break;
33988             case yes.BOTTOM:
33989                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33990                 break;
33991         }
33992     }
33993 };
33994
33995 /**
33996  * Orientation constant - Create a vertical SplitBar
33997  * @static
33998  * @type Number
33999  */
34000 Roo.bootstrap.SplitBar.VERTICAL = 1;
34001
34002 /**
34003  * Orientation constant - Create a horizontal SplitBar
34004  * @static
34005  * @type Number
34006  */
34007 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34008
34009 /**
34010  * Placement constant - The resizing element is to the left of the splitter element
34011  * @static
34012  * @type Number
34013  */
34014 Roo.bootstrap.SplitBar.LEFT = 1;
34015
34016 /**
34017  * Placement constant - The resizing element is to the right of the splitter element
34018  * @static
34019  * @type Number
34020  */
34021 Roo.bootstrap.SplitBar.RIGHT = 2;
34022
34023 /**
34024  * Placement constant - The resizing element is positioned above the splitter element
34025  * @static
34026  * @type Number
34027  */
34028 Roo.bootstrap.SplitBar.TOP = 3;
34029
34030 /**
34031  * Placement constant - The resizing element is positioned under splitter element
34032  * @static
34033  * @type Number
34034  */
34035 Roo.bootstrap.SplitBar.BOTTOM = 4;
34036 Roo.namespace("Roo.bootstrap.layout");/*
34037  * Based on:
34038  * Ext JS Library 1.1.1
34039  * Copyright(c) 2006-2007, Ext JS, LLC.
34040  *
34041  * Originally Released Under LGPL - original licence link has changed is not relivant.
34042  *
34043  * Fork - LGPL
34044  * <script type="text/javascript">
34045  */
34046
34047 /**
34048  * @class Roo.bootstrap.layout.Manager
34049  * @extends Roo.bootstrap.Component
34050  * Base class for layout managers.
34051  */
34052 Roo.bootstrap.layout.Manager = function(config)
34053 {
34054     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34055
34056
34057
34058
34059
34060     /** false to disable window resize monitoring @type Boolean */
34061     this.monitorWindowResize = true;
34062     this.regions = {};
34063     this.addEvents({
34064         /**
34065          * @event layout
34066          * Fires when a layout is performed.
34067          * @param {Roo.LayoutManager} this
34068          */
34069         "layout" : true,
34070         /**
34071          * @event regionresized
34072          * Fires when the user resizes a region.
34073          * @param {Roo.LayoutRegion} region The resized region
34074          * @param {Number} newSize The new size (width for east/west, height for north/south)
34075          */
34076         "regionresized" : true,
34077         /**
34078          * @event regioncollapsed
34079          * Fires when a region is collapsed.
34080          * @param {Roo.LayoutRegion} region The collapsed region
34081          */
34082         "regioncollapsed" : true,
34083         /**
34084          * @event regionexpanded
34085          * Fires when a region is expanded.
34086          * @param {Roo.LayoutRegion} region The expanded region
34087          */
34088         "regionexpanded" : true
34089     });
34090     this.updating = false;
34091
34092     if (config.el) {
34093         this.el = Roo.get(config.el);
34094         this.initEvents();
34095     }
34096
34097 };
34098
34099 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34100
34101
34102     regions : null,
34103
34104     monitorWindowResize : true,
34105
34106
34107     updating : false,
34108
34109
34110     onRender : function(ct, position)
34111     {
34112         if(!this.el){
34113             this.el = Roo.get(ct);
34114             this.initEvents();
34115         }
34116         //this.fireEvent('render',this);
34117     },
34118
34119
34120     initEvents: function()
34121     {
34122
34123
34124         // ie scrollbar fix
34125         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34126             document.body.scroll = "no";
34127         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34128             this.el.position('relative');
34129         }
34130         this.id = this.el.id;
34131         this.el.addClass("roo-layout-container");
34132         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34133         if(this.el.dom != document.body ) {
34134             this.el.on('resize', this.layout,this);
34135             this.el.on('show', this.layout,this);
34136         }
34137
34138     },
34139
34140     /**
34141      * Returns true if this layout is currently being updated
34142      * @return {Boolean}
34143      */
34144     isUpdating : function(){
34145         return this.updating;
34146     },
34147
34148     /**
34149      * Suspend the LayoutManager from doing auto-layouts while
34150      * making multiple add or remove calls
34151      */
34152     beginUpdate : function(){
34153         this.updating = true;
34154     },
34155
34156     /**
34157      * Restore auto-layouts and optionally disable the manager from performing a layout
34158      * @param {Boolean} noLayout true to disable a layout update
34159      */
34160     endUpdate : function(noLayout){
34161         this.updating = false;
34162         if(!noLayout){
34163             this.layout();
34164         }
34165     },
34166
34167     layout: function(){
34168         // abstract...
34169     },
34170
34171     onRegionResized : function(region, newSize){
34172         this.fireEvent("regionresized", region, newSize);
34173         this.layout();
34174     },
34175
34176     onRegionCollapsed : function(region){
34177         this.fireEvent("regioncollapsed", region);
34178     },
34179
34180     onRegionExpanded : function(region){
34181         this.fireEvent("regionexpanded", region);
34182     },
34183
34184     /**
34185      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34186      * performs box-model adjustments.
34187      * @return {Object} The size as an object {width: (the width), height: (the height)}
34188      */
34189     getViewSize : function()
34190     {
34191         var size;
34192         if(this.el.dom != document.body){
34193             size = this.el.getSize();
34194         }else{
34195             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34196         }
34197         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34198         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34199         return size;
34200     },
34201
34202     /**
34203      * Returns the Element this layout is bound to.
34204      * @return {Roo.Element}
34205      */
34206     getEl : function(){
34207         return this.el;
34208     },
34209
34210     /**
34211      * Returns the specified region.
34212      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34213      * @return {Roo.LayoutRegion}
34214      */
34215     getRegion : function(target){
34216         return this.regions[target.toLowerCase()];
34217     },
34218
34219     onWindowResize : function(){
34220         if(this.monitorWindowResize){
34221             this.layout();
34222         }
34223     }
34224 });
34225 /*
34226  * Based on:
34227  * Ext JS Library 1.1.1
34228  * Copyright(c) 2006-2007, Ext JS, LLC.
34229  *
34230  * Originally Released Under LGPL - original licence link has changed is not relivant.
34231  *
34232  * Fork - LGPL
34233  * <script type="text/javascript">
34234  */
34235 /**
34236  * @class Roo.bootstrap.layout.Border
34237  * @extends Roo.bootstrap.layout.Manager
34238  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34239  * please see: examples/bootstrap/nested.html<br><br>
34240  
34241 <b>The container the layout is rendered into can be either the body element or any other element.
34242 If it is not the body element, the container needs to either be an absolute positioned element,
34243 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34244 the container size if it is not the body element.</b>
34245
34246 * @constructor
34247 * Create a new Border
34248 * @param {Object} config Configuration options
34249  */
34250 Roo.bootstrap.layout.Border = function(config){
34251     config = config || {};
34252     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34253     
34254     
34255     
34256     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34257         if(config[region]){
34258             config[region].region = region;
34259             this.addRegion(config[region]);
34260         }
34261     },this);
34262     
34263 };
34264
34265 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34266
34267 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34268     /**
34269      * Creates and adds a new region if it doesn't already exist.
34270      * @param {String} target The target region key (north, south, east, west or center).
34271      * @param {Object} config The regions config object
34272      * @return {BorderLayoutRegion} The new region
34273      */
34274     addRegion : function(config)
34275     {
34276         if(!this.regions[config.region]){
34277             var r = this.factory(config);
34278             this.bindRegion(r);
34279         }
34280         return this.regions[config.region];
34281     },
34282
34283     // private (kinda)
34284     bindRegion : function(r){
34285         this.regions[r.config.region] = r;
34286         
34287         r.on("visibilitychange",    this.layout, this);
34288         r.on("paneladded",          this.layout, this);
34289         r.on("panelremoved",        this.layout, this);
34290         r.on("invalidated",         this.layout, this);
34291         r.on("resized",             this.onRegionResized, this);
34292         r.on("collapsed",           this.onRegionCollapsed, this);
34293         r.on("expanded",            this.onRegionExpanded, this);
34294     },
34295
34296     /**
34297      * Performs a layout update.
34298      */
34299     layout : function()
34300     {
34301         if(this.updating) {
34302             return;
34303         }
34304         
34305         // render all the rebions if they have not been done alreayd?
34306         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34307             if(this.regions[region] && !this.regions[region].bodyEl){
34308                 this.regions[region].onRender(this.el)
34309             }
34310         },this);
34311         
34312         var size = this.getViewSize();
34313         var w = size.width;
34314         var h = size.height;
34315         var centerW = w;
34316         var centerH = h;
34317         var centerY = 0;
34318         var centerX = 0;
34319         //var x = 0, y = 0;
34320
34321         var rs = this.regions;
34322         var north = rs["north"];
34323         var south = rs["south"]; 
34324         var west = rs["west"];
34325         var east = rs["east"];
34326         var center = rs["center"];
34327         //if(this.hideOnLayout){ // not supported anymore
34328             //c.el.setStyle("display", "none");
34329         //}
34330         if(north && north.isVisible()){
34331             var b = north.getBox();
34332             var m = north.getMargins();
34333             b.width = w - (m.left+m.right);
34334             b.x = m.left;
34335             b.y = m.top;
34336             centerY = b.height + b.y + m.bottom;
34337             centerH -= centerY;
34338             north.updateBox(this.safeBox(b));
34339         }
34340         if(south && south.isVisible()){
34341             var b = south.getBox();
34342             var m = south.getMargins();
34343             b.width = w - (m.left+m.right);
34344             b.x = m.left;
34345             var totalHeight = (b.height + m.top + m.bottom);
34346             b.y = h - totalHeight + m.top;
34347             centerH -= totalHeight;
34348             south.updateBox(this.safeBox(b));
34349         }
34350         if(west && west.isVisible()){
34351             var b = west.getBox();
34352             var m = west.getMargins();
34353             b.height = centerH - (m.top+m.bottom);
34354             b.x = m.left;
34355             b.y = centerY + m.top;
34356             var totalWidth = (b.width + m.left + m.right);
34357             centerX += totalWidth;
34358             centerW -= totalWidth;
34359             west.updateBox(this.safeBox(b));
34360         }
34361         if(east && east.isVisible()){
34362             var b = east.getBox();
34363             var m = east.getMargins();
34364             b.height = centerH - (m.top+m.bottom);
34365             var totalWidth = (b.width + m.left + m.right);
34366             b.x = w - totalWidth + m.left;
34367             b.y = centerY + m.top;
34368             centerW -= totalWidth;
34369             east.updateBox(this.safeBox(b));
34370         }
34371         if(center){
34372             var m = center.getMargins();
34373             var centerBox = {
34374                 x: centerX + m.left,
34375                 y: centerY + m.top,
34376                 width: centerW - (m.left+m.right),
34377                 height: centerH - (m.top+m.bottom)
34378             };
34379             //if(this.hideOnLayout){
34380                 //center.el.setStyle("display", "block");
34381             //}
34382             center.updateBox(this.safeBox(centerBox));
34383         }
34384         this.el.repaint();
34385         this.fireEvent("layout", this);
34386     },
34387
34388     // private
34389     safeBox : function(box){
34390         box.width = Math.max(0, box.width);
34391         box.height = Math.max(0, box.height);
34392         return box;
34393     },
34394
34395     /**
34396      * Adds a ContentPanel (or subclass) to this layout.
34397      * @param {String} target The target region key (north, south, east, west or center).
34398      * @param {Roo.ContentPanel} panel The panel to add
34399      * @return {Roo.ContentPanel} The added panel
34400      */
34401     add : function(target, panel){
34402          
34403         target = target.toLowerCase();
34404         return this.regions[target].add(panel);
34405     },
34406
34407     /**
34408      * Remove a ContentPanel (or subclass) to this layout.
34409      * @param {String} target The target region key (north, south, east, west or center).
34410      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34411      * @return {Roo.ContentPanel} The removed panel
34412      */
34413     remove : function(target, panel){
34414         target = target.toLowerCase();
34415         return this.regions[target].remove(panel);
34416     },
34417
34418     /**
34419      * Searches all regions for a panel with the specified id
34420      * @param {String} panelId
34421      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34422      */
34423     findPanel : function(panelId){
34424         var rs = this.regions;
34425         for(var target in rs){
34426             if(typeof rs[target] != "function"){
34427                 var p = rs[target].getPanel(panelId);
34428                 if(p){
34429                     return p;
34430                 }
34431             }
34432         }
34433         return null;
34434     },
34435
34436     /**
34437      * Searches all regions for a panel with the specified id and activates (shows) it.
34438      * @param {String/ContentPanel} panelId The panels id or the panel itself
34439      * @return {Roo.ContentPanel} The shown panel or null
34440      */
34441     showPanel : function(panelId) {
34442       var rs = this.regions;
34443       for(var target in rs){
34444          var r = rs[target];
34445          if(typeof r != "function"){
34446             if(r.hasPanel(panelId)){
34447                return r.showPanel(panelId);
34448             }
34449          }
34450       }
34451       return null;
34452    },
34453
34454    /**
34455      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34456      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34457      */
34458    /*
34459     restoreState : function(provider){
34460         if(!provider){
34461             provider = Roo.state.Manager;
34462         }
34463         var sm = new Roo.LayoutStateManager();
34464         sm.init(this, provider);
34465     },
34466 */
34467  
34468  
34469     /**
34470      * Adds a xtype elements to the layout.
34471      * <pre><code>
34472
34473 layout.addxtype({
34474        xtype : 'ContentPanel',
34475        region: 'west',
34476        items: [ .... ]
34477    }
34478 );
34479
34480 layout.addxtype({
34481         xtype : 'NestedLayoutPanel',
34482         region: 'west',
34483         layout: {
34484            center: { },
34485            west: { }   
34486         },
34487         items : [ ... list of content panels or nested layout panels.. ]
34488    }
34489 );
34490 </code></pre>
34491      * @param {Object} cfg Xtype definition of item to add.
34492      */
34493     addxtype : function(cfg)
34494     {
34495         // basically accepts a pannel...
34496         // can accept a layout region..!?!?
34497         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34498         
34499         
34500         // theory?  children can only be panels??
34501         
34502         //if (!cfg.xtype.match(/Panel$/)) {
34503         //    return false;
34504         //}
34505         var ret = false;
34506         
34507         if (typeof(cfg.region) == 'undefined') {
34508             Roo.log("Failed to add Panel, region was not set");
34509             Roo.log(cfg);
34510             return false;
34511         }
34512         var region = cfg.region;
34513         delete cfg.region;
34514         
34515           
34516         var xitems = [];
34517         if (cfg.items) {
34518             xitems = cfg.items;
34519             delete cfg.items;
34520         }
34521         var nb = false;
34522         
34523         switch(cfg.xtype) 
34524         {
34525             case 'Content':  // ContentPanel (el, cfg)
34526             case 'Scroll':  // ContentPanel (el, cfg)
34527             case 'View': 
34528                 cfg.autoCreate = true;
34529                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34530                 //} else {
34531                 //    var el = this.el.createChild();
34532                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34533                 //}
34534                 
34535                 this.add(region, ret);
34536                 break;
34537             
34538             /*
34539             case 'TreePanel': // our new panel!
34540                 cfg.el = this.el.createChild();
34541                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34542                 this.add(region, ret);
34543                 break;
34544             */
34545             
34546             case 'Nest': 
34547                 // create a new Layout (which is  a Border Layout...
34548                 
34549                 var clayout = cfg.layout;
34550                 clayout.el  = this.el.createChild();
34551                 clayout.items   = clayout.items  || [];
34552                 
34553                 delete cfg.layout;
34554                 
34555                 // replace this exitems with the clayout ones..
34556                 xitems = clayout.items;
34557                  
34558                 // force background off if it's in center...
34559                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34560                     cfg.background = false;
34561                 }
34562                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34563                 
34564                 
34565                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34566                 //console.log('adding nested layout panel '  + cfg.toSource());
34567                 this.add(region, ret);
34568                 nb = {}; /// find first...
34569                 break;
34570             
34571             case 'Grid':
34572                 
34573                 // needs grid and region
34574                 
34575                 //var el = this.getRegion(region).el.createChild();
34576                 /*
34577                  *var el = this.el.createChild();
34578                 // create the grid first...
34579                 cfg.grid.container = el;
34580                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34581                 */
34582                 
34583                 if (region == 'center' && this.active ) {
34584                     cfg.background = false;
34585                 }
34586                 
34587                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34588                 
34589                 this.add(region, ret);
34590                 /*
34591                 if (cfg.background) {
34592                     // render grid on panel activation (if panel background)
34593                     ret.on('activate', function(gp) {
34594                         if (!gp.grid.rendered) {
34595                     //        gp.grid.render(el);
34596                         }
34597                     });
34598                 } else {
34599                   //  cfg.grid.render(el);
34600                 }
34601                 */
34602                 break;
34603            
34604            
34605             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34606                 // it was the old xcomponent building that caused this before.
34607                 // espeically if border is the top element in the tree.
34608                 ret = this;
34609                 break; 
34610                 
34611                     
34612                 
34613                 
34614                 
34615             default:
34616                 /*
34617                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34618                     
34619                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34620                     this.add(region, ret);
34621                 } else {
34622                 */
34623                     Roo.log(cfg);
34624                     throw "Can not add '" + cfg.xtype + "' to Border";
34625                     return null;
34626              
34627                                 
34628              
34629         }
34630         this.beginUpdate();
34631         // add children..
34632         var region = '';
34633         var abn = {};
34634         Roo.each(xitems, function(i)  {
34635             region = nb && i.region ? i.region : false;
34636             
34637             var add = ret.addxtype(i);
34638            
34639             if (region) {
34640                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34641                 if (!i.background) {
34642                     abn[region] = nb[region] ;
34643                 }
34644             }
34645             
34646         });
34647         this.endUpdate();
34648
34649         // make the last non-background panel active..
34650         //if (nb) { Roo.log(abn); }
34651         if (nb) {
34652             
34653             for(var r in abn) {
34654                 region = this.getRegion(r);
34655                 if (region) {
34656                     // tried using nb[r], but it does not work..
34657                      
34658                     region.showPanel(abn[r]);
34659                    
34660                 }
34661             }
34662         }
34663         return ret;
34664         
34665     },
34666     
34667     
34668 // private
34669     factory : function(cfg)
34670     {
34671         
34672         var validRegions = Roo.bootstrap.layout.Border.regions;
34673
34674         var target = cfg.region;
34675         cfg.mgr = this;
34676         
34677         var r = Roo.bootstrap.layout;
34678         Roo.log(target);
34679         switch(target){
34680             case "north":
34681                 return new r.North(cfg);
34682             case "south":
34683                 return new r.South(cfg);
34684             case "east":
34685                 return new r.East(cfg);
34686             case "west":
34687                 return new r.West(cfg);
34688             case "center":
34689                 return new r.Center(cfg);
34690         }
34691         throw 'Layout region "'+target+'" not supported.';
34692     }
34693     
34694     
34695 });
34696  /*
34697  * Based on:
34698  * Ext JS Library 1.1.1
34699  * Copyright(c) 2006-2007, Ext JS, LLC.
34700  *
34701  * Originally Released Under LGPL - original licence link has changed is not relivant.
34702  *
34703  * Fork - LGPL
34704  * <script type="text/javascript">
34705  */
34706  
34707 /**
34708  * @class Roo.bootstrap.layout.Basic
34709  * @extends Roo.util.Observable
34710  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34711  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34712  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34713  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34714  * @cfg {string}   region  the region that it inhabits..
34715  * @cfg {bool}   skipConfig skip config?
34716  * 
34717
34718  */
34719 Roo.bootstrap.layout.Basic = function(config){
34720     
34721     this.mgr = config.mgr;
34722     
34723     this.position = config.region;
34724     
34725     var skipConfig = config.skipConfig;
34726     
34727     this.events = {
34728         /**
34729          * @scope Roo.BasicLayoutRegion
34730          */
34731         
34732         /**
34733          * @event beforeremove
34734          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34735          * @param {Roo.LayoutRegion} this
34736          * @param {Roo.ContentPanel} panel The panel
34737          * @param {Object} e The cancel event object
34738          */
34739         "beforeremove" : true,
34740         /**
34741          * @event invalidated
34742          * Fires when the layout for this region is changed.
34743          * @param {Roo.LayoutRegion} this
34744          */
34745         "invalidated" : true,
34746         /**
34747          * @event visibilitychange
34748          * Fires when this region is shown or hidden 
34749          * @param {Roo.LayoutRegion} this
34750          * @param {Boolean} visibility true or false
34751          */
34752         "visibilitychange" : true,
34753         /**
34754          * @event paneladded
34755          * Fires when a panel is added. 
34756          * @param {Roo.LayoutRegion} this
34757          * @param {Roo.ContentPanel} panel The panel
34758          */
34759         "paneladded" : true,
34760         /**
34761          * @event panelremoved
34762          * Fires when a panel is removed. 
34763          * @param {Roo.LayoutRegion} this
34764          * @param {Roo.ContentPanel} panel The panel
34765          */
34766         "panelremoved" : true,
34767         /**
34768          * @event beforecollapse
34769          * Fires when this region before collapse.
34770          * @param {Roo.LayoutRegion} this
34771          */
34772         "beforecollapse" : true,
34773         /**
34774          * @event collapsed
34775          * Fires when this region is collapsed.
34776          * @param {Roo.LayoutRegion} this
34777          */
34778         "collapsed" : true,
34779         /**
34780          * @event expanded
34781          * Fires when this region is expanded.
34782          * @param {Roo.LayoutRegion} this
34783          */
34784         "expanded" : true,
34785         /**
34786          * @event slideshow
34787          * Fires when this region is slid into view.
34788          * @param {Roo.LayoutRegion} this
34789          */
34790         "slideshow" : true,
34791         /**
34792          * @event slidehide
34793          * Fires when this region slides out of view. 
34794          * @param {Roo.LayoutRegion} this
34795          */
34796         "slidehide" : true,
34797         /**
34798          * @event panelactivated
34799          * Fires when a panel is activated. 
34800          * @param {Roo.LayoutRegion} this
34801          * @param {Roo.ContentPanel} panel The activated panel
34802          */
34803         "panelactivated" : true,
34804         /**
34805          * @event resized
34806          * Fires when the user resizes this region. 
34807          * @param {Roo.LayoutRegion} this
34808          * @param {Number} newSize The new size (width for east/west, height for north/south)
34809          */
34810         "resized" : true
34811     };
34812     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34813     this.panels = new Roo.util.MixedCollection();
34814     this.panels.getKey = this.getPanelId.createDelegate(this);
34815     this.box = null;
34816     this.activePanel = null;
34817     // ensure listeners are added...
34818     
34819     if (config.listeners || config.events) {
34820         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34821             listeners : config.listeners || {},
34822             events : config.events || {}
34823         });
34824     }
34825     
34826     if(skipConfig !== true){
34827         this.applyConfig(config);
34828     }
34829 };
34830
34831 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34832 {
34833     getPanelId : function(p){
34834         return p.getId();
34835     },
34836     
34837     applyConfig : function(config){
34838         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34839         this.config = config;
34840         
34841     },
34842     
34843     /**
34844      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34845      * the width, for horizontal (north, south) the height.
34846      * @param {Number} newSize The new width or height
34847      */
34848     resizeTo : function(newSize){
34849         var el = this.el ? this.el :
34850                  (this.activePanel ? this.activePanel.getEl() : null);
34851         if(el){
34852             switch(this.position){
34853                 case "east":
34854                 case "west":
34855                     el.setWidth(newSize);
34856                     this.fireEvent("resized", this, newSize);
34857                 break;
34858                 case "north":
34859                 case "south":
34860                     el.setHeight(newSize);
34861                     this.fireEvent("resized", this, newSize);
34862                 break;                
34863             }
34864         }
34865     },
34866     
34867     getBox : function(){
34868         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34869     },
34870     
34871     getMargins : function(){
34872         return this.margins;
34873     },
34874     
34875     updateBox : function(box){
34876         this.box = box;
34877         var el = this.activePanel.getEl();
34878         el.dom.style.left = box.x + "px";
34879         el.dom.style.top = box.y + "px";
34880         this.activePanel.setSize(box.width, box.height);
34881     },
34882     
34883     /**
34884      * Returns the container element for this region.
34885      * @return {Roo.Element}
34886      */
34887     getEl : function(){
34888         return this.activePanel;
34889     },
34890     
34891     /**
34892      * Returns true if this region is currently visible.
34893      * @return {Boolean}
34894      */
34895     isVisible : function(){
34896         return this.activePanel ? true : false;
34897     },
34898     
34899     setActivePanel : function(panel){
34900         panel = this.getPanel(panel);
34901         if(this.activePanel && this.activePanel != panel){
34902             this.activePanel.setActiveState(false);
34903             this.activePanel.getEl().setLeftTop(-10000,-10000);
34904         }
34905         this.activePanel = panel;
34906         panel.setActiveState(true);
34907         if(this.box){
34908             panel.setSize(this.box.width, this.box.height);
34909         }
34910         this.fireEvent("panelactivated", this, panel);
34911         this.fireEvent("invalidated");
34912     },
34913     
34914     /**
34915      * Show the specified panel.
34916      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34917      * @return {Roo.ContentPanel} The shown panel or null
34918      */
34919     showPanel : function(panel){
34920         panel = this.getPanel(panel);
34921         if(panel){
34922             this.setActivePanel(panel);
34923         }
34924         return panel;
34925     },
34926     
34927     /**
34928      * Get the active panel for this region.
34929      * @return {Roo.ContentPanel} The active panel or null
34930      */
34931     getActivePanel : function(){
34932         return this.activePanel;
34933     },
34934     
34935     /**
34936      * Add the passed ContentPanel(s)
34937      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34938      * @return {Roo.ContentPanel} The panel added (if only one was added)
34939      */
34940     add : function(panel){
34941         if(arguments.length > 1){
34942             for(var i = 0, len = arguments.length; i < len; i++) {
34943                 this.add(arguments[i]);
34944             }
34945             return null;
34946         }
34947         if(this.hasPanel(panel)){
34948             this.showPanel(panel);
34949             return panel;
34950         }
34951         var el = panel.getEl();
34952         if(el.dom.parentNode != this.mgr.el.dom){
34953             this.mgr.el.dom.appendChild(el.dom);
34954         }
34955         if(panel.setRegion){
34956             panel.setRegion(this);
34957         }
34958         this.panels.add(panel);
34959         el.setStyle("position", "absolute");
34960         if(!panel.background){
34961             this.setActivePanel(panel);
34962             if(this.config.initialSize && this.panels.getCount()==1){
34963                 this.resizeTo(this.config.initialSize);
34964             }
34965         }
34966         this.fireEvent("paneladded", this, panel);
34967         return panel;
34968     },
34969     
34970     /**
34971      * Returns true if the panel is in this region.
34972      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34973      * @return {Boolean}
34974      */
34975     hasPanel : function(panel){
34976         if(typeof panel == "object"){ // must be panel obj
34977             panel = panel.getId();
34978         }
34979         return this.getPanel(panel) ? true : false;
34980     },
34981     
34982     /**
34983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34984      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34985      * @param {Boolean} preservePanel Overrides the config preservePanel option
34986      * @return {Roo.ContentPanel} The panel that was removed
34987      */
34988     remove : function(panel, preservePanel){
34989         panel = this.getPanel(panel);
34990         if(!panel){
34991             return null;
34992         }
34993         var e = {};
34994         this.fireEvent("beforeremove", this, panel, e);
34995         if(e.cancel === true){
34996             return null;
34997         }
34998         var panelId = panel.getId();
34999         this.panels.removeKey(panelId);
35000         return panel;
35001     },
35002     
35003     /**
35004      * Returns the panel specified or null if it's not in this region.
35005      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35006      * @return {Roo.ContentPanel}
35007      */
35008     getPanel : function(id){
35009         if(typeof id == "object"){ // must be panel obj
35010             return id;
35011         }
35012         return this.panels.get(id);
35013     },
35014     
35015     /**
35016      * Returns this regions position (north/south/east/west/center).
35017      * @return {String} 
35018      */
35019     getPosition: function(){
35020         return this.position;    
35021     }
35022 });/*
35023  * Based on:
35024  * Ext JS Library 1.1.1
35025  * Copyright(c) 2006-2007, Ext JS, LLC.
35026  *
35027  * Originally Released Under LGPL - original licence link has changed is not relivant.
35028  *
35029  * Fork - LGPL
35030  * <script type="text/javascript">
35031  */
35032  
35033 /**
35034  * @class Roo.bootstrap.layout.Region
35035  * @extends Roo.bootstrap.layout.Basic
35036  * This class represents a region in a layout manager.
35037  
35038  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35039  * @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})
35040  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35041  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35042  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35043  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35044  * @cfg {String}    title           The title for the region (overrides panel titles)
35045  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35046  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35047  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35048  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35049  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35050  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35051  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35052  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35053  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35054  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35055
35056  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35057  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35058  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35059  * @cfg {Number}    width           For East/West panels
35060  * @cfg {Number}    height          For North/South panels
35061  * @cfg {Boolean}   split           To show the splitter
35062  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35063  * 
35064  * @cfg {string}   cls             Extra CSS classes to add to region
35065  * 
35066  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35067  * @cfg {string}   region  the region that it inhabits..
35068  *
35069
35070  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35071  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35072
35073  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35074  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35075  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35076  */
35077 Roo.bootstrap.layout.Region = function(config)
35078 {
35079     this.applyConfig(config);
35080
35081     var mgr = config.mgr;
35082     var pos = config.region;
35083     config.skipConfig = true;
35084     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35085     
35086     if (mgr.el) {
35087         this.onRender(mgr.el);   
35088     }
35089      
35090     this.visible = true;
35091     this.collapsed = false;
35092     this.unrendered_panels = [];
35093 };
35094
35095 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35096
35097     position: '', // set by wrapper (eg. north/south etc..)
35098     unrendered_panels : null,  // unrendered panels.
35099     createBody : function(){
35100         /** This region's body element 
35101         * @type Roo.Element */
35102         this.bodyEl = this.el.createChild({
35103                 tag: "div",
35104                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35105         });
35106     },
35107
35108     onRender: function(ctr, pos)
35109     {
35110         var dh = Roo.DomHelper;
35111         /** This region's container element 
35112         * @type Roo.Element */
35113         this.el = dh.append(ctr.dom, {
35114                 tag: "div",
35115                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35116             }, true);
35117         /** This region's title element 
35118         * @type Roo.Element */
35119     
35120         this.titleEl = dh.append(this.el.dom,
35121             {
35122                     tag: "div",
35123                     unselectable: "on",
35124                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35125                     children:[
35126                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35127                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35128                     ]}, true);
35129         
35130         this.titleEl.enableDisplayMode();
35131         /** This region's title text element 
35132         * @type HTMLElement */
35133         this.titleTextEl = this.titleEl.dom.firstChild;
35134         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35135         /*
35136         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35137         this.closeBtn.enableDisplayMode();
35138         this.closeBtn.on("click", this.closeClicked, this);
35139         this.closeBtn.hide();
35140     */
35141         this.createBody(this.config);
35142         if(this.config.hideWhenEmpty){
35143             this.hide();
35144             this.on("paneladded", this.validateVisibility, this);
35145             this.on("panelremoved", this.validateVisibility, this);
35146         }
35147         if(this.autoScroll){
35148             this.bodyEl.setStyle("overflow", "auto");
35149         }else{
35150             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35151         }
35152         //if(c.titlebar !== false){
35153             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35154                 this.titleEl.hide();
35155             }else{
35156                 this.titleEl.show();
35157                 if(this.config.title){
35158                     this.titleTextEl.innerHTML = this.config.title;
35159                 }
35160             }
35161         //}
35162         if(this.config.collapsed){
35163             this.collapse(true);
35164         }
35165         if(this.config.hidden){
35166             this.hide();
35167         }
35168         
35169         if (this.unrendered_panels && this.unrendered_panels.length) {
35170             for (var i =0;i< this.unrendered_panels.length; i++) {
35171                 this.add(this.unrendered_panels[i]);
35172             }
35173             this.unrendered_panels = null;
35174             
35175         }
35176         
35177     },
35178     
35179     applyConfig : function(c)
35180     {
35181         /*
35182          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35183             var dh = Roo.DomHelper;
35184             if(c.titlebar !== false){
35185                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35186                 this.collapseBtn.on("click", this.collapse, this);
35187                 this.collapseBtn.enableDisplayMode();
35188                 /*
35189                 if(c.showPin === true || this.showPin){
35190                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35191                     this.stickBtn.enableDisplayMode();
35192                     this.stickBtn.on("click", this.expand, this);
35193                     this.stickBtn.hide();
35194                 }
35195                 
35196             }
35197             */
35198             /** This region's collapsed element
35199             * @type Roo.Element */
35200             /*
35201              *
35202             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35203                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35204             ]}, true);
35205             
35206             if(c.floatable !== false){
35207                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35208                this.collapsedEl.on("click", this.collapseClick, this);
35209             }
35210
35211             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35212                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35213                    id: "message", unselectable: "on", style:{"float":"left"}});
35214                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35215              }
35216             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35217             this.expandBtn.on("click", this.expand, this);
35218             
35219         }
35220         
35221         if(this.collapseBtn){
35222             this.collapseBtn.setVisible(c.collapsible == true);
35223         }
35224         
35225         this.cmargins = c.cmargins || this.cmargins ||
35226                          (this.position == "west" || this.position == "east" ?
35227                              {top: 0, left: 2, right:2, bottom: 0} :
35228                              {top: 2, left: 0, right:0, bottom: 2});
35229         */
35230         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35231         
35232         
35233         this.bottomTabs = c.tabPosition != "top";
35234         
35235         this.autoScroll = c.autoScroll || false;
35236         
35237         
35238        
35239         
35240         this.duration = c.duration || .30;
35241         this.slideDuration = c.slideDuration || .45;
35242         this.config = c;
35243        
35244     },
35245     /**
35246      * Returns true if this region is currently visible.
35247      * @return {Boolean}
35248      */
35249     isVisible : function(){
35250         return this.visible;
35251     },
35252
35253     /**
35254      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35255      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35256      */
35257     //setCollapsedTitle : function(title){
35258     //    title = title || "&#160;";
35259      //   if(this.collapsedTitleTextEl){
35260       //      this.collapsedTitleTextEl.innerHTML = title;
35261        // }
35262     //},
35263
35264     getBox : function(){
35265         var b;
35266       //  if(!this.collapsed){
35267             b = this.el.getBox(false, true);
35268        // }else{
35269           //  b = this.collapsedEl.getBox(false, true);
35270         //}
35271         return b;
35272     },
35273
35274     getMargins : function(){
35275         return this.margins;
35276         //return this.collapsed ? this.cmargins : this.margins;
35277     },
35278 /*
35279     highlight : function(){
35280         this.el.addClass("x-layout-panel-dragover");
35281     },
35282
35283     unhighlight : function(){
35284         this.el.removeClass("x-layout-panel-dragover");
35285     },
35286 */
35287     updateBox : function(box)
35288     {
35289         if (!this.bodyEl) {
35290             return; // not rendered yet..
35291         }
35292         
35293         this.box = box;
35294         if(!this.collapsed){
35295             this.el.dom.style.left = box.x + "px";
35296             this.el.dom.style.top = box.y + "px";
35297             this.updateBody(box.width, box.height);
35298         }else{
35299             this.collapsedEl.dom.style.left = box.x + "px";
35300             this.collapsedEl.dom.style.top = box.y + "px";
35301             this.collapsedEl.setSize(box.width, box.height);
35302         }
35303         if(this.tabs){
35304             this.tabs.autoSizeTabs();
35305         }
35306     },
35307
35308     updateBody : function(w, h)
35309     {
35310         if(w !== null){
35311             this.el.setWidth(w);
35312             w -= this.el.getBorderWidth("rl");
35313             if(this.config.adjustments){
35314                 w += this.config.adjustments[0];
35315             }
35316         }
35317         if(h !== null && h > 0){
35318             this.el.setHeight(h);
35319             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35320             h -= this.el.getBorderWidth("tb");
35321             if(this.config.adjustments){
35322                 h += this.config.adjustments[1];
35323             }
35324             this.bodyEl.setHeight(h);
35325             if(this.tabs){
35326                 h = this.tabs.syncHeight(h);
35327             }
35328         }
35329         if(this.panelSize){
35330             w = w !== null ? w : this.panelSize.width;
35331             h = h !== null ? h : this.panelSize.height;
35332         }
35333         if(this.activePanel){
35334             var el = this.activePanel.getEl();
35335             w = w !== null ? w : el.getWidth();
35336             h = h !== null ? h : el.getHeight();
35337             this.panelSize = {width: w, height: h};
35338             this.activePanel.setSize(w, h);
35339         }
35340         if(Roo.isIE && this.tabs){
35341             this.tabs.el.repaint();
35342         }
35343     },
35344
35345     /**
35346      * Returns the container element for this region.
35347      * @return {Roo.Element}
35348      */
35349     getEl : function(){
35350         return this.el;
35351     },
35352
35353     /**
35354      * Hides this region.
35355      */
35356     hide : function(){
35357         //if(!this.collapsed){
35358             this.el.dom.style.left = "-2000px";
35359             this.el.hide();
35360         //}else{
35361          //   this.collapsedEl.dom.style.left = "-2000px";
35362          //   this.collapsedEl.hide();
35363        // }
35364         this.visible = false;
35365         this.fireEvent("visibilitychange", this, false);
35366     },
35367
35368     /**
35369      * Shows this region if it was previously hidden.
35370      */
35371     show : function(){
35372         //if(!this.collapsed){
35373             this.el.show();
35374         //}else{
35375         //    this.collapsedEl.show();
35376        // }
35377         this.visible = true;
35378         this.fireEvent("visibilitychange", this, true);
35379     },
35380 /*
35381     closeClicked : function(){
35382         if(this.activePanel){
35383             this.remove(this.activePanel);
35384         }
35385     },
35386
35387     collapseClick : function(e){
35388         if(this.isSlid){
35389            e.stopPropagation();
35390            this.slideIn();
35391         }else{
35392            e.stopPropagation();
35393            this.slideOut();
35394         }
35395     },
35396 */
35397     /**
35398      * Collapses this region.
35399      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35400      */
35401     /*
35402     collapse : function(skipAnim, skipCheck = false){
35403         if(this.collapsed) {
35404             return;
35405         }
35406         
35407         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35408             
35409             this.collapsed = true;
35410             if(this.split){
35411                 this.split.el.hide();
35412             }
35413             if(this.config.animate && skipAnim !== true){
35414                 this.fireEvent("invalidated", this);
35415                 this.animateCollapse();
35416             }else{
35417                 this.el.setLocation(-20000,-20000);
35418                 this.el.hide();
35419                 this.collapsedEl.show();
35420                 this.fireEvent("collapsed", this);
35421                 this.fireEvent("invalidated", this);
35422             }
35423         }
35424         
35425     },
35426 */
35427     animateCollapse : function(){
35428         // overridden
35429     },
35430
35431     /**
35432      * Expands this region if it was previously collapsed.
35433      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35434      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35435      */
35436     /*
35437     expand : function(e, skipAnim){
35438         if(e) {
35439             e.stopPropagation();
35440         }
35441         if(!this.collapsed || this.el.hasActiveFx()) {
35442             return;
35443         }
35444         if(this.isSlid){
35445             this.afterSlideIn();
35446             skipAnim = true;
35447         }
35448         this.collapsed = false;
35449         if(this.config.animate && skipAnim !== true){
35450             this.animateExpand();
35451         }else{
35452             this.el.show();
35453             if(this.split){
35454                 this.split.el.show();
35455             }
35456             this.collapsedEl.setLocation(-2000,-2000);
35457             this.collapsedEl.hide();
35458             this.fireEvent("invalidated", this);
35459             this.fireEvent("expanded", this);
35460         }
35461     },
35462 */
35463     animateExpand : function(){
35464         // overridden
35465     },
35466
35467     initTabs : function()
35468     {
35469         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35470         
35471         var ts = new Roo.bootstrap.panel.Tabs({
35472                 el: this.bodyEl.dom,
35473                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35474                 disableTooltips: this.config.disableTabTips,
35475                 toolbar : this.config.toolbar
35476             });
35477         
35478         if(this.config.hideTabs){
35479             ts.stripWrap.setDisplayed(false);
35480         }
35481         this.tabs = ts;
35482         ts.resizeTabs = this.config.resizeTabs === true;
35483         ts.minTabWidth = this.config.minTabWidth || 40;
35484         ts.maxTabWidth = this.config.maxTabWidth || 250;
35485         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35486         ts.monitorResize = false;
35487         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35488         ts.bodyEl.addClass('roo-layout-tabs-body');
35489         this.panels.each(this.initPanelAsTab, this);
35490     },
35491
35492     initPanelAsTab : function(panel){
35493         var ti = this.tabs.addTab(
35494             panel.getEl().id,
35495             panel.getTitle(),
35496             null,
35497             this.config.closeOnTab && panel.isClosable(),
35498             panel.tpl
35499         );
35500         if(panel.tabTip !== undefined){
35501             ti.setTooltip(panel.tabTip);
35502         }
35503         ti.on("activate", function(){
35504               this.setActivePanel(panel);
35505         }, this);
35506         
35507         if(this.config.closeOnTab){
35508             ti.on("beforeclose", function(t, e){
35509                 e.cancel = true;
35510                 this.remove(panel);
35511             }, this);
35512         }
35513         
35514         panel.tabItem = ti;
35515         
35516         return ti;
35517     },
35518
35519     updatePanelTitle : function(panel, title)
35520     {
35521         if(this.activePanel == panel){
35522             this.updateTitle(title);
35523         }
35524         if(this.tabs){
35525             var ti = this.tabs.getTab(panel.getEl().id);
35526             ti.setText(title);
35527             if(panel.tabTip !== undefined){
35528                 ti.setTooltip(panel.tabTip);
35529             }
35530         }
35531     },
35532
35533     updateTitle : function(title){
35534         if(this.titleTextEl && !this.config.title){
35535             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35536         }
35537     },
35538
35539     setActivePanel : function(panel)
35540     {
35541         panel = this.getPanel(panel);
35542         if(this.activePanel && this.activePanel != panel){
35543             if(this.activePanel.setActiveState(false) === false){
35544                 return;
35545             }
35546         }
35547         this.activePanel = panel;
35548         panel.setActiveState(true);
35549         if(this.panelSize){
35550             panel.setSize(this.panelSize.width, this.panelSize.height);
35551         }
35552         if(this.closeBtn){
35553             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35554         }
35555         this.updateTitle(panel.getTitle());
35556         if(this.tabs){
35557             this.fireEvent("invalidated", this);
35558         }
35559         this.fireEvent("panelactivated", this, panel);
35560     },
35561
35562     /**
35563      * Shows the specified panel.
35564      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35565      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35566      */
35567     showPanel : function(panel)
35568     {
35569         panel = this.getPanel(panel);
35570         if(panel){
35571             if(this.tabs){
35572                 var tab = this.tabs.getTab(panel.getEl().id);
35573                 if(tab.isHidden()){
35574                     this.tabs.unhideTab(tab.id);
35575                 }
35576                 tab.activate();
35577             }else{
35578                 this.setActivePanel(panel);
35579             }
35580         }
35581         return panel;
35582     },
35583
35584     /**
35585      * Get the active panel for this region.
35586      * @return {Roo.ContentPanel} The active panel or null
35587      */
35588     getActivePanel : function(){
35589         return this.activePanel;
35590     },
35591
35592     validateVisibility : function(){
35593         if(this.panels.getCount() < 1){
35594             this.updateTitle("&#160;");
35595             this.closeBtn.hide();
35596             this.hide();
35597         }else{
35598             if(!this.isVisible()){
35599                 this.show();
35600             }
35601         }
35602     },
35603
35604     /**
35605      * Adds the passed ContentPanel(s) to this region.
35606      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35607      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35608      */
35609     add : function(panel)
35610     {
35611         if(arguments.length > 1){
35612             for(var i = 0, len = arguments.length; i < len; i++) {
35613                 this.add(arguments[i]);
35614             }
35615             return null;
35616         }
35617         
35618         // if we have not been rendered yet, then we can not really do much of this..
35619         if (!this.bodyEl) {
35620             this.unrendered_panels.push(panel);
35621             return panel;
35622         }
35623         
35624         
35625         
35626         
35627         if(this.hasPanel(panel)){
35628             this.showPanel(panel);
35629             return panel;
35630         }
35631         panel.setRegion(this);
35632         this.panels.add(panel);
35633        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35634             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35635             // and hide them... ???
35636             this.bodyEl.dom.appendChild(panel.getEl().dom);
35637             if(panel.background !== true){
35638                 this.setActivePanel(panel);
35639             }
35640             this.fireEvent("paneladded", this, panel);
35641             return panel;
35642         }
35643         */
35644         if(!this.tabs){
35645             this.initTabs();
35646         }else{
35647             this.initPanelAsTab(panel);
35648         }
35649         
35650         
35651         if(panel.background !== true){
35652             this.tabs.activate(panel.getEl().id);
35653         }
35654         this.fireEvent("paneladded", this, panel);
35655         return panel;
35656     },
35657
35658     /**
35659      * Hides the tab for the specified panel.
35660      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35661      */
35662     hidePanel : function(panel){
35663         if(this.tabs && (panel = this.getPanel(panel))){
35664             this.tabs.hideTab(panel.getEl().id);
35665         }
35666     },
35667
35668     /**
35669      * Unhides the tab for a previously hidden panel.
35670      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35671      */
35672     unhidePanel : function(panel){
35673         if(this.tabs && (panel = this.getPanel(panel))){
35674             this.tabs.unhideTab(panel.getEl().id);
35675         }
35676     },
35677
35678     clearPanels : function(){
35679         while(this.panels.getCount() > 0){
35680              this.remove(this.panels.first());
35681         }
35682     },
35683
35684     /**
35685      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35686      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35687      * @param {Boolean} preservePanel Overrides the config preservePanel option
35688      * @return {Roo.ContentPanel} The panel that was removed
35689      */
35690     remove : function(panel, preservePanel)
35691     {
35692         panel = this.getPanel(panel);
35693         if(!panel){
35694             return null;
35695         }
35696         var e = {};
35697         this.fireEvent("beforeremove", this, panel, e);
35698         if(e.cancel === true){
35699             return null;
35700         }
35701         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35702         var panelId = panel.getId();
35703         this.panels.removeKey(panelId);
35704         if(preservePanel){
35705             document.body.appendChild(panel.getEl().dom);
35706         }
35707         if(this.tabs){
35708             this.tabs.removeTab(panel.getEl().id);
35709         }else if (!preservePanel){
35710             this.bodyEl.dom.removeChild(panel.getEl().dom);
35711         }
35712         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35713             var p = this.panels.first();
35714             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35715             tempEl.appendChild(p.getEl().dom);
35716             this.bodyEl.update("");
35717             this.bodyEl.dom.appendChild(p.getEl().dom);
35718             tempEl = null;
35719             this.updateTitle(p.getTitle());
35720             this.tabs = null;
35721             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35722             this.setActivePanel(p);
35723         }
35724         panel.setRegion(null);
35725         if(this.activePanel == panel){
35726             this.activePanel = null;
35727         }
35728         if(this.config.autoDestroy !== false && preservePanel !== true){
35729             try{panel.destroy();}catch(e){}
35730         }
35731         this.fireEvent("panelremoved", this, panel);
35732         return panel;
35733     },
35734
35735     /**
35736      * Returns the TabPanel component used by this region
35737      * @return {Roo.TabPanel}
35738      */
35739     getTabs : function(){
35740         return this.tabs;
35741     },
35742
35743     createTool : function(parentEl, className){
35744         var btn = Roo.DomHelper.append(parentEl, {
35745             tag: "div",
35746             cls: "x-layout-tools-button",
35747             children: [ {
35748                 tag: "div",
35749                 cls: "roo-layout-tools-button-inner " + className,
35750                 html: "&#160;"
35751             }]
35752         }, true);
35753         btn.addClassOnOver("roo-layout-tools-button-over");
35754         return btn;
35755     }
35756 });/*
35757  * Based on:
35758  * Ext JS Library 1.1.1
35759  * Copyright(c) 2006-2007, Ext JS, LLC.
35760  *
35761  * Originally Released Under LGPL - original licence link has changed is not relivant.
35762  *
35763  * Fork - LGPL
35764  * <script type="text/javascript">
35765  */
35766  
35767
35768
35769 /**
35770  * @class Roo.SplitLayoutRegion
35771  * @extends Roo.LayoutRegion
35772  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35773  */
35774 Roo.bootstrap.layout.Split = function(config){
35775     this.cursor = config.cursor;
35776     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35777 };
35778
35779 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35780 {
35781     splitTip : "Drag to resize.",
35782     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35783     useSplitTips : false,
35784
35785     applyConfig : function(config){
35786         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35787     },
35788     
35789     onRender : function(ctr,pos) {
35790         
35791         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35792         if(!this.config.split){
35793             return;
35794         }
35795         if(!this.split){
35796             
35797             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35798                             tag: "div",
35799                             id: this.el.id + "-split",
35800                             cls: "roo-layout-split roo-layout-split-"+this.position,
35801                             html: "&#160;"
35802             });
35803             /** The SplitBar for this region 
35804             * @type Roo.SplitBar */
35805             // does not exist yet...
35806             Roo.log([this.position, this.orientation]);
35807             
35808             this.split = new Roo.bootstrap.SplitBar({
35809                 dragElement : splitEl,
35810                 resizingElement: this.el,
35811                 orientation : this.orientation
35812             });
35813             
35814             this.split.on("moved", this.onSplitMove, this);
35815             this.split.useShim = this.config.useShim === true;
35816             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35817             if(this.useSplitTips){
35818                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35819             }
35820             //if(config.collapsible){
35821             //    this.split.el.on("dblclick", this.collapse,  this);
35822             //}
35823         }
35824         if(typeof this.config.minSize != "undefined"){
35825             this.split.minSize = this.config.minSize;
35826         }
35827         if(typeof this.config.maxSize != "undefined"){
35828             this.split.maxSize = this.config.maxSize;
35829         }
35830         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35831             this.hideSplitter();
35832         }
35833         
35834     },
35835
35836     getHMaxSize : function(){
35837          var cmax = this.config.maxSize || 10000;
35838          var center = this.mgr.getRegion("center");
35839          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35840     },
35841
35842     getVMaxSize : function(){
35843          var cmax = this.config.maxSize || 10000;
35844          var center = this.mgr.getRegion("center");
35845          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35846     },
35847
35848     onSplitMove : function(split, newSize){
35849         this.fireEvent("resized", this, newSize);
35850     },
35851     
35852     /** 
35853      * Returns the {@link Roo.SplitBar} for this region.
35854      * @return {Roo.SplitBar}
35855      */
35856     getSplitBar : function(){
35857         return this.split;
35858     },
35859     
35860     hide : function(){
35861         this.hideSplitter();
35862         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35863     },
35864
35865     hideSplitter : function(){
35866         if(this.split){
35867             this.split.el.setLocation(-2000,-2000);
35868             this.split.el.hide();
35869         }
35870     },
35871
35872     show : function(){
35873         if(this.split){
35874             this.split.el.show();
35875         }
35876         Roo.bootstrap.layout.Split.superclass.show.call(this);
35877     },
35878     
35879     beforeSlide: function(){
35880         if(Roo.isGecko){// firefox overflow auto bug workaround
35881             this.bodyEl.clip();
35882             if(this.tabs) {
35883                 this.tabs.bodyEl.clip();
35884             }
35885             if(this.activePanel){
35886                 this.activePanel.getEl().clip();
35887                 
35888                 if(this.activePanel.beforeSlide){
35889                     this.activePanel.beforeSlide();
35890                 }
35891             }
35892         }
35893     },
35894     
35895     afterSlide : function(){
35896         if(Roo.isGecko){// firefox overflow auto bug workaround
35897             this.bodyEl.unclip();
35898             if(this.tabs) {
35899                 this.tabs.bodyEl.unclip();
35900             }
35901             if(this.activePanel){
35902                 this.activePanel.getEl().unclip();
35903                 if(this.activePanel.afterSlide){
35904                     this.activePanel.afterSlide();
35905                 }
35906             }
35907         }
35908     },
35909
35910     initAutoHide : function(){
35911         if(this.autoHide !== false){
35912             if(!this.autoHideHd){
35913                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35914                 this.autoHideHd = {
35915                     "mouseout": function(e){
35916                         if(!e.within(this.el, true)){
35917                             st.delay(500);
35918                         }
35919                     },
35920                     "mouseover" : function(e){
35921                         st.cancel();
35922                     },
35923                     scope : this
35924                 };
35925             }
35926             this.el.on(this.autoHideHd);
35927         }
35928     },
35929
35930     clearAutoHide : function(){
35931         if(this.autoHide !== false){
35932             this.el.un("mouseout", this.autoHideHd.mouseout);
35933             this.el.un("mouseover", this.autoHideHd.mouseover);
35934         }
35935     },
35936
35937     clearMonitor : function(){
35938         Roo.get(document).un("click", this.slideInIf, this);
35939     },
35940
35941     // these names are backwards but not changed for compat
35942     slideOut : function(){
35943         if(this.isSlid || this.el.hasActiveFx()){
35944             return;
35945         }
35946         this.isSlid = true;
35947         if(this.collapseBtn){
35948             this.collapseBtn.hide();
35949         }
35950         this.closeBtnState = this.closeBtn.getStyle('display');
35951         this.closeBtn.hide();
35952         if(this.stickBtn){
35953             this.stickBtn.show();
35954         }
35955         this.el.show();
35956         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35957         this.beforeSlide();
35958         this.el.setStyle("z-index", 10001);
35959         this.el.slideIn(this.getSlideAnchor(), {
35960             callback: function(){
35961                 this.afterSlide();
35962                 this.initAutoHide();
35963                 Roo.get(document).on("click", this.slideInIf, this);
35964                 this.fireEvent("slideshow", this);
35965             },
35966             scope: this,
35967             block: true
35968         });
35969     },
35970
35971     afterSlideIn : function(){
35972         this.clearAutoHide();
35973         this.isSlid = false;
35974         this.clearMonitor();
35975         this.el.setStyle("z-index", "");
35976         if(this.collapseBtn){
35977             this.collapseBtn.show();
35978         }
35979         this.closeBtn.setStyle('display', this.closeBtnState);
35980         if(this.stickBtn){
35981             this.stickBtn.hide();
35982         }
35983         this.fireEvent("slidehide", this);
35984     },
35985
35986     slideIn : function(cb){
35987         if(!this.isSlid || this.el.hasActiveFx()){
35988             Roo.callback(cb);
35989             return;
35990         }
35991         this.isSlid = false;
35992         this.beforeSlide();
35993         this.el.slideOut(this.getSlideAnchor(), {
35994             callback: function(){
35995                 this.el.setLeftTop(-10000, -10000);
35996                 this.afterSlide();
35997                 this.afterSlideIn();
35998                 Roo.callback(cb);
35999             },
36000             scope: this,
36001             block: true
36002         });
36003     },
36004     
36005     slideInIf : function(e){
36006         if(!e.within(this.el)){
36007             this.slideIn();
36008         }
36009     },
36010
36011     animateCollapse : function(){
36012         this.beforeSlide();
36013         this.el.setStyle("z-index", 20000);
36014         var anchor = this.getSlideAnchor();
36015         this.el.slideOut(anchor, {
36016             callback : function(){
36017                 this.el.setStyle("z-index", "");
36018                 this.collapsedEl.slideIn(anchor, {duration:.3});
36019                 this.afterSlide();
36020                 this.el.setLocation(-10000,-10000);
36021                 this.el.hide();
36022                 this.fireEvent("collapsed", this);
36023             },
36024             scope: this,
36025             block: true
36026         });
36027     },
36028
36029     animateExpand : function(){
36030         this.beforeSlide();
36031         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36032         this.el.setStyle("z-index", 20000);
36033         this.collapsedEl.hide({
36034             duration:.1
36035         });
36036         this.el.slideIn(this.getSlideAnchor(), {
36037             callback : function(){
36038                 this.el.setStyle("z-index", "");
36039                 this.afterSlide();
36040                 if(this.split){
36041                     this.split.el.show();
36042                 }
36043                 this.fireEvent("invalidated", this);
36044                 this.fireEvent("expanded", this);
36045             },
36046             scope: this,
36047             block: true
36048         });
36049     },
36050
36051     anchors : {
36052         "west" : "left",
36053         "east" : "right",
36054         "north" : "top",
36055         "south" : "bottom"
36056     },
36057
36058     sanchors : {
36059         "west" : "l",
36060         "east" : "r",
36061         "north" : "t",
36062         "south" : "b"
36063     },
36064
36065     canchors : {
36066         "west" : "tl-tr",
36067         "east" : "tr-tl",
36068         "north" : "tl-bl",
36069         "south" : "bl-tl"
36070     },
36071
36072     getAnchor : function(){
36073         return this.anchors[this.position];
36074     },
36075
36076     getCollapseAnchor : function(){
36077         return this.canchors[this.position];
36078     },
36079
36080     getSlideAnchor : function(){
36081         return this.sanchors[this.position];
36082     },
36083
36084     getAlignAdj : function(){
36085         var cm = this.cmargins;
36086         switch(this.position){
36087             case "west":
36088                 return [0, 0];
36089             break;
36090             case "east":
36091                 return [0, 0];
36092             break;
36093             case "north":
36094                 return [0, 0];
36095             break;
36096             case "south":
36097                 return [0, 0];
36098             break;
36099         }
36100     },
36101
36102     getExpandAdj : function(){
36103         var c = this.collapsedEl, cm = this.cmargins;
36104         switch(this.position){
36105             case "west":
36106                 return [-(cm.right+c.getWidth()+cm.left), 0];
36107             break;
36108             case "east":
36109                 return [cm.right+c.getWidth()+cm.left, 0];
36110             break;
36111             case "north":
36112                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36113             break;
36114             case "south":
36115                 return [0, cm.top+cm.bottom+c.getHeight()];
36116             break;
36117         }
36118     }
36119 });/*
36120  * Based on:
36121  * Ext JS Library 1.1.1
36122  * Copyright(c) 2006-2007, Ext JS, LLC.
36123  *
36124  * Originally Released Under LGPL - original licence link has changed is not relivant.
36125  *
36126  * Fork - LGPL
36127  * <script type="text/javascript">
36128  */
36129 /*
36130  * These classes are private internal classes
36131  */
36132 Roo.bootstrap.layout.Center = function(config){
36133     config.region = "center";
36134     Roo.bootstrap.layout.Region.call(this, config);
36135     this.visible = true;
36136     this.minWidth = config.minWidth || 20;
36137     this.minHeight = config.minHeight || 20;
36138 };
36139
36140 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36141     hide : function(){
36142         // center panel can't be hidden
36143     },
36144     
36145     show : function(){
36146         // center panel can't be hidden
36147     },
36148     
36149     getMinWidth: function(){
36150         return this.minWidth;
36151     },
36152     
36153     getMinHeight: function(){
36154         return this.minHeight;
36155     }
36156 });
36157
36158
36159
36160
36161  
36162
36163
36164
36165
36166
36167 Roo.bootstrap.layout.North = function(config)
36168 {
36169     config.region = 'north';
36170     config.cursor = 'n-resize';
36171     
36172     Roo.bootstrap.layout.Split.call(this, config);
36173     
36174     
36175     if(this.split){
36176         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36177         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36178         this.split.el.addClass("roo-layout-split-v");
36179     }
36180     var size = config.initialSize || config.height;
36181     if(typeof size != "undefined"){
36182         this.el.setHeight(size);
36183     }
36184 };
36185 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36186 {
36187     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36188     
36189     
36190     
36191     getBox : function(){
36192         if(this.collapsed){
36193             return this.collapsedEl.getBox();
36194         }
36195         var box = this.el.getBox();
36196         if(this.split){
36197             box.height += this.split.el.getHeight();
36198         }
36199         return box;
36200     },
36201     
36202     updateBox : function(box){
36203         if(this.split && !this.collapsed){
36204             box.height -= this.split.el.getHeight();
36205             this.split.el.setLeft(box.x);
36206             this.split.el.setTop(box.y+box.height);
36207             this.split.el.setWidth(box.width);
36208         }
36209         if(this.collapsed){
36210             this.updateBody(box.width, null);
36211         }
36212         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36213     }
36214 });
36215
36216
36217
36218
36219
36220 Roo.bootstrap.layout.South = function(config){
36221     config.region = 'south';
36222     config.cursor = 's-resize';
36223     Roo.bootstrap.layout.Split.call(this, config);
36224     if(this.split){
36225         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36226         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36227         this.split.el.addClass("roo-layout-split-v");
36228     }
36229     var size = config.initialSize || config.height;
36230     if(typeof size != "undefined"){
36231         this.el.setHeight(size);
36232     }
36233 };
36234
36235 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36236     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36237     getBox : function(){
36238         if(this.collapsed){
36239             return this.collapsedEl.getBox();
36240         }
36241         var box = this.el.getBox();
36242         if(this.split){
36243             var sh = this.split.el.getHeight();
36244             box.height += sh;
36245             box.y -= sh;
36246         }
36247         return box;
36248     },
36249     
36250     updateBox : function(box){
36251         if(this.split && !this.collapsed){
36252             var sh = this.split.el.getHeight();
36253             box.height -= sh;
36254             box.y += sh;
36255             this.split.el.setLeft(box.x);
36256             this.split.el.setTop(box.y-sh);
36257             this.split.el.setWidth(box.width);
36258         }
36259         if(this.collapsed){
36260             this.updateBody(box.width, null);
36261         }
36262         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36263     }
36264 });
36265
36266 Roo.bootstrap.layout.East = function(config){
36267     config.region = "east";
36268     config.cursor = "e-resize";
36269     Roo.bootstrap.layout.Split.call(this, config);
36270     if(this.split){
36271         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36272         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36273         this.split.el.addClass("roo-layout-split-h");
36274     }
36275     var size = config.initialSize || config.width;
36276     if(typeof size != "undefined"){
36277         this.el.setWidth(size);
36278     }
36279 };
36280 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36281     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36282     getBox : function(){
36283         if(this.collapsed){
36284             return this.collapsedEl.getBox();
36285         }
36286         var box = this.el.getBox();
36287         if(this.split){
36288             var sw = this.split.el.getWidth();
36289             box.width += sw;
36290             box.x -= sw;
36291         }
36292         return box;
36293     },
36294
36295     updateBox : function(box){
36296         if(this.split && !this.collapsed){
36297             var sw = this.split.el.getWidth();
36298             box.width -= sw;
36299             this.split.el.setLeft(box.x);
36300             this.split.el.setTop(box.y);
36301             this.split.el.setHeight(box.height);
36302             box.x += sw;
36303         }
36304         if(this.collapsed){
36305             this.updateBody(null, box.height);
36306         }
36307         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36308     }
36309 });
36310
36311 Roo.bootstrap.layout.West = function(config){
36312     config.region = "west";
36313     config.cursor = "w-resize";
36314     
36315     Roo.bootstrap.layout.Split.call(this, config);
36316     if(this.split){
36317         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36318         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36319         this.split.el.addClass("roo-layout-split-h");
36320     }
36321     
36322 };
36323 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36324     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36325     
36326     onRender: function(ctr, pos)
36327     {
36328         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36329         var size = this.config.initialSize || this.config.width;
36330         if(typeof size != "undefined"){
36331             this.el.setWidth(size);
36332         }
36333     },
36334     
36335     getBox : function(){
36336         if(this.collapsed){
36337             return this.collapsedEl.getBox();
36338         }
36339         var box = this.el.getBox();
36340         if(this.split){
36341             box.width += this.split.el.getWidth();
36342         }
36343         return box;
36344     },
36345     
36346     updateBox : function(box){
36347         if(this.split && !this.collapsed){
36348             var sw = this.split.el.getWidth();
36349             box.width -= sw;
36350             this.split.el.setLeft(box.x+box.width);
36351             this.split.el.setTop(box.y);
36352             this.split.el.setHeight(box.height);
36353         }
36354         if(this.collapsed){
36355             this.updateBody(null, box.height);
36356         }
36357         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36358     }
36359 });
36360 Roo.namespace("Roo.bootstrap.panel");/*
36361  * Based on:
36362  * Ext JS Library 1.1.1
36363  * Copyright(c) 2006-2007, Ext JS, LLC.
36364  *
36365  * Originally Released Under LGPL - original licence link has changed is not relivant.
36366  *
36367  * Fork - LGPL
36368  * <script type="text/javascript">
36369  */
36370 /**
36371  * @class Roo.ContentPanel
36372  * @extends Roo.util.Observable
36373  * A basic ContentPanel element.
36374  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36375  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36376  * @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
36377  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36378  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36379  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36380  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36381  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36382  * @cfg {String} title          The title for this panel
36383  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36384  * @cfg {String} url            Calls {@link #setUrl} with this value
36385  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36386  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36387  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36388  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36389  * @cfg {Boolean} badges render the badges
36390
36391  * @constructor
36392  * Create a new ContentPanel.
36393  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36394  * @param {String/Object} config A string to set only the title or a config object
36395  * @param {String} content (optional) Set the HTML content for this panel
36396  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36397  */
36398 Roo.bootstrap.panel.Content = function( config){
36399     
36400     this.tpl = config.tpl || false;
36401     
36402     var el = config.el;
36403     var content = config.content;
36404
36405     if(config.autoCreate){ // xtype is available if this is called from factory
36406         el = Roo.id();
36407     }
36408     this.el = Roo.get(el);
36409     if(!this.el && config && config.autoCreate){
36410         if(typeof config.autoCreate == "object"){
36411             if(!config.autoCreate.id){
36412                 config.autoCreate.id = config.id||el;
36413             }
36414             this.el = Roo.DomHelper.append(document.body,
36415                         config.autoCreate, true);
36416         }else{
36417             var elcfg =  {   tag: "div",
36418                             cls: "roo-layout-inactive-content",
36419                             id: config.id||el
36420                             };
36421             if (config.html) {
36422                 elcfg.html = config.html;
36423                 
36424             }
36425                         
36426             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36427         }
36428     } 
36429     this.closable = false;
36430     this.loaded = false;
36431     this.active = false;
36432    
36433       
36434     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36435         
36436         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36437         
36438         this.wrapEl = this.el; //this.el.wrap();
36439         var ti = [];
36440         if (config.toolbar.items) {
36441             ti = config.toolbar.items ;
36442             delete config.toolbar.items ;
36443         }
36444         
36445         var nitems = [];
36446         this.toolbar.render(this.wrapEl, 'before');
36447         for(var i =0;i < ti.length;i++) {
36448           //  Roo.log(['add child', items[i]]);
36449             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36450         }
36451         this.toolbar.items = nitems;
36452         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36453         delete config.toolbar;
36454         
36455     }
36456     /*
36457     // xtype created footer. - not sure if will work as we normally have to render first..
36458     if (this.footer && !this.footer.el && this.footer.xtype) {
36459         if (!this.wrapEl) {
36460             this.wrapEl = this.el.wrap();
36461         }
36462     
36463         this.footer.container = this.wrapEl.createChild();
36464          
36465         this.footer = Roo.factory(this.footer, Roo);
36466         
36467     }
36468     */
36469     
36470      if(typeof config == "string"){
36471         this.title = config;
36472     }else{
36473         Roo.apply(this, config);
36474     }
36475     
36476     if(this.resizeEl){
36477         this.resizeEl = Roo.get(this.resizeEl, true);
36478     }else{
36479         this.resizeEl = this.el;
36480     }
36481     // handle view.xtype
36482     
36483  
36484     
36485     
36486     this.addEvents({
36487         /**
36488          * @event activate
36489          * Fires when this panel is activated. 
36490          * @param {Roo.ContentPanel} this
36491          */
36492         "activate" : true,
36493         /**
36494          * @event deactivate
36495          * Fires when this panel is activated. 
36496          * @param {Roo.ContentPanel} this
36497          */
36498         "deactivate" : true,
36499
36500         /**
36501          * @event resize
36502          * Fires when this panel is resized if fitToFrame is true.
36503          * @param {Roo.ContentPanel} this
36504          * @param {Number} width The width after any component adjustments
36505          * @param {Number} height The height after any component adjustments
36506          */
36507         "resize" : true,
36508         
36509          /**
36510          * @event render
36511          * Fires when this tab is created
36512          * @param {Roo.ContentPanel} this
36513          */
36514         "render" : true
36515         
36516         
36517         
36518     });
36519     
36520
36521     
36522     
36523     if(this.autoScroll){
36524         this.resizeEl.setStyle("overflow", "auto");
36525     } else {
36526         // fix randome scrolling
36527         //this.el.on('scroll', function() {
36528         //    Roo.log('fix random scolling');
36529         //    this.scrollTo('top',0); 
36530         //});
36531     }
36532     content = content || this.content;
36533     if(content){
36534         this.setContent(content);
36535     }
36536     if(config && config.url){
36537         this.setUrl(this.url, this.params, this.loadOnce);
36538     }
36539     
36540     
36541     
36542     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36543     
36544     if (this.view && typeof(this.view.xtype) != 'undefined') {
36545         this.view.el = this.el.appendChild(document.createElement("div"));
36546         this.view = Roo.factory(this.view); 
36547         this.view.render  &&  this.view.render(false, '');  
36548     }
36549     
36550     
36551     this.fireEvent('render', this);
36552 };
36553
36554 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36555     
36556     tabTip : '',
36557     
36558     setRegion : function(region){
36559         this.region = region;
36560         this.setActiveClass(region && !this.background);
36561     },
36562     
36563     
36564     setActiveClass: function(state)
36565     {
36566         if(state){
36567            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36568            this.el.setStyle('position','relative');
36569         }else{
36570            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36571            this.el.setStyle('position', 'absolute');
36572         } 
36573     },
36574     
36575     /**
36576      * Returns the toolbar for this Panel if one was configured. 
36577      * @return {Roo.Toolbar} 
36578      */
36579     getToolbar : function(){
36580         return this.toolbar;
36581     },
36582     
36583     setActiveState : function(active)
36584     {
36585         this.active = active;
36586         this.setActiveClass(active);
36587         if(!active){
36588             if(this.fireEvent("deactivate", this) === false){
36589                 return false;
36590             }
36591             return true;
36592         }
36593         this.fireEvent("activate", this);
36594         return true;
36595     },
36596     /**
36597      * Updates this panel's element
36598      * @param {String} content The new content
36599      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36600     */
36601     setContent : function(content, loadScripts){
36602         this.el.update(content, loadScripts);
36603     },
36604
36605     ignoreResize : function(w, h){
36606         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36607             return true;
36608         }else{
36609             this.lastSize = {width: w, height: h};
36610             return false;
36611         }
36612     },
36613     /**
36614      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36615      * @return {Roo.UpdateManager} The UpdateManager
36616      */
36617     getUpdateManager : function(){
36618         return this.el.getUpdateManager();
36619     },
36620      /**
36621      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36622      * @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:
36623 <pre><code>
36624 panel.load({
36625     url: "your-url.php",
36626     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36627     callback: yourFunction,
36628     scope: yourObject, //(optional scope)
36629     discardUrl: false,
36630     nocache: false,
36631     text: "Loading...",
36632     timeout: 30,
36633     scripts: false
36634 });
36635 </code></pre>
36636      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36637      * 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.
36638      * @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}
36639      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36640      * @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.
36641      * @return {Roo.ContentPanel} this
36642      */
36643     load : function(){
36644         var um = this.el.getUpdateManager();
36645         um.update.apply(um, arguments);
36646         return this;
36647     },
36648
36649
36650     /**
36651      * 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.
36652      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36653      * @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)
36654      * @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)
36655      * @return {Roo.UpdateManager} The UpdateManager
36656      */
36657     setUrl : function(url, params, loadOnce){
36658         if(this.refreshDelegate){
36659             this.removeListener("activate", this.refreshDelegate);
36660         }
36661         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36662         this.on("activate", this.refreshDelegate);
36663         return this.el.getUpdateManager();
36664     },
36665     
36666     _handleRefresh : function(url, params, loadOnce){
36667         if(!loadOnce || !this.loaded){
36668             var updater = this.el.getUpdateManager();
36669             updater.update(url, params, this._setLoaded.createDelegate(this));
36670         }
36671     },
36672     
36673     _setLoaded : function(){
36674         this.loaded = true;
36675     }, 
36676     
36677     /**
36678      * Returns this panel's id
36679      * @return {String} 
36680      */
36681     getId : function(){
36682         return this.el.id;
36683     },
36684     
36685     /** 
36686      * Returns this panel's element - used by regiosn to add.
36687      * @return {Roo.Element} 
36688      */
36689     getEl : function(){
36690         return this.wrapEl || this.el;
36691     },
36692     
36693    
36694     
36695     adjustForComponents : function(width, height)
36696     {
36697         //Roo.log('adjustForComponents ');
36698         if(this.resizeEl != this.el){
36699             width -= this.el.getFrameWidth('lr');
36700             height -= this.el.getFrameWidth('tb');
36701         }
36702         if(this.toolbar){
36703             var te = this.toolbar.getEl();
36704             te.setWidth(width);
36705             height -= te.getHeight();
36706         }
36707         if(this.footer){
36708             var te = this.footer.getEl();
36709             te.setWidth(width);
36710             height -= te.getHeight();
36711         }
36712         
36713         
36714         if(this.adjustments){
36715             width += this.adjustments[0];
36716             height += this.adjustments[1];
36717         }
36718         return {"width": width, "height": height};
36719     },
36720     
36721     setSize : function(width, height){
36722         if(this.fitToFrame && !this.ignoreResize(width, height)){
36723             if(this.fitContainer && this.resizeEl != this.el){
36724                 this.el.setSize(width, height);
36725             }
36726             var size = this.adjustForComponents(width, height);
36727             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36728             this.fireEvent('resize', this, size.width, size.height);
36729         }
36730     },
36731     
36732     /**
36733      * Returns this panel's title
36734      * @return {String} 
36735      */
36736     getTitle : function(){
36737         
36738         if (typeof(this.title) != 'object') {
36739             return this.title;
36740         }
36741         
36742         var t = '';
36743         for (var k in this.title) {
36744             if (!this.title.hasOwnProperty(k)) {
36745                 continue;
36746             }
36747             
36748             if (k.indexOf('-') >= 0) {
36749                 var s = k.split('-');
36750                 for (var i = 0; i<s.length; i++) {
36751                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36752                 }
36753             } else {
36754                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36755             }
36756         }
36757         return t;
36758     },
36759     
36760     /**
36761      * Set this panel's title
36762      * @param {String} title
36763      */
36764     setTitle : function(title){
36765         this.title = title;
36766         if(this.region){
36767             this.region.updatePanelTitle(this, title);
36768         }
36769     },
36770     
36771     /**
36772      * Returns true is this panel was configured to be closable
36773      * @return {Boolean} 
36774      */
36775     isClosable : function(){
36776         return this.closable;
36777     },
36778     
36779     beforeSlide : function(){
36780         this.el.clip();
36781         this.resizeEl.clip();
36782     },
36783     
36784     afterSlide : function(){
36785         this.el.unclip();
36786         this.resizeEl.unclip();
36787     },
36788     
36789     /**
36790      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36791      *   Will fail silently if the {@link #setUrl} method has not been called.
36792      *   This does not activate the panel, just updates its content.
36793      */
36794     refresh : function(){
36795         if(this.refreshDelegate){
36796            this.loaded = false;
36797            this.refreshDelegate();
36798         }
36799     },
36800     
36801     /**
36802      * Destroys this panel
36803      */
36804     destroy : function(){
36805         this.el.removeAllListeners();
36806         var tempEl = document.createElement("span");
36807         tempEl.appendChild(this.el.dom);
36808         tempEl.innerHTML = "";
36809         this.el.remove();
36810         this.el = null;
36811     },
36812     
36813     /**
36814      * form - if the content panel contains a form - this is a reference to it.
36815      * @type {Roo.form.Form}
36816      */
36817     form : false,
36818     /**
36819      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36820      *    This contains a reference to it.
36821      * @type {Roo.View}
36822      */
36823     view : false,
36824     
36825       /**
36826      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36827      * <pre><code>
36828
36829 layout.addxtype({
36830        xtype : 'Form',
36831        items: [ .... ]
36832    }
36833 );
36834
36835 </code></pre>
36836      * @param {Object} cfg Xtype definition of item to add.
36837      */
36838     
36839     
36840     getChildContainer: function () {
36841         return this.getEl();
36842     }
36843     
36844     
36845     /*
36846         var  ret = new Roo.factory(cfg);
36847         return ret;
36848         
36849         
36850         // add form..
36851         if (cfg.xtype.match(/^Form$/)) {
36852             
36853             var el;
36854             //if (this.footer) {
36855             //    el = this.footer.container.insertSibling(false, 'before');
36856             //} else {
36857                 el = this.el.createChild();
36858             //}
36859
36860             this.form = new  Roo.form.Form(cfg);
36861             
36862             
36863             if ( this.form.allItems.length) {
36864                 this.form.render(el.dom);
36865             }
36866             return this.form;
36867         }
36868         // should only have one of theses..
36869         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36870             // views.. should not be just added - used named prop 'view''
36871             
36872             cfg.el = this.el.appendChild(document.createElement("div"));
36873             // factory?
36874             
36875             var ret = new Roo.factory(cfg);
36876              
36877              ret.render && ret.render(false, ''); // render blank..
36878             this.view = ret;
36879             return ret;
36880         }
36881         return false;
36882     }
36883     \*/
36884 });
36885  
36886 /**
36887  * @class Roo.bootstrap.panel.Grid
36888  * @extends Roo.bootstrap.panel.Content
36889  * @constructor
36890  * Create a new GridPanel.
36891  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36892  * @param {Object} config A the config object
36893   
36894  */
36895
36896
36897
36898 Roo.bootstrap.panel.Grid = function(config)
36899 {
36900     
36901       
36902     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36903         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36904
36905     config.el = this.wrapper;
36906     //this.el = this.wrapper;
36907     
36908       if (config.container) {
36909         // ctor'ed from a Border/panel.grid
36910         
36911         
36912         this.wrapper.setStyle("overflow", "hidden");
36913         this.wrapper.addClass('roo-grid-container');
36914
36915     }
36916     
36917     
36918     if(config.toolbar){
36919         var tool_el = this.wrapper.createChild();    
36920         this.toolbar = Roo.factory(config.toolbar);
36921         var ti = [];
36922         if (config.toolbar.items) {
36923             ti = config.toolbar.items ;
36924             delete config.toolbar.items ;
36925         }
36926         
36927         var nitems = [];
36928         this.toolbar.render(tool_el);
36929         for(var i =0;i < ti.length;i++) {
36930           //  Roo.log(['add child', items[i]]);
36931             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36932         }
36933         this.toolbar.items = nitems;
36934         
36935         delete config.toolbar;
36936     }
36937     
36938     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36939     config.grid.scrollBody = true;;
36940     config.grid.monitorWindowResize = false; // turn off autosizing
36941     config.grid.autoHeight = false;
36942     config.grid.autoWidth = false;
36943     
36944     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36945     
36946     if (config.background) {
36947         // render grid on panel activation (if panel background)
36948         this.on('activate', function(gp) {
36949             if (!gp.grid.rendered) {
36950                 gp.grid.render(this.wrapper);
36951                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36952             }
36953         });
36954             
36955     } else {
36956         this.grid.render(this.wrapper);
36957         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36958
36959     }
36960     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36961     // ??? needed ??? config.el = this.wrapper;
36962     
36963     
36964     
36965   
36966     // xtype created footer. - not sure if will work as we normally have to render first..
36967     if (this.footer && !this.footer.el && this.footer.xtype) {
36968         
36969         var ctr = this.grid.getView().getFooterPanel(true);
36970         this.footer.dataSource = this.grid.dataSource;
36971         this.footer = Roo.factory(this.footer, Roo);
36972         this.footer.render(ctr);
36973         
36974     }
36975     
36976     
36977     
36978     
36979      
36980 };
36981
36982 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36983     getId : function(){
36984         return this.grid.id;
36985     },
36986     
36987     /**
36988      * Returns the grid for this panel
36989      * @return {Roo.bootstrap.Table} 
36990      */
36991     getGrid : function(){
36992         return this.grid;    
36993     },
36994     
36995     setSize : function(width, height){
36996         if(!this.ignoreResize(width, height)){
36997             var grid = this.grid;
36998             var size = this.adjustForComponents(width, height);
36999             var gridel = grid.getGridEl();
37000             gridel.setSize(size.width, size.height);
37001             /*
37002             var thd = grid.getGridEl().select('thead',true).first();
37003             var tbd = grid.getGridEl().select('tbody', true).first();
37004             if (tbd) {
37005                 tbd.setSize(width, height - thd.getHeight());
37006             }
37007             */
37008             grid.autoSize();
37009         }
37010     },
37011      
37012     
37013     
37014     beforeSlide : function(){
37015         this.grid.getView().scroller.clip();
37016     },
37017     
37018     afterSlide : function(){
37019         this.grid.getView().scroller.unclip();
37020     },
37021     
37022     destroy : function(){
37023         this.grid.destroy();
37024         delete this.grid;
37025         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37026     }
37027 });
37028
37029 /**
37030  * @class Roo.bootstrap.panel.Nest
37031  * @extends Roo.bootstrap.panel.Content
37032  * @constructor
37033  * Create a new Panel, that can contain a layout.Border.
37034  * 
37035  * 
37036  * @param {Roo.BorderLayout} layout The layout for this panel
37037  * @param {String/Object} config A string to set only the title or a config object
37038  */
37039 Roo.bootstrap.panel.Nest = function(config)
37040 {
37041     // construct with only one argument..
37042     /* FIXME - implement nicer consturctors
37043     if (layout.layout) {
37044         config = layout;
37045         layout = config.layout;
37046         delete config.layout;
37047     }
37048     if (layout.xtype && !layout.getEl) {
37049         // then layout needs constructing..
37050         layout = Roo.factory(layout, Roo);
37051     }
37052     */
37053     
37054     config.el =  config.layout.getEl();
37055     
37056     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37057     
37058     config.layout.monitorWindowResize = false; // turn off autosizing
37059     this.layout = config.layout;
37060     this.layout.getEl().addClass("roo-layout-nested-layout");
37061     
37062     
37063     
37064     
37065 };
37066
37067 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37068
37069     setSize : function(width, height){
37070         if(!this.ignoreResize(width, height)){
37071             var size = this.adjustForComponents(width, height);
37072             var el = this.layout.getEl();
37073             if (size.height < 1) {
37074                 el.setWidth(size.width);   
37075             } else {
37076                 el.setSize(size.width, size.height);
37077             }
37078             var touch = el.dom.offsetWidth;
37079             this.layout.layout();
37080             // ie requires a double layout on the first pass
37081             if(Roo.isIE && !this.initialized){
37082                 this.initialized = true;
37083                 this.layout.layout();
37084             }
37085         }
37086     },
37087     
37088     // activate all subpanels if not currently active..
37089     
37090     setActiveState : function(active){
37091         this.active = active;
37092         this.setActiveClass(active);
37093         
37094         if(!active){
37095             this.fireEvent("deactivate", this);
37096             return;
37097         }
37098         
37099         this.fireEvent("activate", this);
37100         // not sure if this should happen before or after..
37101         if (!this.layout) {
37102             return; // should not happen..
37103         }
37104         var reg = false;
37105         for (var r in this.layout.regions) {
37106             reg = this.layout.getRegion(r);
37107             if (reg.getActivePanel()) {
37108                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37109                 reg.setActivePanel(reg.getActivePanel());
37110                 continue;
37111             }
37112             if (!reg.panels.length) {
37113                 continue;
37114             }
37115             reg.showPanel(reg.getPanel(0));
37116         }
37117         
37118         
37119         
37120         
37121     },
37122     
37123     /**
37124      * Returns the nested BorderLayout for this panel
37125      * @return {Roo.BorderLayout} 
37126      */
37127     getLayout : function(){
37128         return this.layout;
37129     },
37130     
37131      /**
37132      * Adds a xtype elements to the layout of the nested panel
37133      * <pre><code>
37134
37135 panel.addxtype({
37136        xtype : 'ContentPanel',
37137        region: 'west',
37138        items: [ .... ]
37139    }
37140 );
37141
37142 panel.addxtype({
37143         xtype : 'NestedLayoutPanel',
37144         region: 'west',
37145         layout: {
37146            center: { },
37147            west: { }   
37148         },
37149         items : [ ... list of content panels or nested layout panels.. ]
37150    }
37151 );
37152 </code></pre>
37153      * @param {Object} cfg Xtype definition of item to add.
37154      */
37155     addxtype : function(cfg) {
37156         return this.layout.addxtype(cfg);
37157     
37158     }
37159 });        /*
37160  * Based on:
37161  * Ext JS Library 1.1.1
37162  * Copyright(c) 2006-2007, Ext JS, LLC.
37163  *
37164  * Originally Released Under LGPL - original licence link has changed is not relivant.
37165  *
37166  * Fork - LGPL
37167  * <script type="text/javascript">
37168  */
37169 /**
37170  * @class Roo.TabPanel
37171  * @extends Roo.util.Observable
37172  * A lightweight tab container.
37173  * <br><br>
37174  * Usage:
37175  * <pre><code>
37176 // basic tabs 1, built from existing content
37177 var tabs = new Roo.TabPanel("tabs1");
37178 tabs.addTab("script", "View Script");
37179 tabs.addTab("markup", "View Markup");
37180 tabs.activate("script");
37181
37182 // more advanced tabs, built from javascript
37183 var jtabs = new Roo.TabPanel("jtabs");
37184 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37185
37186 // set up the UpdateManager
37187 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37188 var updater = tab2.getUpdateManager();
37189 updater.setDefaultUrl("ajax1.htm");
37190 tab2.on('activate', updater.refresh, updater, true);
37191
37192 // Use setUrl for Ajax loading
37193 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37194 tab3.setUrl("ajax2.htm", null, true);
37195
37196 // Disabled tab
37197 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37198 tab4.disable();
37199
37200 jtabs.activate("jtabs-1");
37201  * </code></pre>
37202  * @constructor
37203  * Create a new TabPanel.
37204  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37205  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37206  */
37207 Roo.bootstrap.panel.Tabs = function(config){
37208     /**
37209     * The container element for this TabPanel.
37210     * @type Roo.Element
37211     */
37212     this.el = Roo.get(config.el);
37213     delete config.el;
37214     if(config){
37215         if(typeof config == "boolean"){
37216             this.tabPosition = config ? "bottom" : "top";
37217         }else{
37218             Roo.apply(this, config);
37219         }
37220     }
37221     
37222     if(this.tabPosition == "bottom"){
37223         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37224         this.el.addClass("roo-tabs-bottom");
37225     }
37226     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37227     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37228     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37229     if(Roo.isIE){
37230         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37231     }
37232     if(this.tabPosition != "bottom"){
37233         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37234          * @type Roo.Element
37235          */
37236         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37237         this.el.addClass("roo-tabs-top");
37238     }
37239     this.items = [];
37240
37241     this.bodyEl.setStyle("position", "relative");
37242
37243     this.active = null;
37244     this.activateDelegate = this.activate.createDelegate(this);
37245
37246     this.addEvents({
37247         /**
37248          * @event tabchange
37249          * Fires when the active tab changes
37250          * @param {Roo.TabPanel} this
37251          * @param {Roo.TabPanelItem} activePanel The new active tab
37252          */
37253         "tabchange": true,
37254         /**
37255          * @event beforetabchange
37256          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37257          * @param {Roo.TabPanel} this
37258          * @param {Object} e Set cancel to true on this object to cancel the tab change
37259          * @param {Roo.TabPanelItem} tab The tab being changed to
37260          */
37261         "beforetabchange" : true
37262     });
37263
37264     Roo.EventManager.onWindowResize(this.onResize, this);
37265     this.cpad = this.el.getPadding("lr");
37266     this.hiddenCount = 0;
37267
37268
37269     // toolbar on the tabbar support...
37270     if (this.toolbar) {
37271         alert("no toolbar support yet");
37272         this.toolbar  = false;
37273         /*
37274         var tcfg = this.toolbar;
37275         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37276         this.toolbar = new Roo.Toolbar(tcfg);
37277         if (Roo.isSafari) {
37278             var tbl = tcfg.container.child('table', true);
37279             tbl.setAttribute('width', '100%');
37280         }
37281         */
37282         
37283     }
37284    
37285
37286
37287     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37288 };
37289
37290 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37291     /*
37292      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37293      */
37294     tabPosition : "top",
37295     /*
37296      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37297      */
37298     currentTabWidth : 0,
37299     /*
37300      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37301      */
37302     minTabWidth : 40,
37303     /*
37304      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37305      */
37306     maxTabWidth : 250,
37307     /*
37308      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37309      */
37310     preferredTabWidth : 175,
37311     /*
37312      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37313      */
37314     resizeTabs : false,
37315     /*
37316      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37317      */
37318     monitorResize : true,
37319     /*
37320      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37321      */
37322     toolbar : false,
37323
37324     /**
37325      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37326      * @param {String} id The id of the div to use <b>or create</b>
37327      * @param {String} text The text for the tab
37328      * @param {String} content (optional) Content to put in the TabPanelItem body
37329      * @param {Boolean} closable (optional) True to create a close icon on the tab
37330      * @return {Roo.TabPanelItem} The created TabPanelItem
37331      */
37332     addTab : function(id, text, content, closable, tpl)
37333     {
37334         var item = new Roo.bootstrap.panel.TabItem({
37335             panel: this,
37336             id : id,
37337             text : text,
37338             closable : closable,
37339             tpl : tpl
37340         });
37341         this.addTabItem(item);
37342         if(content){
37343             item.setContent(content);
37344         }
37345         return item;
37346     },
37347
37348     /**
37349      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37350      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37351      * @return {Roo.TabPanelItem}
37352      */
37353     getTab : function(id){
37354         return this.items[id];
37355     },
37356
37357     /**
37358      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37359      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37360      */
37361     hideTab : function(id){
37362         var t = this.items[id];
37363         if(!t.isHidden()){
37364            t.setHidden(true);
37365            this.hiddenCount++;
37366            this.autoSizeTabs();
37367         }
37368     },
37369
37370     /**
37371      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37372      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37373      */
37374     unhideTab : function(id){
37375         var t = this.items[id];
37376         if(t.isHidden()){
37377            t.setHidden(false);
37378            this.hiddenCount--;
37379            this.autoSizeTabs();
37380         }
37381     },
37382
37383     /**
37384      * Adds an existing {@link Roo.TabPanelItem}.
37385      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37386      */
37387     addTabItem : function(item){
37388         this.items[item.id] = item;
37389         this.items.push(item);
37390       //  if(this.resizeTabs){
37391     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37392   //         this.autoSizeTabs();
37393 //        }else{
37394 //            item.autoSize();
37395        // }
37396     },
37397
37398     /**
37399      * Removes a {@link Roo.TabPanelItem}.
37400      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37401      */
37402     removeTab : function(id){
37403         var items = this.items;
37404         var tab = items[id];
37405         if(!tab) { return; }
37406         var index = items.indexOf(tab);
37407         if(this.active == tab && items.length > 1){
37408             var newTab = this.getNextAvailable(index);
37409             if(newTab) {
37410                 newTab.activate();
37411             }
37412         }
37413         this.stripEl.dom.removeChild(tab.pnode.dom);
37414         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37415             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37416         }
37417         items.splice(index, 1);
37418         delete this.items[tab.id];
37419         tab.fireEvent("close", tab);
37420         tab.purgeListeners();
37421         this.autoSizeTabs();
37422     },
37423
37424     getNextAvailable : function(start){
37425         var items = this.items;
37426         var index = start;
37427         // look for a next tab that will slide over to
37428         // replace the one being removed
37429         while(index < items.length){
37430             var item = items[++index];
37431             if(item && !item.isHidden()){
37432                 return item;
37433             }
37434         }
37435         // if one isn't found select the previous tab (on the left)
37436         index = start;
37437         while(index >= 0){
37438             var item = items[--index];
37439             if(item && !item.isHidden()){
37440                 return item;
37441             }
37442         }
37443         return null;
37444     },
37445
37446     /**
37447      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37448      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37449      */
37450     disableTab : function(id){
37451         var tab = this.items[id];
37452         if(tab && this.active != tab){
37453             tab.disable();
37454         }
37455     },
37456
37457     /**
37458      * Enables a {@link Roo.TabPanelItem} that is disabled.
37459      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37460      */
37461     enableTab : function(id){
37462         var tab = this.items[id];
37463         tab.enable();
37464     },
37465
37466     /**
37467      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37468      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37469      * @return {Roo.TabPanelItem} The TabPanelItem.
37470      */
37471     activate : function(id){
37472         var tab = this.items[id];
37473         if(!tab){
37474             return null;
37475         }
37476         if(tab == this.active || tab.disabled){
37477             return tab;
37478         }
37479         var e = {};
37480         this.fireEvent("beforetabchange", this, e, tab);
37481         if(e.cancel !== true && !tab.disabled){
37482             if(this.active){
37483                 this.active.hide();
37484             }
37485             this.active = this.items[id];
37486             this.active.show();
37487             this.fireEvent("tabchange", this, this.active);
37488         }
37489         return tab;
37490     },
37491
37492     /**
37493      * Gets the active {@link Roo.TabPanelItem}.
37494      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37495      */
37496     getActiveTab : function(){
37497         return this.active;
37498     },
37499
37500     /**
37501      * Updates the tab body element to fit the height of the container element
37502      * for overflow scrolling
37503      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37504      */
37505     syncHeight : function(targetHeight){
37506         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37507         var bm = this.bodyEl.getMargins();
37508         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37509         this.bodyEl.setHeight(newHeight);
37510         return newHeight;
37511     },
37512
37513     onResize : function(){
37514         if(this.monitorResize){
37515             this.autoSizeTabs();
37516         }
37517     },
37518
37519     /**
37520      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37521      */
37522     beginUpdate : function(){
37523         this.updating = true;
37524     },
37525
37526     /**
37527      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37528      */
37529     endUpdate : function(){
37530         this.updating = false;
37531         this.autoSizeTabs();
37532     },
37533
37534     /**
37535      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37536      */
37537     autoSizeTabs : function(){
37538         var count = this.items.length;
37539         var vcount = count - this.hiddenCount;
37540         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37541             return;
37542         }
37543         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37544         var availWidth = Math.floor(w / vcount);
37545         var b = this.stripBody;
37546         if(b.getWidth() > w){
37547             var tabs = this.items;
37548             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37549             if(availWidth < this.minTabWidth){
37550                 /*if(!this.sleft){    // incomplete scrolling code
37551                     this.createScrollButtons();
37552                 }
37553                 this.showScroll();
37554                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37555             }
37556         }else{
37557             if(this.currentTabWidth < this.preferredTabWidth){
37558                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37559             }
37560         }
37561     },
37562
37563     /**
37564      * Returns the number of tabs in this TabPanel.
37565      * @return {Number}
37566      */
37567      getCount : function(){
37568          return this.items.length;
37569      },
37570
37571     /**
37572      * Resizes all the tabs to the passed width
37573      * @param {Number} The new width
37574      */
37575     setTabWidth : function(width){
37576         this.currentTabWidth = width;
37577         for(var i = 0, len = this.items.length; i < len; i++) {
37578                 if(!this.items[i].isHidden()) {
37579                 this.items[i].setWidth(width);
37580             }
37581         }
37582     },
37583
37584     /**
37585      * Destroys this TabPanel
37586      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37587      */
37588     destroy : function(removeEl){
37589         Roo.EventManager.removeResizeListener(this.onResize, this);
37590         for(var i = 0, len = this.items.length; i < len; i++){
37591             this.items[i].purgeListeners();
37592         }
37593         if(removeEl === true){
37594             this.el.update("");
37595             this.el.remove();
37596         }
37597     },
37598     
37599     createStrip : function(container)
37600     {
37601         var strip = document.createElement("nav");
37602         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37603         container.appendChild(strip);
37604         return strip;
37605     },
37606     
37607     createStripList : function(strip)
37608     {
37609         // div wrapper for retard IE
37610         // returns the "tr" element.
37611         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37612         //'<div class="x-tabs-strip-wrap">'+
37613           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37614           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37615         return strip.firstChild; //.firstChild.firstChild.firstChild;
37616     },
37617     createBody : function(container)
37618     {
37619         var body = document.createElement("div");
37620         Roo.id(body, "tab-body");
37621         //Roo.fly(body).addClass("x-tabs-body");
37622         Roo.fly(body).addClass("tab-content");
37623         container.appendChild(body);
37624         return body;
37625     },
37626     createItemBody :function(bodyEl, id){
37627         var body = Roo.getDom(id);
37628         if(!body){
37629             body = document.createElement("div");
37630             body.id = id;
37631         }
37632         //Roo.fly(body).addClass("x-tabs-item-body");
37633         Roo.fly(body).addClass("tab-pane");
37634          bodyEl.insertBefore(body, bodyEl.firstChild);
37635         return body;
37636     },
37637     /** @private */
37638     createStripElements :  function(stripEl, text, closable, tpl)
37639     {
37640         var td = document.createElement("li"); // was td..
37641         
37642         
37643         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37644         
37645         
37646         stripEl.appendChild(td);
37647         /*if(closable){
37648             td.className = "x-tabs-closable";
37649             if(!this.closeTpl){
37650                 this.closeTpl = new Roo.Template(
37651                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37652                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37653                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37654                 );
37655             }
37656             var el = this.closeTpl.overwrite(td, {"text": text});
37657             var close = el.getElementsByTagName("div")[0];
37658             var inner = el.getElementsByTagName("em")[0];
37659             return {"el": el, "close": close, "inner": inner};
37660         } else {
37661         */
37662         // not sure what this is..
37663 //            if(!this.tabTpl){
37664                 //this.tabTpl = new Roo.Template(
37665                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37666                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37667                 //);
37668 //                this.tabTpl = new Roo.Template(
37669 //                   '<a href="#">' +
37670 //                   '<span unselectable="on"' +
37671 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37672 //                            ' >{text}</span></a>'
37673 //                );
37674 //                
37675 //            }
37676
37677
37678             var template = tpl || this.tabTpl || false;
37679             
37680             if(!template){
37681                 
37682                 template = new Roo.Template(
37683                    '<a href="#">' +
37684                    '<span unselectable="on"' +
37685                             (this.disableTooltips ? '' : ' title="{text}"') +
37686                             ' >{text}</span></a>'
37687                 );
37688             }
37689             
37690             switch (typeof(template)) {
37691                 case 'object' :
37692                     break;
37693                 case 'string' :
37694                     template = new Roo.Template(template);
37695                     break;
37696                 default :
37697                     break;
37698             }
37699             
37700             var el = template.overwrite(td, {"text": text});
37701             
37702             var inner = el.getElementsByTagName("span")[0];
37703             
37704             return {"el": el, "inner": inner};
37705             
37706     }
37707         
37708     
37709 });
37710
37711 /**
37712  * @class Roo.TabPanelItem
37713  * @extends Roo.util.Observable
37714  * Represents an individual item (tab plus body) in a TabPanel.
37715  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37716  * @param {String} id The id of this TabPanelItem
37717  * @param {String} text The text for the tab of this TabPanelItem
37718  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37719  */
37720 Roo.bootstrap.panel.TabItem = function(config){
37721     /**
37722      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37723      * @type Roo.TabPanel
37724      */
37725     this.tabPanel = config.panel;
37726     /**
37727      * The id for this TabPanelItem
37728      * @type String
37729      */
37730     this.id = config.id;
37731     /** @private */
37732     this.disabled = false;
37733     /** @private */
37734     this.text = config.text;
37735     /** @private */
37736     this.loaded = false;
37737     this.closable = config.closable;
37738
37739     /**
37740      * The body element for this TabPanelItem.
37741      * @type Roo.Element
37742      */
37743     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37744     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37745     this.bodyEl.setStyle("display", "block");
37746     this.bodyEl.setStyle("zoom", "1");
37747     //this.hideAction();
37748
37749     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37750     /** @private */
37751     this.el = Roo.get(els.el);
37752     this.inner = Roo.get(els.inner, true);
37753     this.textEl = Roo.get(this.el.dom.firstChild, true);
37754     this.pnode = Roo.get(els.el.parentNode, true);
37755 //    this.el.on("mousedown", this.onTabMouseDown, this);
37756     this.el.on("click", this.onTabClick, this);
37757     /** @private */
37758     if(config.closable){
37759         var c = Roo.get(els.close, true);
37760         c.dom.title = this.closeText;
37761         c.addClassOnOver("close-over");
37762         c.on("click", this.closeClick, this);
37763      }
37764
37765     this.addEvents({
37766          /**
37767          * @event activate
37768          * Fires when this tab becomes the active tab.
37769          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37770          * @param {Roo.TabPanelItem} this
37771          */
37772         "activate": true,
37773         /**
37774          * @event beforeclose
37775          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37776          * @param {Roo.TabPanelItem} this
37777          * @param {Object} e Set cancel to true on this object to cancel the close.
37778          */
37779         "beforeclose": true,
37780         /**
37781          * @event close
37782          * Fires when this tab is closed.
37783          * @param {Roo.TabPanelItem} this
37784          */
37785          "close": true,
37786         /**
37787          * @event deactivate
37788          * Fires when this tab is no longer the active tab.
37789          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37790          * @param {Roo.TabPanelItem} this
37791          */
37792          "deactivate" : true
37793     });
37794     this.hidden = false;
37795
37796     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37797 };
37798
37799 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37800            {
37801     purgeListeners : function(){
37802        Roo.util.Observable.prototype.purgeListeners.call(this);
37803        this.el.removeAllListeners();
37804     },
37805     /**
37806      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37807      */
37808     show : function(){
37809         this.pnode.addClass("active");
37810         this.showAction();
37811         if(Roo.isOpera){
37812             this.tabPanel.stripWrap.repaint();
37813         }
37814         this.fireEvent("activate", this.tabPanel, this);
37815     },
37816
37817     /**
37818      * Returns true if this tab is the active tab.
37819      * @return {Boolean}
37820      */
37821     isActive : function(){
37822         return this.tabPanel.getActiveTab() == this;
37823     },
37824
37825     /**
37826      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37827      */
37828     hide : function(){
37829         this.pnode.removeClass("active");
37830         this.hideAction();
37831         this.fireEvent("deactivate", this.tabPanel, this);
37832     },
37833
37834     hideAction : function(){
37835         this.bodyEl.hide();
37836         this.bodyEl.setStyle("position", "absolute");
37837         this.bodyEl.setLeft("-20000px");
37838         this.bodyEl.setTop("-20000px");
37839     },
37840
37841     showAction : function(){
37842         this.bodyEl.setStyle("position", "relative");
37843         this.bodyEl.setTop("");
37844         this.bodyEl.setLeft("");
37845         this.bodyEl.show();
37846     },
37847
37848     /**
37849      * Set the tooltip for the tab.
37850      * @param {String} tooltip The tab's tooltip
37851      */
37852     setTooltip : function(text){
37853         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37854             this.textEl.dom.qtip = text;
37855             this.textEl.dom.removeAttribute('title');
37856         }else{
37857             this.textEl.dom.title = text;
37858         }
37859     },
37860
37861     onTabClick : function(e){
37862         e.preventDefault();
37863         this.tabPanel.activate(this.id);
37864     },
37865
37866     onTabMouseDown : function(e){
37867         e.preventDefault();
37868         this.tabPanel.activate(this.id);
37869     },
37870 /*
37871     getWidth : function(){
37872         return this.inner.getWidth();
37873     },
37874
37875     setWidth : function(width){
37876         var iwidth = width - this.pnode.getPadding("lr");
37877         this.inner.setWidth(iwidth);
37878         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37879         this.pnode.setWidth(width);
37880     },
37881 */
37882     /**
37883      * Show or hide the tab
37884      * @param {Boolean} hidden True to hide or false to show.
37885      */
37886     setHidden : function(hidden){
37887         this.hidden = hidden;
37888         this.pnode.setStyle("display", hidden ? "none" : "");
37889     },
37890
37891     /**
37892      * Returns true if this tab is "hidden"
37893      * @return {Boolean}
37894      */
37895     isHidden : function(){
37896         return this.hidden;
37897     },
37898
37899     /**
37900      * Returns the text for this tab
37901      * @return {String}
37902      */
37903     getText : function(){
37904         return this.text;
37905     },
37906     /*
37907     autoSize : function(){
37908         //this.el.beginMeasure();
37909         this.textEl.setWidth(1);
37910         /*
37911          *  #2804 [new] Tabs in Roojs
37912          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37913          */
37914         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37915         //this.el.endMeasure();
37916     //},
37917
37918     /**
37919      * Sets the text for the tab (Note: this also sets the tooltip text)
37920      * @param {String} text The tab's text and tooltip
37921      */
37922     setText : function(text){
37923         this.text = text;
37924         this.textEl.update(text);
37925         this.setTooltip(text);
37926         //if(!this.tabPanel.resizeTabs){
37927         //    this.autoSize();
37928         //}
37929     },
37930     /**
37931      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37932      */
37933     activate : function(){
37934         this.tabPanel.activate(this.id);
37935     },
37936
37937     /**
37938      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37939      */
37940     disable : function(){
37941         if(this.tabPanel.active != this){
37942             this.disabled = true;
37943             this.pnode.addClass("disabled");
37944         }
37945     },
37946
37947     /**
37948      * Enables this TabPanelItem if it was previously disabled.
37949      */
37950     enable : function(){
37951         this.disabled = false;
37952         this.pnode.removeClass("disabled");
37953     },
37954
37955     /**
37956      * Sets the content for this TabPanelItem.
37957      * @param {String} content The content
37958      * @param {Boolean} loadScripts true to look for and load scripts
37959      */
37960     setContent : function(content, loadScripts){
37961         this.bodyEl.update(content, loadScripts);
37962     },
37963
37964     /**
37965      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37966      * @return {Roo.UpdateManager} The UpdateManager
37967      */
37968     getUpdateManager : function(){
37969         return this.bodyEl.getUpdateManager();
37970     },
37971
37972     /**
37973      * Set a URL to be used to load the content for this TabPanelItem.
37974      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37975      * @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)
37976      * @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)
37977      * @return {Roo.UpdateManager} The UpdateManager
37978      */
37979     setUrl : function(url, params, loadOnce){
37980         if(this.refreshDelegate){
37981             this.un('activate', this.refreshDelegate);
37982         }
37983         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37984         this.on("activate", this.refreshDelegate);
37985         return this.bodyEl.getUpdateManager();
37986     },
37987
37988     /** @private */
37989     _handleRefresh : function(url, params, loadOnce){
37990         if(!loadOnce || !this.loaded){
37991             var updater = this.bodyEl.getUpdateManager();
37992             updater.update(url, params, this._setLoaded.createDelegate(this));
37993         }
37994     },
37995
37996     /**
37997      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37998      *   Will fail silently if the setUrl method has not been called.
37999      *   This does not activate the panel, just updates its content.
38000      */
38001     refresh : function(){
38002         if(this.refreshDelegate){
38003            this.loaded = false;
38004            this.refreshDelegate();
38005         }
38006     },
38007
38008     /** @private */
38009     _setLoaded : function(){
38010         this.loaded = true;
38011     },
38012
38013     /** @private */
38014     closeClick : function(e){
38015         var o = {};
38016         e.stopEvent();
38017         this.fireEvent("beforeclose", this, o);
38018         if(o.cancel !== true){
38019             this.tabPanel.removeTab(this.id);
38020         }
38021     },
38022     /**
38023      * The text displayed in the tooltip for the close icon.
38024      * @type String
38025      */
38026     closeText : "Close this tab"
38027 });
38028 /**
38029 *    This script refer to:
38030 *    Title: International Telephone Input
38031 *    Author: Jack O'Connor
38032 *    Code version:  v12.1.12
38033 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38034 **/
38035
38036 Roo.bootstrap.PhoneInputData = function() {
38037     var d = [
38038       [
38039         "Afghanistan (‫افغانستان‬‎)",
38040         "af",
38041         "93"
38042       ],
38043       [
38044         "Albania (Shqipëri)",
38045         "al",
38046         "355"
38047       ],
38048       [
38049         "Algeria (‫الجزائر‬‎)",
38050         "dz",
38051         "213"
38052       ],
38053       [
38054         "American Samoa",
38055         "as",
38056         "1684"
38057       ],
38058       [
38059         "Andorra",
38060         "ad",
38061         "376"
38062       ],
38063       [
38064         "Angola",
38065         "ao",
38066         "244"
38067       ],
38068       [
38069         "Anguilla",
38070         "ai",
38071         "1264"
38072       ],
38073       [
38074         "Antigua and Barbuda",
38075         "ag",
38076         "1268"
38077       ],
38078       [
38079         "Argentina",
38080         "ar",
38081         "54"
38082       ],
38083       [
38084         "Armenia (Հայաստան)",
38085         "am",
38086         "374"
38087       ],
38088       [
38089         "Aruba",
38090         "aw",
38091         "297"
38092       ],
38093       [
38094         "Australia",
38095         "au",
38096         "61",
38097         0
38098       ],
38099       [
38100         "Austria (Österreich)",
38101         "at",
38102         "43"
38103       ],
38104       [
38105         "Azerbaijan (Azərbaycan)",
38106         "az",
38107         "994"
38108       ],
38109       [
38110         "Bahamas",
38111         "bs",
38112         "1242"
38113       ],
38114       [
38115         "Bahrain (‫البحرين‬‎)",
38116         "bh",
38117         "973"
38118       ],
38119       [
38120         "Bangladesh (বাংলাদেশ)",
38121         "bd",
38122         "880"
38123       ],
38124       [
38125         "Barbados",
38126         "bb",
38127         "1246"
38128       ],
38129       [
38130         "Belarus (Беларусь)",
38131         "by",
38132         "375"
38133       ],
38134       [
38135         "Belgium (België)",
38136         "be",
38137         "32"
38138       ],
38139       [
38140         "Belize",
38141         "bz",
38142         "501"
38143       ],
38144       [
38145         "Benin (Bénin)",
38146         "bj",
38147         "229"
38148       ],
38149       [
38150         "Bermuda",
38151         "bm",
38152         "1441"
38153       ],
38154       [
38155         "Bhutan (འབྲུག)",
38156         "bt",
38157         "975"
38158       ],
38159       [
38160         "Bolivia",
38161         "bo",
38162         "591"
38163       ],
38164       [
38165         "Bosnia and Herzegovina (Босна и Херцеговина)",
38166         "ba",
38167         "387"
38168       ],
38169       [
38170         "Botswana",
38171         "bw",
38172         "267"
38173       ],
38174       [
38175         "Brazil (Brasil)",
38176         "br",
38177         "55"
38178       ],
38179       [
38180         "British Indian Ocean Territory",
38181         "io",
38182         "246"
38183       ],
38184       [
38185         "British Virgin Islands",
38186         "vg",
38187         "1284"
38188       ],
38189       [
38190         "Brunei",
38191         "bn",
38192         "673"
38193       ],
38194       [
38195         "Bulgaria (България)",
38196         "bg",
38197         "359"
38198       ],
38199       [
38200         "Burkina Faso",
38201         "bf",
38202         "226"
38203       ],
38204       [
38205         "Burundi (Uburundi)",
38206         "bi",
38207         "257"
38208       ],
38209       [
38210         "Cambodia (កម្ពុជា)",
38211         "kh",
38212         "855"
38213       ],
38214       [
38215         "Cameroon (Cameroun)",
38216         "cm",
38217         "237"
38218       ],
38219       [
38220         "Canada",
38221         "ca",
38222         "1",
38223         1,
38224         ["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"]
38225       ],
38226       [
38227         "Cape Verde (Kabu Verdi)",
38228         "cv",
38229         "238"
38230       ],
38231       [
38232         "Caribbean Netherlands",
38233         "bq",
38234         "599",
38235         1
38236       ],
38237       [
38238         "Cayman Islands",
38239         "ky",
38240         "1345"
38241       ],
38242       [
38243         "Central African Republic (République centrafricaine)",
38244         "cf",
38245         "236"
38246       ],
38247       [
38248         "Chad (Tchad)",
38249         "td",
38250         "235"
38251       ],
38252       [
38253         "Chile",
38254         "cl",
38255         "56"
38256       ],
38257       [
38258         "China (中国)",
38259         "cn",
38260         "86"
38261       ],
38262       [
38263         "Christmas Island",
38264         "cx",
38265         "61",
38266         2
38267       ],
38268       [
38269         "Cocos (Keeling) Islands",
38270         "cc",
38271         "61",
38272         1
38273       ],
38274       [
38275         "Colombia",
38276         "co",
38277         "57"
38278       ],
38279       [
38280         "Comoros (‫جزر القمر‬‎)",
38281         "km",
38282         "269"
38283       ],
38284       [
38285         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38286         "cd",
38287         "243"
38288       ],
38289       [
38290         "Congo (Republic) (Congo-Brazzaville)",
38291         "cg",
38292         "242"
38293       ],
38294       [
38295         "Cook Islands",
38296         "ck",
38297         "682"
38298       ],
38299       [
38300         "Costa Rica",
38301         "cr",
38302         "506"
38303       ],
38304       [
38305         "Côte d’Ivoire",
38306         "ci",
38307         "225"
38308       ],
38309       [
38310         "Croatia (Hrvatska)",
38311         "hr",
38312         "385"
38313       ],
38314       [
38315         "Cuba",
38316         "cu",
38317         "53"
38318       ],
38319       [
38320         "Curaçao",
38321         "cw",
38322         "599",
38323         0
38324       ],
38325       [
38326         "Cyprus (Κύπρος)",
38327         "cy",
38328         "357"
38329       ],
38330       [
38331         "Czech Republic (Česká republika)",
38332         "cz",
38333         "420"
38334       ],
38335       [
38336         "Denmark (Danmark)",
38337         "dk",
38338         "45"
38339       ],
38340       [
38341         "Djibouti",
38342         "dj",
38343         "253"
38344       ],
38345       [
38346         "Dominica",
38347         "dm",
38348         "1767"
38349       ],
38350       [
38351         "Dominican Republic (República Dominicana)",
38352         "do",
38353         "1",
38354         2,
38355         ["809", "829", "849"]
38356       ],
38357       [
38358         "Ecuador",
38359         "ec",
38360         "593"
38361       ],
38362       [
38363         "Egypt (‫مصر‬‎)",
38364         "eg",
38365         "20"
38366       ],
38367       [
38368         "El Salvador",
38369         "sv",
38370         "503"
38371       ],
38372       [
38373         "Equatorial Guinea (Guinea Ecuatorial)",
38374         "gq",
38375         "240"
38376       ],
38377       [
38378         "Eritrea",
38379         "er",
38380         "291"
38381       ],
38382       [
38383         "Estonia (Eesti)",
38384         "ee",
38385         "372"
38386       ],
38387       [
38388         "Ethiopia",
38389         "et",
38390         "251"
38391       ],
38392       [
38393         "Falkland Islands (Islas Malvinas)",
38394         "fk",
38395         "500"
38396       ],
38397       [
38398         "Faroe Islands (Føroyar)",
38399         "fo",
38400         "298"
38401       ],
38402       [
38403         "Fiji",
38404         "fj",
38405         "679"
38406       ],
38407       [
38408         "Finland (Suomi)",
38409         "fi",
38410         "358",
38411         0
38412       ],
38413       [
38414         "France",
38415         "fr",
38416         "33"
38417       ],
38418       [
38419         "French Guiana (Guyane française)",
38420         "gf",
38421         "594"
38422       ],
38423       [
38424         "French Polynesia (Polynésie française)",
38425         "pf",
38426         "689"
38427       ],
38428       [
38429         "Gabon",
38430         "ga",
38431         "241"
38432       ],
38433       [
38434         "Gambia",
38435         "gm",
38436         "220"
38437       ],
38438       [
38439         "Georgia (საქართველო)",
38440         "ge",
38441         "995"
38442       ],
38443       [
38444         "Germany (Deutschland)",
38445         "de",
38446         "49"
38447       ],
38448       [
38449         "Ghana (Gaana)",
38450         "gh",
38451         "233"
38452       ],
38453       [
38454         "Gibraltar",
38455         "gi",
38456         "350"
38457       ],
38458       [
38459         "Greece (Ελλάδα)",
38460         "gr",
38461         "30"
38462       ],
38463       [
38464         "Greenland (Kalaallit Nunaat)",
38465         "gl",
38466         "299"
38467       ],
38468       [
38469         "Grenada",
38470         "gd",
38471         "1473"
38472       ],
38473       [
38474         "Guadeloupe",
38475         "gp",
38476         "590",
38477         0
38478       ],
38479       [
38480         "Guam",
38481         "gu",
38482         "1671"
38483       ],
38484       [
38485         "Guatemala",
38486         "gt",
38487         "502"
38488       ],
38489       [
38490         "Guernsey",
38491         "gg",
38492         "44",
38493         1
38494       ],
38495       [
38496         "Guinea (Guinée)",
38497         "gn",
38498         "224"
38499       ],
38500       [
38501         "Guinea-Bissau (Guiné Bissau)",
38502         "gw",
38503         "245"
38504       ],
38505       [
38506         "Guyana",
38507         "gy",
38508         "592"
38509       ],
38510       [
38511         "Haiti",
38512         "ht",
38513         "509"
38514       ],
38515       [
38516         "Honduras",
38517         "hn",
38518         "504"
38519       ],
38520       [
38521         "Hong Kong (香港)",
38522         "hk",
38523         "852"
38524       ],
38525       [
38526         "Hungary (Magyarország)",
38527         "hu",
38528         "36"
38529       ],
38530       [
38531         "Iceland (Ísland)",
38532         "is",
38533         "354"
38534       ],
38535       [
38536         "India (भारत)",
38537         "in",
38538         "91"
38539       ],
38540       [
38541         "Indonesia",
38542         "id",
38543         "62"
38544       ],
38545       [
38546         "Iran (‫ایران‬‎)",
38547         "ir",
38548         "98"
38549       ],
38550       [
38551         "Iraq (‫العراق‬‎)",
38552         "iq",
38553         "964"
38554       ],
38555       [
38556         "Ireland",
38557         "ie",
38558         "353"
38559       ],
38560       [
38561         "Isle of Man",
38562         "im",
38563         "44",
38564         2
38565       ],
38566       [
38567         "Israel (‫ישראל‬‎)",
38568         "il",
38569         "972"
38570       ],
38571       [
38572         "Italy (Italia)",
38573         "it",
38574         "39",
38575         0
38576       ],
38577       [
38578         "Jamaica",
38579         "jm",
38580         "1876"
38581       ],
38582       [
38583         "Japan (日本)",
38584         "jp",
38585         "81"
38586       ],
38587       [
38588         "Jersey",
38589         "je",
38590         "44",
38591         3
38592       ],
38593       [
38594         "Jordan (‫الأردن‬‎)",
38595         "jo",
38596         "962"
38597       ],
38598       [
38599         "Kazakhstan (Казахстан)",
38600         "kz",
38601         "7",
38602         1
38603       ],
38604       [
38605         "Kenya",
38606         "ke",
38607         "254"
38608       ],
38609       [
38610         "Kiribati",
38611         "ki",
38612         "686"
38613       ],
38614       [
38615         "Kosovo",
38616         "xk",
38617         "383"
38618       ],
38619       [
38620         "Kuwait (‫الكويت‬‎)",
38621         "kw",
38622         "965"
38623       ],
38624       [
38625         "Kyrgyzstan (Кыргызстан)",
38626         "kg",
38627         "996"
38628       ],
38629       [
38630         "Laos (ລາວ)",
38631         "la",
38632         "856"
38633       ],
38634       [
38635         "Latvia (Latvija)",
38636         "lv",
38637         "371"
38638       ],
38639       [
38640         "Lebanon (‫لبنان‬‎)",
38641         "lb",
38642         "961"
38643       ],
38644       [
38645         "Lesotho",
38646         "ls",
38647         "266"
38648       ],
38649       [
38650         "Liberia",
38651         "lr",
38652         "231"
38653       ],
38654       [
38655         "Libya (‫ليبيا‬‎)",
38656         "ly",
38657         "218"
38658       ],
38659       [
38660         "Liechtenstein",
38661         "li",
38662         "423"
38663       ],
38664       [
38665         "Lithuania (Lietuva)",
38666         "lt",
38667         "370"
38668       ],
38669       [
38670         "Luxembourg",
38671         "lu",
38672         "352"
38673       ],
38674       [
38675         "Macau (澳門)",
38676         "mo",
38677         "853"
38678       ],
38679       [
38680         "Macedonia (FYROM) (Македонија)",
38681         "mk",
38682         "389"
38683       ],
38684       [
38685         "Madagascar (Madagasikara)",
38686         "mg",
38687         "261"
38688       ],
38689       [
38690         "Malawi",
38691         "mw",
38692         "265"
38693       ],
38694       [
38695         "Malaysia",
38696         "my",
38697         "60"
38698       ],
38699       [
38700         "Maldives",
38701         "mv",
38702         "960"
38703       ],
38704       [
38705         "Mali",
38706         "ml",
38707         "223"
38708       ],
38709       [
38710         "Malta",
38711         "mt",
38712         "356"
38713       ],
38714       [
38715         "Marshall Islands",
38716         "mh",
38717         "692"
38718       ],
38719       [
38720         "Martinique",
38721         "mq",
38722         "596"
38723       ],
38724       [
38725         "Mauritania (‫موريتانيا‬‎)",
38726         "mr",
38727         "222"
38728       ],
38729       [
38730         "Mauritius (Moris)",
38731         "mu",
38732         "230"
38733       ],
38734       [
38735         "Mayotte",
38736         "yt",
38737         "262",
38738         1
38739       ],
38740       [
38741         "Mexico (México)",
38742         "mx",
38743         "52"
38744       ],
38745       [
38746         "Micronesia",
38747         "fm",
38748         "691"
38749       ],
38750       [
38751         "Moldova (Republica Moldova)",
38752         "md",
38753         "373"
38754       ],
38755       [
38756         "Monaco",
38757         "mc",
38758         "377"
38759       ],
38760       [
38761         "Mongolia (Монгол)",
38762         "mn",
38763         "976"
38764       ],
38765       [
38766         "Montenegro (Crna Gora)",
38767         "me",
38768         "382"
38769       ],
38770       [
38771         "Montserrat",
38772         "ms",
38773         "1664"
38774       ],
38775       [
38776         "Morocco (‫المغرب‬‎)",
38777         "ma",
38778         "212",
38779         0
38780       ],
38781       [
38782         "Mozambique (Moçambique)",
38783         "mz",
38784         "258"
38785       ],
38786       [
38787         "Myanmar (Burma) (မြန်မာ)",
38788         "mm",
38789         "95"
38790       ],
38791       [
38792         "Namibia (Namibië)",
38793         "na",
38794         "264"
38795       ],
38796       [
38797         "Nauru",
38798         "nr",
38799         "674"
38800       ],
38801       [
38802         "Nepal (नेपाल)",
38803         "np",
38804         "977"
38805       ],
38806       [
38807         "Netherlands (Nederland)",
38808         "nl",
38809         "31"
38810       ],
38811       [
38812         "New Caledonia (Nouvelle-Calédonie)",
38813         "nc",
38814         "687"
38815       ],
38816       [
38817         "New Zealand",
38818         "nz",
38819         "64"
38820       ],
38821       [
38822         "Nicaragua",
38823         "ni",
38824         "505"
38825       ],
38826       [
38827         "Niger (Nijar)",
38828         "ne",
38829         "227"
38830       ],
38831       [
38832         "Nigeria",
38833         "ng",
38834         "234"
38835       ],
38836       [
38837         "Niue",
38838         "nu",
38839         "683"
38840       ],
38841       [
38842         "Norfolk Island",
38843         "nf",
38844         "672"
38845       ],
38846       [
38847         "North Korea (조선 민주주의 인민 공화국)",
38848         "kp",
38849         "850"
38850       ],
38851       [
38852         "Northern Mariana Islands",
38853         "mp",
38854         "1670"
38855       ],
38856       [
38857         "Norway (Norge)",
38858         "no",
38859         "47",
38860         0
38861       ],
38862       [
38863         "Oman (‫عُمان‬‎)",
38864         "om",
38865         "968"
38866       ],
38867       [
38868         "Pakistan (‫پاکستان‬‎)",
38869         "pk",
38870         "92"
38871       ],
38872       [
38873         "Palau",
38874         "pw",
38875         "680"
38876       ],
38877       [
38878         "Palestine (‫فلسطين‬‎)",
38879         "ps",
38880         "970"
38881       ],
38882       [
38883         "Panama (Panamá)",
38884         "pa",
38885         "507"
38886       ],
38887       [
38888         "Papua New Guinea",
38889         "pg",
38890         "675"
38891       ],
38892       [
38893         "Paraguay",
38894         "py",
38895         "595"
38896       ],
38897       [
38898         "Peru (Perú)",
38899         "pe",
38900         "51"
38901       ],
38902       [
38903         "Philippines",
38904         "ph",
38905         "63"
38906       ],
38907       [
38908         "Poland (Polska)",
38909         "pl",
38910         "48"
38911       ],
38912       [
38913         "Portugal",
38914         "pt",
38915         "351"
38916       ],
38917       [
38918         "Puerto Rico",
38919         "pr",
38920         "1",
38921         3,
38922         ["787", "939"]
38923       ],
38924       [
38925         "Qatar (‫قطر‬‎)",
38926         "qa",
38927         "974"
38928       ],
38929       [
38930         "Réunion (La Réunion)",
38931         "re",
38932         "262",
38933         0
38934       ],
38935       [
38936         "Romania (România)",
38937         "ro",
38938         "40"
38939       ],
38940       [
38941         "Russia (Россия)",
38942         "ru",
38943         "7",
38944         0
38945       ],
38946       [
38947         "Rwanda",
38948         "rw",
38949         "250"
38950       ],
38951       [
38952         "Saint Barthélemy",
38953         "bl",
38954         "590",
38955         1
38956       ],
38957       [
38958         "Saint Helena",
38959         "sh",
38960         "290"
38961       ],
38962       [
38963         "Saint Kitts and Nevis",
38964         "kn",
38965         "1869"
38966       ],
38967       [
38968         "Saint Lucia",
38969         "lc",
38970         "1758"
38971       ],
38972       [
38973         "Saint Martin (Saint-Martin (partie française))",
38974         "mf",
38975         "590",
38976         2
38977       ],
38978       [
38979         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38980         "pm",
38981         "508"
38982       ],
38983       [
38984         "Saint Vincent and the Grenadines",
38985         "vc",
38986         "1784"
38987       ],
38988       [
38989         "Samoa",
38990         "ws",
38991         "685"
38992       ],
38993       [
38994         "San Marino",
38995         "sm",
38996         "378"
38997       ],
38998       [
38999         "São Tomé and Príncipe (São Tomé e Príncipe)",
39000         "st",
39001         "239"
39002       ],
39003       [
39004         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39005         "sa",
39006         "966"
39007       ],
39008       [
39009         "Senegal (Sénégal)",
39010         "sn",
39011         "221"
39012       ],
39013       [
39014         "Serbia (Србија)",
39015         "rs",
39016         "381"
39017       ],
39018       [
39019         "Seychelles",
39020         "sc",
39021         "248"
39022       ],
39023       [
39024         "Sierra Leone",
39025         "sl",
39026         "232"
39027       ],
39028       [
39029         "Singapore",
39030         "sg",
39031         "65"
39032       ],
39033       [
39034         "Sint Maarten",
39035         "sx",
39036         "1721"
39037       ],
39038       [
39039         "Slovakia (Slovensko)",
39040         "sk",
39041         "421"
39042       ],
39043       [
39044         "Slovenia (Slovenija)",
39045         "si",
39046         "386"
39047       ],
39048       [
39049         "Solomon Islands",
39050         "sb",
39051         "677"
39052       ],
39053       [
39054         "Somalia (Soomaaliya)",
39055         "so",
39056         "252"
39057       ],
39058       [
39059         "South Africa",
39060         "za",
39061         "27"
39062       ],
39063       [
39064         "South Korea (대한민국)",
39065         "kr",
39066         "82"
39067       ],
39068       [
39069         "South Sudan (‫جنوب السودان‬‎)",
39070         "ss",
39071         "211"
39072       ],
39073       [
39074         "Spain (España)",
39075         "es",
39076         "34"
39077       ],
39078       [
39079         "Sri Lanka (ශ්‍රී ලංකාව)",
39080         "lk",
39081         "94"
39082       ],
39083       [
39084         "Sudan (‫السودان‬‎)",
39085         "sd",
39086         "249"
39087       ],
39088       [
39089         "Suriname",
39090         "sr",
39091         "597"
39092       ],
39093       [
39094         "Svalbard and Jan Mayen",
39095         "sj",
39096         "47",
39097         1
39098       ],
39099       [
39100         "Swaziland",
39101         "sz",
39102         "268"
39103       ],
39104       [
39105         "Sweden (Sverige)",
39106         "se",
39107         "46"
39108       ],
39109       [
39110         "Switzerland (Schweiz)",
39111         "ch",
39112         "41"
39113       ],
39114       [
39115         "Syria (‫سوريا‬‎)",
39116         "sy",
39117         "963"
39118       ],
39119       [
39120         "Taiwan (台灣)",
39121         "tw",
39122         "886"
39123       ],
39124       [
39125         "Tajikistan",
39126         "tj",
39127         "992"
39128       ],
39129       [
39130         "Tanzania",
39131         "tz",
39132         "255"
39133       ],
39134       [
39135         "Thailand (ไทย)",
39136         "th",
39137         "66"
39138       ],
39139       [
39140         "Timor-Leste",
39141         "tl",
39142         "670"
39143       ],
39144       [
39145         "Togo",
39146         "tg",
39147         "228"
39148       ],
39149       [
39150         "Tokelau",
39151         "tk",
39152         "690"
39153       ],
39154       [
39155         "Tonga",
39156         "to",
39157         "676"
39158       ],
39159       [
39160         "Trinidad and Tobago",
39161         "tt",
39162         "1868"
39163       ],
39164       [
39165         "Tunisia (‫تونس‬‎)",
39166         "tn",
39167         "216"
39168       ],
39169       [
39170         "Turkey (Türkiye)",
39171         "tr",
39172         "90"
39173       ],
39174       [
39175         "Turkmenistan",
39176         "tm",
39177         "993"
39178       ],
39179       [
39180         "Turks and Caicos Islands",
39181         "tc",
39182         "1649"
39183       ],
39184       [
39185         "Tuvalu",
39186         "tv",
39187         "688"
39188       ],
39189       [
39190         "U.S. Virgin Islands",
39191         "vi",
39192         "1340"
39193       ],
39194       [
39195         "Uganda",
39196         "ug",
39197         "256"
39198       ],
39199       [
39200         "Ukraine (Україна)",
39201         "ua",
39202         "380"
39203       ],
39204       [
39205         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39206         "ae",
39207         "971"
39208       ],
39209       [
39210         "United Kingdom",
39211         "gb",
39212         "44",
39213         0
39214       ],
39215       [
39216         "United States",
39217         "us",
39218         "1",
39219         0
39220       ],
39221       [
39222         "Uruguay",
39223         "uy",
39224         "598"
39225       ],
39226       [
39227         "Uzbekistan (Oʻzbekiston)",
39228         "uz",
39229         "998"
39230       ],
39231       [
39232         "Vanuatu",
39233         "vu",
39234         "678"
39235       ],
39236       [
39237         "Vatican City (Città del Vaticano)",
39238         "va",
39239         "39",
39240         1
39241       ],
39242       [
39243         "Venezuela",
39244         "ve",
39245         "58"
39246       ],
39247       [
39248         "Vietnam (Việt Nam)",
39249         "vn",
39250         "84"
39251       ],
39252       [
39253         "Wallis and Futuna (Wallis-et-Futuna)",
39254         "wf",
39255         "681"
39256       ],
39257       [
39258         "Western Sahara (‫الصحراء الغربية‬‎)",
39259         "eh",
39260         "212",
39261         1
39262       ],
39263       [
39264         "Yemen (‫اليمن‬‎)",
39265         "ye",
39266         "967"
39267       ],
39268       [
39269         "Zambia",
39270         "zm",
39271         "260"
39272       ],
39273       [
39274         "Zimbabwe",
39275         "zw",
39276         "263"
39277       ],
39278       [
39279         "Åland Islands",
39280         "ax",
39281         "358",
39282         1
39283       ]
39284   ];
39285   
39286   return d;
39287 }/**
39288 *    This script refer to:
39289 *    Title: International Telephone Input
39290 *    Author: Jack O'Connor
39291 *    Code version:  v12.1.12
39292 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39293 **/
39294
39295 /**
39296  * @class Roo.bootstrap.PhoneInput
39297  * @extends Roo.bootstrap.TriggerField
39298  * An input with International dial-code selection
39299  
39300  * @cfg {String} defaultDialCode default '+852'
39301  * @cfg {Array} preferedCountries default []
39302   
39303  * @constructor
39304  * Create a new PhoneInput.
39305  * @param {Object} config Configuration options
39306  */
39307
39308 Roo.bootstrap.PhoneInput = function(config) {
39309     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39310 };
39311
39312 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39313         
39314         listWidth: undefined,
39315         
39316         selectedClass: 'active',
39317         
39318         invalidClass : "has-warning",
39319         
39320         validClass: 'has-success',
39321         
39322         allowed: '0123456789',
39323         
39324         /**
39325          * @cfg {String} defaultDialCode The default dial code when initializing the input
39326          */
39327         defaultDialCode: '+852',
39328         
39329         /**
39330          * @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
39331          */
39332         preferedCountries: false,
39333         
39334         getAutoCreate : function()
39335         {
39336             var data = Roo.bootstrap.PhoneInputData();
39337             var align = this.labelAlign || this.parentLabelAlign();
39338             var id = Roo.id();
39339             
39340             this.allCountries = [];
39341             this.dialCodeMapping = [];
39342             
39343             for (var i = 0; i < data.length; i++) {
39344               var c = data[i];
39345               this.allCountries[i] = {
39346                 name: c[0],
39347                 iso2: c[1],
39348                 dialCode: c[2],
39349                 priority: c[3] || 0,
39350                 areaCodes: c[4] || null
39351               };
39352               this.dialCodeMapping[c[2]] = {
39353                   name: c[0],
39354                   iso2: c[1],
39355                   priority: c[3] || 0,
39356                   areaCodes: c[4] || null
39357               };
39358             }
39359             
39360             var cfg = {
39361                 cls: 'form-group',
39362                 cn: []
39363             };
39364             
39365             var input =  {
39366                 tag: 'input',
39367                 id : id,
39368                 cls : 'form-control tel-input',
39369                 autocomplete: 'new-password'
39370             };
39371             
39372             var hiddenInput = {
39373                 tag: 'input',
39374                 type: 'hidden',
39375                 cls: 'hidden-tel-input'
39376             };
39377             
39378             if (this.name) {
39379                 hiddenInput.name = this.name;
39380             }
39381             
39382             if (this.disabled) {
39383                 input.disabled = true;
39384             }
39385             
39386             var flag_container = {
39387                 tag: 'div',
39388                 cls: 'flag-box',
39389                 cn: [
39390                     {
39391                         tag: 'div',
39392                         cls: 'flag'
39393                     },
39394                     {
39395                         tag: 'div',
39396                         cls: 'caret'
39397                     }
39398                 ]
39399             };
39400             
39401             var box = {
39402                 tag: 'div',
39403                 cls: this.hasFeedback ? 'has-feedback' : '',
39404                 cn: [
39405                     hiddenInput,
39406                     input,
39407                     {
39408                         tag: 'input',
39409                         cls: 'dial-code-holder',
39410                         disabled: true
39411                     }
39412                 ]
39413             };
39414             
39415             var container = {
39416                 cls: 'roo-select2-container input-group',
39417                 cn: [
39418                     flag_container,
39419                     box
39420                 ]
39421             };
39422             
39423             if (this.fieldLabel.length) {
39424                 var indicator = {
39425                     tag: 'i',
39426                     tooltip: 'This field is required'
39427                 };
39428                 
39429                 var label = {
39430                     tag: 'label',
39431                     'for':  id,
39432                     cls: 'control-label',
39433                     cn: []
39434                 };
39435                 
39436                 var label_text = {
39437                     tag: 'span',
39438                     html: this.fieldLabel
39439                 };
39440                 
39441                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39442                 label.cn = [
39443                     indicator,
39444                     label_text
39445                 ];
39446                 
39447                 if(this.indicatorpos == 'right') {
39448                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39449                     label.cn = [
39450                         label_text,
39451                         indicator
39452                     ];
39453                 }
39454                 
39455                 if(align == 'left') {
39456                     container = {
39457                         tag: 'div',
39458                         cn: [
39459                             container
39460                         ]
39461                     };
39462                     
39463                     if(this.labelWidth > 12){
39464                         label.style = "width: " + this.labelWidth + 'px';
39465                     }
39466                     if(this.labelWidth < 13 && this.labelmd == 0){
39467                         this.labelmd = this.labelWidth;
39468                     }
39469                     if(this.labellg > 0){
39470                         label.cls += ' col-lg-' + this.labellg;
39471                         input.cls += ' col-lg-' + (12 - this.labellg);
39472                     }
39473                     if(this.labelmd > 0){
39474                         label.cls += ' col-md-' + this.labelmd;
39475                         container.cls += ' col-md-' + (12 - this.labelmd);
39476                     }
39477                     if(this.labelsm > 0){
39478                         label.cls += ' col-sm-' + this.labelsm;
39479                         container.cls += ' col-sm-' + (12 - this.labelsm);
39480                     }
39481                     if(this.labelxs > 0){
39482                         label.cls += ' col-xs-' + this.labelxs;
39483                         container.cls += ' col-xs-' + (12 - this.labelxs);
39484                     }
39485                 }
39486             }
39487             
39488             cfg.cn = [
39489                 label,
39490                 container
39491             ];
39492             
39493             var settings = this;
39494             
39495             ['xs','sm','md','lg'].map(function(size){
39496                 if (settings[size]) {
39497                     cfg.cls += ' col-' + size + '-' + settings[size];
39498                 }
39499             });
39500             
39501             this.store = new Roo.data.Store({
39502                 proxy : new Roo.data.MemoryProxy({}),
39503                 reader : new Roo.data.JsonReader({
39504                     fields : [
39505                         {
39506                             'name' : 'name',
39507                             'type' : 'string'
39508                         },
39509                         {
39510                             'name' : 'iso2',
39511                             'type' : 'string'
39512                         },
39513                         {
39514                             'name' : 'dialCode',
39515                             'type' : 'string'
39516                         },
39517                         {
39518                             'name' : 'priority',
39519                             'type' : 'string'
39520                         },
39521                         {
39522                             'name' : 'areaCodes',
39523                             'type' : 'string'
39524                         }
39525                     ]
39526                 })
39527             });
39528             
39529             if(!this.preferedCountries) {
39530                 this.preferedCountries = [
39531                     'hk',
39532                     'gb',
39533                     'us'
39534                 ];
39535             }
39536             
39537             var p = this.preferedCountries.reverse();
39538             
39539             if(p) {
39540                 for (var i = 0; i < p.length; i++) {
39541                     for (var j = 0; j < this.allCountries.length; j++) {
39542                         if(this.allCountries[j].iso2 == p[i]) {
39543                             var t = this.allCountries[j];
39544                             this.allCountries.splice(j,1);
39545                             this.allCountries.unshift(t);
39546                         }
39547                     } 
39548                 }
39549             }
39550             
39551             this.store.proxy.data = {
39552                 success: true,
39553                 data: this.allCountries
39554             };
39555             
39556             return cfg;
39557         },
39558         
39559         initEvents : function()
39560         {
39561             this.createList();
39562             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39563             
39564             this.indicator = this.indicatorEl();
39565             this.flag = this.flagEl();
39566             this.dialCodeHolder = this.dialCodeHolderEl();
39567             
39568             this.trigger = this.el.select('div.flag-box',true).first();
39569             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39570             
39571             var _this = this;
39572             
39573             (function(){
39574                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39575                 _this.list.setWidth(lw);
39576             }).defer(100);
39577             
39578             this.list.on('mouseover', this.onViewOver, this);
39579             this.list.on('mousemove', this.onViewMove, this);
39580             this.inputEl().on("keyup", this.onKeyUp, this);
39581             
39582             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39583
39584             this.view = new Roo.View(this.list, this.tpl, {
39585                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39586             });
39587             
39588             this.view.on('click', this.onViewClick, this);
39589             this.setValue(this.defaultDialCode);
39590         },
39591         
39592         onTriggerClick : function(e)
39593         {
39594             Roo.log('trigger click');
39595             if(this.disabled){
39596                 return;
39597             }
39598             
39599             if(this.isExpanded()){
39600                 this.collapse();
39601                 this.hasFocus = false;
39602             }else {
39603                 this.store.load({});
39604                 this.hasFocus = true;
39605                 this.expand();
39606             }
39607         },
39608         
39609         isExpanded : function()
39610         {
39611             return this.list.isVisible();
39612         },
39613         
39614         collapse : function()
39615         {
39616             if(!this.isExpanded()){
39617                 return;
39618             }
39619             this.list.hide();
39620             Roo.get(document).un('mousedown', this.collapseIf, this);
39621             Roo.get(document).un('mousewheel', this.collapseIf, this);
39622             this.fireEvent('collapse', this);
39623             this.validate();
39624         },
39625         
39626         expand : function()
39627         {
39628             Roo.log('expand');
39629
39630             if(this.isExpanded() || !this.hasFocus){
39631                 return;
39632             }
39633             
39634             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39635             this.list.setWidth(lw);
39636             
39637             this.list.show();
39638             this.restrictHeight();
39639             
39640             Roo.get(document).on('mousedown', this.collapseIf, this);
39641             Roo.get(document).on('mousewheel', this.collapseIf, this);
39642             
39643             this.fireEvent('expand', this);
39644         },
39645         
39646         restrictHeight : function()
39647         {
39648             this.list.alignTo(this.inputEl(), this.listAlign);
39649             this.list.alignTo(this.inputEl(), this.listAlign);
39650         },
39651         
39652         onViewOver : function(e, t)
39653         {
39654             if(this.inKeyMode){
39655                 return;
39656             }
39657             var item = this.view.findItemFromChild(t);
39658             
39659             if(item){
39660                 var index = this.view.indexOf(item);
39661                 this.select(index, false);
39662             }
39663         },
39664
39665         // private
39666         onViewClick : function(view, doFocus, el, e)
39667         {
39668             var index = this.view.getSelectedIndexes()[0];
39669             
39670             var r = this.store.getAt(index);
39671             
39672             if(r){
39673                 this.onSelect(r, index);
39674             }
39675             if(doFocus !== false && !this.blockFocus){
39676                 this.inputEl().focus();
39677             }
39678         },
39679         
39680         onViewMove : function(e, t)
39681         {
39682             this.inKeyMode = false;
39683         },
39684         
39685         select : function(index, scrollIntoView)
39686         {
39687             this.selectedIndex = index;
39688             this.view.select(index);
39689             if(scrollIntoView !== false){
39690                 var el = this.view.getNode(index);
39691                 if(el){
39692                     this.list.scrollChildIntoView(el, false);
39693                 }
39694             }
39695         },
39696         
39697         createList : function()
39698         {
39699             this.list = Roo.get(document.body).createChild({
39700                 tag: 'ul',
39701                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39702                 style: 'display:none'
39703             });
39704             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39705         },
39706         
39707         collapseIf : function(e)
39708         {
39709             var in_combo  = e.within(this.el);
39710             var in_list =  e.within(this.list);
39711             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39712             
39713             if (in_combo || in_list || is_list) {
39714                 return;
39715             }
39716             this.collapse();
39717         },
39718         
39719         onSelect : function(record, index)
39720         {
39721             if(this.fireEvent('beforeselect', this, record, index) !== false){
39722                 
39723                 this.setFlagClass(record.data.iso2);
39724                 this.setDialCode(record.data.dialCode);
39725                 this.hasFocus = false;
39726                 this.collapse();
39727                 this.fireEvent('select', this, record, index);
39728             }
39729         },
39730         
39731         flagEl : function()
39732         {
39733             var flag = this.el.select('div.flag',true).first();
39734             if(!flag){
39735                 return false;
39736             }
39737             return flag;
39738         },
39739         
39740         dialCodeHolderEl : function()
39741         {
39742             var d = this.el.select('input.dial-code-holder',true).first();
39743             if(!d){
39744                 return false;
39745             }
39746             return d;
39747         },
39748         
39749         setDialCode : function(v)
39750         {
39751             this.dialCodeHolder.dom.value = '+'+v;
39752         },
39753         
39754         setFlagClass : function(n)
39755         {
39756             this.flag.dom.className = 'flag '+n;
39757         },
39758         
39759         getValue : function()
39760         {
39761             var v = this.inputEl().getValue();
39762             if(this.dialCodeHolder) {
39763                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39764             }
39765             return v;
39766         },
39767         
39768         setValue : function(v)
39769         {
39770             var d = this.getDialCode(v);
39771             
39772             //invalid dial code
39773             if(v.length == 0 || !d || d.length == 0) {
39774                 if(this.rendered){
39775                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39776                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39777                 }
39778                 return;
39779             }
39780             
39781             //valid dial code
39782             this.setFlagClass(this.dialCodeMapping[d].iso2);
39783             this.setDialCode(d);
39784             this.inputEl().dom.value = v.replace('+'+d,'');
39785             this.hiddenEl().dom.value = this.getValue();
39786             
39787             this.validate();
39788         },
39789         
39790         getDialCode : function(v = '')
39791         {
39792             if (v.length == 0) {
39793                 return this.dialCodeHolder.dom.value;
39794             }
39795             
39796             var dialCode = "";
39797             if (v.charAt(0) != "+") {
39798                 return false;
39799             }
39800             var numericChars = "";
39801             for (var i = 1; i < v.length; i++) {
39802               var c = v.charAt(i);
39803               if (!isNaN(c)) {
39804                 numericChars += c;
39805                 if (this.dialCodeMapping[numericChars]) {
39806                   dialCode = v.substr(1, i);
39807                 }
39808                 if (numericChars.length == 4) {
39809                   break;
39810                 }
39811               }
39812             }
39813             return dialCode;
39814         },
39815         
39816         reset : function()
39817         {
39818             this.setValue(this.defaultDialCode);
39819             this.validate();
39820         },
39821         
39822         hiddenEl : function()
39823         {
39824             return this.el.select('input.hidden-tel-input',true).first();
39825         },
39826         
39827         onKeyUp : function(e){
39828             
39829             var k = e.getKey();
39830             var c = e.getCharCode();
39831             
39832             if(
39833                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39834                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39835             ){
39836                 e.stopEvent();
39837             }
39838             
39839             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39840             //     return;
39841             // }
39842             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39843                 e.stopEvent();
39844             }
39845             
39846             this.setValue(this.getValue());
39847         }
39848         
39849 });
39850 /**
39851  * @class Roo.bootstrap.MoneyField
39852  * @extends Roo.bootstrap.ComboBox
39853  * Bootstrap MoneyField class
39854  * 
39855  * @constructor
39856  * Create a new MoneyField.
39857  * @param {Object} config Configuration options
39858  */
39859
39860 Roo.bootstrap.MoneyField = function(config) {
39861     
39862     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39863     
39864 };
39865
39866 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39867     
39868     /**
39869      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39870      */
39871     allowDecimals : true,
39872     /**
39873      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39874      */
39875     decimalSeparator : ".",
39876     /**
39877      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39878      */
39879     decimalPrecision : 2,
39880     /**
39881      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39882      */
39883     allowNegative : true,
39884     /**
39885      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39886      */
39887     minValue : Number.NEGATIVE_INFINITY,
39888     /**
39889      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39890      */
39891     maxValue : Number.MAX_VALUE,
39892     /**
39893      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39894      */
39895     minText : "The minimum value for this field is {0}",
39896     /**
39897      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39898      */
39899     maxText : "The maximum value for this field is {0}",
39900     /**
39901      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39902      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39903      */
39904     nanText : "{0} is not a valid number",
39905     /**
39906      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39907      */
39908     castInt : true,
39909     
39910     inputlg : 9,
39911     inputmd : 9,
39912     inputsm : 9,
39913     inputxs : 6,
39914     
39915     store : false,
39916     
39917     getAutoCreate : function()
39918     {
39919         var align = this.labelAlign || this.parentLabelAlign();
39920         
39921         var id = Roo.id();
39922
39923         var cfg = {
39924             cls: 'form-group',
39925             cn: []
39926         };
39927
39928         var input =  {
39929             tag: 'input',
39930             id : id,
39931             cls : 'form-control roo-money-amount-input',
39932             autocomplete: 'new-password'
39933         };
39934         
39935         if (this.name) {
39936             input.name = this.name;
39937         }
39938
39939         if (this.disabled) {
39940             input.disabled = true;
39941         }
39942
39943         var clg = 12 - this.inputlg;
39944         var cmd = 12 - this.inputmd;
39945         var csm = 12 - this.inputsm;
39946         var cxs = 12 - this.inputxs;
39947         
39948         var container = {
39949             tag : 'div',
39950             cls : 'row roo-money-field',
39951             cn : [
39952                 {
39953                     tag : 'div',
39954                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39955                     cn : [
39956                         {
39957                             tag : 'div',
39958                             cls: 'roo-select2-container input-group',
39959                             cn: [
39960                                 {
39961                                     tag : 'input',
39962                                     cls : 'form-control roo-money-currency-input',
39963                                     autocomplete: 'new-password',
39964                                     readOnly : 1,
39965                                     name : this.currencyName
39966                                 },
39967                                 {
39968                                     tag :'span',
39969                                     cls : 'input-group-addon',
39970                                     cn : [
39971                                         {
39972                                             tag: 'span',
39973                                             cls: 'caret'
39974                                         }
39975                                     ]
39976                                 }
39977                             ]
39978                         }
39979                     ]
39980                 },
39981                 {
39982                     tag : 'div',
39983                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39984                     cn : [
39985                         {
39986                             tag: 'div',
39987                             cls: this.hasFeedback ? 'has-feedback' : '',
39988                             cn: [
39989                                 input
39990                             ]
39991                         }
39992                     ]
39993                 }
39994             ]
39995             
39996         };
39997         
39998         if (this.fieldLabel.length) {
39999             var indicator = {
40000                 tag: 'i',
40001                 tooltip: 'This field is required'
40002             };
40003
40004             var label = {
40005                 tag: 'label',
40006                 'for':  id,
40007                 cls: 'control-label',
40008                 cn: []
40009             };
40010
40011             var label_text = {
40012                 tag: 'span',
40013                 html: this.fieldLabel
40014             };
40015
40016             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40017             label.cn = [
40018                 indicator,
40019                 label_text
40020             ];
40021
40022             if(this.indicatorpos == 'right') {
40023                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40024                 label.cn = [
40025                     label_text,
40026                     indicator
40027                 ];
40028             }
40029
40030             if(align == 'left') {
40031                 container = {
40032                     tag: 'div',
40033                     cn: [
40034                         container
40035                     ]
40036                 };
40037
40038                 if(this.labelWidth > 12){
40039                     label.style = "width: " + this.labelWidth + 'px';
40040                 }
40041                 if(this.labelWidth < 13 && this.labelmd == 0){
40042                     this.labelmd = this.labelWidth;
40043                 }
40044                 if(this.labellg > 0){
40045                     label.cls += ' col-lg-' + this.labellg;
40046                     input.cls += ' col-lg-' + (12 - this.labellg);
40047                 }
40048                 if(this.labelmd > 0){
40049                     label.cls += ' col-md-' + this.labelmd;
40050                     container.cls += ' col-md-' + (12 - this.labelmd);
40051                 }
40052                 if(this.labelsm > 0){
40053                     label.cls += ' col-sm-' + this.labelsm;
40054                     container.cls += ' col-sm-' + (12 - this.labelsm);
40055                 }
40056                 if(this.labelxs > 0){
40057                     label.cls += ' col-xs-' + this.labelxs;
40058                     container.cls += ' col-xs-' + (12 - this.labelxs);
40059                 }
40060             }
40061         }
40062
40063         cfg.cn = [
40064             label,
40065             container
40066         ];
40067
40068         var settings = this;
40069
40070         ['xs','sm','md','lg'].map(function(size){
40071             if (settings[size]) {
40072                 cfg.cls += ' col-' + size + '-' + settings[size];
40073             }
40074         });
40075         
40076         return cfg;
40077         
40078     },
40079     
40080     initEvents : function()
40081     {
40082         this.indicator = this.indicatorEl();
40083         
40084         this.initCurrencyEvent();
40085         
40086         this.initNumberEvent();
40087         
40088     },
40089     
40090     initCurrencyEvent : function()
40091     {
40092         if (!this.store) {
40093             throw "can not find store for combo";
40094         }
40095         
40096         this.store = Roo.factory(this.store, Roo.data);
40097         this.store.parent = this;
40098         
40099         this.createList();
40100         
40101         this.triggerEl = this.el.select('.input-group-addon', true).first();
40102         
40103         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40104         
40105         var _this = this;
40106         
40107         (function(){
40108             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40109             _this.list.setWidth(lw);
40110         }).defer(100);
40111         
40112         this.list.on('mouseover', this.onViewOver, this);
40113         this.list.on('mousemove', this.onViewMove, this);
40114         this.list.on('scroll', this.onViewScroll, this);
40115         
40116         if(!this.tpl){
40117             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40118         }
40119         
40120         this.view = new Roo.View(this.list, this.tpl, {
40121             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40122         });
40123         
40124         this.view.on('click', this.onViewClick, this);
40125         
40126         this.store.on('beforeload', this.onBeforeLoad, this);
40127         this.store.on('load', this.onLoad, this);
40128         this.store.on('loadexception', this.onLoadException, this);
40129         
40130         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40131             "up" : function(e){
40132                 this.inKeyMode = true;
40133                 this.selectPrev();
40134             },
40135
40136             "down" : function(e){
40137                 if(!this.isExpanded()){
40138                     this.onTriggerClick();
40139                 }else{
40140                     this.inKeyMode = true;
40141                     this.selectNext();
40142                 }
40143             },
40144
40145             "enter" : function(e){
40146                 this.collapse();
40147                 
40148                 if(this.fireEvent("specialkey", this, e)){
40149                     this.onViewClick(false);
40150                 }
40151                 
40152                 return true;
40153             },
40154
40155             "esc" : function(e){
40156                 this.collapse();
40157             },
40158
40159             "tab" : function(e){
40160                 this.collapse();
40161                 
40162                 if(this.fireEvent("specialkey", this, e)){
40163                     this.onViewClick(false);
40164                 }
40165                 
40166                 return true;
40167             },
40168
40169             scope : this,
40170
40171             doRelay : function(foo, bar, hname){
40172                 if(hname == 'down' || this.scope.isExpanded()){
40173                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40174                 }
40175                 return true;
40176             },
40177
40178             forceKeyDown: true
40179         });
40180         
40181         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40182         
40183     },
40184     
40185     initNumberEvent : function(e)
40186     {
40187         this.inputEl().on("keydown" , this.fireKey,  this);
40188         this.inputEl().on("focus", this.onFocus,  this);
40189         this.inputEl().on("blur", this.onBlur,  this);
40190         
40191         this.inputEl().relayEvent('keyup', this);
40192         
40193         if(this.indicator){
40194             this.indicator.addClass('invisible');
40195         }
40196  
40197         this.originalValue = this.getValue();
40198         
40199         if(this.validationEvent == 'keyup'){
40200             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40201             this.inputEl().on('keyup', this.filterValidation, this);
40202         }
40203         else if(this.validationEvent !== false){
40204             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40205         }
40206         
40207         if(this.selectOnFocus){
40208             this.on("focus", this.preFocus, this);
40209             
40210         }
40211         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40212             this.inputEl().on("keypress", this.filterKeys, this);
40213         } else {
40214             this.inputEl().relayEvent('keypress', this);
40215         }
40216         
40217         var allowed = "0123456789";
40218         
40219         if(this.allowDecimals){
40220             allowed += this.decimalSeparator;
40221         }
40222         
40223         if(this.allowNegative){
40224             allowed += "-";
40225         }
40226         
40227         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40228         
40229         var keyPress = function(e){
40230             
40231             var k = e.getKey();
40232             
40233             var c = e.getCharCode();
40234             
40235             if(
40236                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40237                     allowed.indexOf(String.fromCharCode(c)) === -1
40238             ){
40239                 e.stopEvent();
40240                 return;
40241             }
40242             
40243             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40244                 return;
40245             }
40246             
40247             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40248                 e.stopEvent();
40249             }
40250         };
40251         
40252         this.inputEl().on("keypress", keyPress, this);
40253         
40254     },
40255     
40256     onTriggerClick : function(e)
40257     {   
40258         if(this.disabled){
40259             return;
40260         }
40261         
40262         this.page = 0;
40263         this.loadNext = false;
40264         
40265         if(this.isExpanded()){
40266             this.collapse();
40267             return;
40268         }
40269         
40270         this.hasFocus = true;
40271         
40272         if(this.triggerAction == 'all') {
40273             this.doQuery(this.allQuery, true);
40274             return;
40275         }
40276         
40277         this.doQuery(this.getRawValue());
40278     },
40279     
40280     getCurrency : function()
40281     {   
40282         var v = this.currencyEl().getValue();
40283         
40284         return v;
40285     },
40286     
40287     restrictHeight : function()
40288     {
40289         this.list.alignTo(this.currencyEl(), this.listAlign);
40290         this.list.alignTo(this.currencyEl(), this.listAlign);
40291     },
40292     
40293     onViewClick : function(view, doFocus, el, e)
40294     {
40295         var index = this.view.getSelectedIndexes()[0];
40296         
40297         var r = this.store.getAt(index);
40298         
40299         if(r){
40300             this.onSelect(r, index);
40301         }
40302     },
40303     
40304     onSelect : function(record, index){
40305         
40306         if(this.fireEvent('beforeselect', this, record, index) !== false){
40307         
40308             this.setFromCurrencyData(index > -1 ? record.data : false);
40309             
40310             this.collapse();
40311             
40312             this.fireEvent('select', this, record, index);
40313         }
40314     },
40315     
40316     setFromCurrencyData : function(o)
40317     {
40318         var currency = '';
40319         
40320         this.lastCurrency = o;
40321         
40322         if (this.currencyField) {
40323             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40324         } else {
40325             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40326         }
40327         
40328         this.lastSelectionText = currency;
40329         
40330         this.setCurrency(currency);
40331     },
40332     
40333     setFromData : function(o)
40334     {
40335         var c = {};
40336         
40337         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40338         
40339         this.setFromCurrencyData(c);
40340         
40341         var value = '';
40342         
40343         if (this.name) {
40344             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40345         } else {
40346             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40347         }
40348         
40349         this.setValue(value);
40350         
40351     },
40352     
40353     setCurrency : function(v)
40354     {   
40355         this.currencyValue = v;
40356         
40357         if(this.rendered){
40358             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40359             this.validate();
40360         }
40361     },
40362     
40363     setValue : function(v)
40364     {
40365         v = this.fixPrecision(v);
40366         
40367         v = String(v).replace(".", this.decimalSeparator);
40368         
40369         this.value = v;
40370         
40371         if(this.rendered){
40372             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40373             this.validate();
40374         }
40375     },
40376     
40377     getRawValue : function()
40378     {
40379         var v = this.inputEl().getValue();
40380         
40381         return v;
40382     },
40383     
40384     getValue : function()
40385     {
40386         return this.fixPrecision(this.parseValue(this.getRawValue()));
40387     },
40388     
40389     parseValue : function(value)
40390     {
40391         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40392         return isNaN(value) ? '' : value;
40393     },
40394     
40395     fixPrecision : function(value)
40396     {
40397         var nan = isNaN(value);
40398         
40399         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40400             return nan ? '' : value;
40401         }
40402         
40403         return parseFloat(value).toFixed(this.decimalPrecision);
40404     },
40405     
40406     decimalPrecisionFcn : function(v)
40407     {
40408         return Math.floor(v);
40409     },
40410     
40411     validateValue : function(value)
40412     {
40413         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40414             return false;
40415         }
40416         
40417         var num = this.parseValue(value);
40418         
40419         if(isNaN(num)){
40420             this.markInvalid(String.format(this.nanText, value));
40421             return false;
40422         }
40423         
40424         if(num < this.minValue){
40425             this.markInvalid(String.format(this.minText, this.minValue));
40426             return false;
40427         }
40428         
40429         if(num > this.maxValue){
40430             this.markInvalid(String.format(this.maxText, this.maxValue));
40431             return false;
40432         }
40433         
40434         return true;
40435     },
40436     
40437     validate : function()
40438     {
40439         if(this.disabled || this.allowBlank){
40440             this.markValid();
40441             return true;
40442         }
40443         
40444         var currency = this.getCurrency();
40445         
40446         if(this.validateValue(this.getRawValue()) && currency.length){
40447             this.markValid();
40448             return true;
40449         }
40450         
40451         this.markInvalid();
40452         return false;
40453     },
40454     
40455     getName: function()
40456     {
40457         return this.name;
40458     },
40459     
40460     beforeBlur : function()
40461     {
40462         if(!this.castInt){
40463             return;
40464         }
40465         
40466         var v = this.parseValue(this.getRawValue());
40467         
40468         if(v){
40469             this.setValue(v);
40470         }
40471     },
40472     
40473     onBlur : function()
40474     {
40475         this.beforeBlur();
40476         
40477         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40478             //this.el.removeClass(this.focusClass);
40479         }
40480         
40481         this.hasFocus = false;
40482         
40483         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40484             this.validate();
40485         }
40486         
40487         var v = this.getValue();
40488         
40489         if(String(v) !== String(this.startValue)){
40490             this.fireEvent('change', this, v, this.startValue);
40491         }
40492         
40493         this.fireEvent("blur", this);
40494     },
40495     
40496     inputEl : function()
40497     {
40498         return this.el.select('.roo-money-amount-input', true).first();
40499     },
40500     
40501     currencyEl : function()
40502     {
40503         return this.el.select('.roo-money-currency-input', true).first();
40504     }
40505     
40506 });