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         this.fieldLabel = v;
9362         
9363         if(this.rendered){
9364             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9365         }
9366     }
9367 });
9368
9369  
9370 /*
9371  * - LGPL
9372  *
9373  * Input
9374  * 
9375  */
9376
9377 /**
9378  * @class Roo.bootstrap.TextArea
9379  * @extends Roo.bootstrap.Input
9380  * Bootstrap TextArea class
9381  * @cfg {Number} cols Specifies the visible width of a text area
9382  * @cfg {Number} rows Specifies the visible number of lines in a text area
9383  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9384  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9385  * @cfg {string} html text
9386  * 
9387  * @constructor
9388  * Create a new TextArea
9389  * @param {Object} config The config object
9390  */
9391
9392 Roo.bootstrap.TextArea = function(config){
9393     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9394    
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9398      
9399     cols : false,
9400     rows : 5,
9401     readOnly : false,
9402     warp : 'soft',
9403     resize : false,
9404     value: false,
9405     html: false,
9406     
9407     getAutoCreate : function(){
9408         
9409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9410         
9411         var id = Roo.id();
9412         
9413         var cfg = {};
9414         
9415         if(this.inputType != 'hidden'){
9416             cfg.cls = 'form-group' //input-group
9417         }
9418         
9419         var input =  {
9420             tag: 'textarea',
9421             id : id,
9422             warp : this.warp,
9423             rows : this.rows,
9424             value : this.value || '',
9425             html: this.html || '',
9426             cls : 'form-control',
9427             placeholder : this.placeholder || '' 
9428             
9429         };
9430         
9431         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9432             input.maxLength = this.maxLength;
9433         }
9434         
9435         if(this.resize){
9436             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9437         }
9438         
9439         if(this.cols){
9440             input.cols = this.cols;
9441         }
9442         
9443         if (this.readOnly) {
9444             input.readonly = true;
9445         }
9446         
9447         if (this.name) {
9448             input.name = this.name;
9449         }
9450         
9451         if (this.size) {
9452             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9453         }
9454         
9455         var settings=this;
9456         ['xs','sm','md','lg'].map(function(size){
9457             if (settings[size]) {
9458                 cfg.cls += ' col-' + size + '-' + settings[size];
9459             }
9460         });
9461         
9462         var inputblock = input;
9463         
9464         if(this.hasFeedback && !this.allowBlank){
9465             
9466             var feedback = {
9467                 tag: 'span',
9468                 cls: 'glyphicon form-control-feedback'
9469             };
9470
9471             inputblock = {
9472                 cls : 'has-feedback',
9473                 cn :  [
9474                     input,
9475                     feedback
9476                 ] 
9477             };  
9478         }
9479         
9480         
9481         if (this.before || this.after) {
9482             
9483             inputblock = {
9484                 cls : 'input-group',
9485                 cn :  [] 
9486             };
9487             if (this.before) {
9488                 inputblock.cn.push({
9489                     tag :'span',
9490                     cls : 'input-group-addon',
9491                     html : this.before
9492                 });
9493             }
9494             
9495             inputblock.cn.push(input);
9496             
9497             if(this.hasFeedback && !this.allowBlank){
9498                 inputblock.cls += ' has-feedback';
9499                 inputblock.cn.push(feedback);
9500             }
9501             
9502             if (this.after) {
9503                 inputblock.cn.push({
9504                     tag :'span',
9505                     cls : 'input-group-addon',
9506                     html : this.after
9507                 });
9508             }
9509             
9510         }
9511         
9512         if (align ==='left' && this.fieldLabel.length) {
9513             cfg.cn = [
9514                 {
9515                     tag: 'label',
9516                     'for' :  id,
9517                     cls : 'control-label',
9518                     html : this.fieldLabel
9519                 },
9520                 {
9521                     cls : "",
9522                     cn: [
9523                         inputblock
9524                     ]
9525                 }
9526
9527             ];
9528             
9529             if(this.labelWidth > 12){
9530                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9531             }
9532
9533             if(this.labelWidth < 13 && this.labelmd == 0){
9534                 this.labelmd = this.labelWidth;
9535             }
9536
9537             if(this.labellg > 0){
9538                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9539                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9540             }
9541
9542             if(this.labelmd > 0){
9543                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9544                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9545             }
9546
9547             if(this.labelsm > 0){
9548                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9549                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9550             }
9551
9552             if(this.labelxs > 0){
9553                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9554                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9555             }
9556             
9557         } else if ( this.fieldLabel.length) {
9558             cfg.cn = [
9559
9560                {
9561                    tag: 'label',
9562                    //cls : 'input-group-addon',
9563                    html : this.fieldLabel
9564
9565                },
9566
9567                inputblock
9568
9569            ];
9570
9571         } else {
9572
9573             cfg.cn = [
9574
9575                 inputblock
9576
9577             ];
9578                 
9579         }
9580         
9581         if (this.disabled) {
9582             input.disabled=true;
9583         }
9584         
9585         return cfg;
9586         
9587     },
9588     /**
9589      * return the real textarea element.
9590      */
9591     inputEl: function ()
9592     {
9593         return this.el.select('textarea.form-control',true).first();
9594     },
9595     
9596     /**
9597      * Clear any invalid styles/messages for this field
9598      */
9599     clearInvalid : function()
9600     {
9601         
9602         if(!this.el || this.preventMark){ // not rendered
9603             return;
9604         }
9605         
9606         var label = this.el.select('label', true).first();
9607         var icon = this.el.select('i.fa-star', true).first();
9608         
9609         if(label && icon){
9610             icon.remove();
9611         }
9612         
9613         this.el.removeClass(this.invalidClass);
9614         
9615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9616             
9617             var feedback = this.el.select('.form-control-feedback', true).first();
9618             
9619             if(feedback){
9620                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9621             }
9622             
9623         }
9624         
9625         this.fireEvent('valid', this);
9626     },
9627     
9628      /**
9629      * Mark this field as valid
9630      */
9631     markValid : function()
9632     {
9633         if(!this.el  || this.preventMark){ // not rendered
9634             return;
9635         }
9636         
9637         this.el.removeClass([this.invalidClass, this.validClass]);
9638         
9639         var feedback = this.el.select('.form-control-feedback', true).first();
9640             
9641         if(feedback){
9642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9643         }
9644
9645         if(this.disabled || this.allowBlank){
9646             return;
9647         }
9648         
9649         var label = this.el.select('label', true).first();
9650         var icon = this.el.select('i.fa-star', true).first();
9651         
9652         if(label && icon){
9653             icon.remove();
9654         }
9655         
9656         this.el.addClass(this.validClass);
9657         
9658         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9659             
9660             var feedback = this.el.select('.form-control-feedback', true).first();
9661             
9662             if(feedback){
9663                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9664                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9665             }
9666             
9667         }
9668         
9669         this.fireEvent('valid', this);
9670     },
9671     
9672      /**
9673      * Mark this field as invalid
9674      * @param {String} msg The validation message
9675      */
9676     markInvalid : function(msg)
9677     {
9678         if(!this.el  || this.preventMark){ // not rendered
9679             return;
9680         }
9681         
9682         this.el.removeClass([this.invalidClass, this.validClass]);
9683         
9684         var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686         if(feedback){
9687             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9688         }
9689
9690         if(this.disabled || this.allowBlank){
9691             return;
9692         }
9693         
9694         var label = this.el.select('label', true).first();
9695         var icon = this.el.select('i.fa-star', true).first();
9696         
9697         if(!this.getValue().length && label && !icon){
9698             this.el.createChild({
9699                 tag : 'i',
9700                 cls : 'text-danger fa fa-lg fa-star',
9701                 tooltip : 'This field is required',
9702                 style : 'margin-right:5px;'
9703             }, label, true);
9704         }
9705
9706         this.el.addClass(this.invalidClass);
9707         
9708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709             
9710             var feedback = this.el.select('.form-control-feedback', true).first();
9711             
9712             if(feedback){
9713                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9714                 
9715                 if(this.getValue().length || this.forceFeedback){
9716                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9717                 }
9718                 
9719             }
9720             
9721         }
9722         
9723         this.fireEvent('invalid', this, msg);
9724     }
9725 });
9726
9727  
9728 /*
9729  * - LGPL
9730  *
9731  * trigger field - base class for combo..
9732  * 
9733  */
9734  
9735 /**
9736  * @class Roo.bootstrap.TriggerField
9737  * @extends Roo.bootstrap.Input
9738  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9739  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9740  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9741  * for which you can provide a custom implementation.  For example:
9742  * <pre><code>
9743 var trigger = new Roo.bootstrap.TriggerField();
9744 trigger.onTriggerClick = myTriggerFn;
9745 trigger.applyTo('my-field');
9746 </code></pre>
9747  *
9748  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9749  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9750  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9751  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9752  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9753
9754  * @constructor
9755  * Create a new TriggerField.
9756  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9757  * to the base TextField)
9758  */
9759 Roo.bootstrap.TriggerField = function(config){
9760     this.mimicing = false;
9761     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9762 };
9763
9764 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9765     /**
9766      * @cfg {String} triggerClass A CSS class to apply to the trigger
9767      */
9768      /**
9769      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9770      */
9771     hideTrigger:false,
9772
9773     /**
9774      * @cfg {Boolean} removable (true|false) special filter default false
9775      */
9776     removable : false,
9777     
9778     /** @cfg {Boolean} grow @hide */
9779     /** @cfg {Number} growMin @hide */
9780     /** @cfg {Number} growMax @hide */
9781
9782     /**
9783      * @hide 
9784      * @method
9785      */
9786     autoSize: Roo.emptyFn,
9787     // private
9788     monitorTab : true,
9789     // private
9790     deferHeight : true,
9791
9792     
9793     actionMode : 'wrap',
9794     
9795     caret : false,
9796     
9797     
9798     getAutoCreate : function(){
9799        
9800         var align = this.labelAlign || this.parentLabelAlign();
9801         
9802         var id = Roo.id();
9803         
9804         var cfg = {
9805             cls: 'form-group' //input-group
9806         };
9807         
9808         
9809         var input =  {
9810             tag: 'input',
9811             id : id,
9812             type : this.inputType,
9813             cls : 'form-control',
9814             autocomplete: 'new-password',
9815             placeholder : this.placeholder || '' 
9816             
9817         };
9818         if (this.name) {
9819             input.name = this.name;
9820         }
9821         if (this.size) {
9822             input.cls += ' input-' + this.size;
9823         }
9824         
9825         if (this.disabled) {
9826             input.disabled=true;
9827         }
9828         
9829         var inputblock = input;
9830         
9831         if(this.hasFeedback && !this.allowBlank){
9832             
9833             var feedback = {
9834                 tag: 'span',
9835                 cls: 'glyphicon form-control-feedback'
9836             };
9837             
9838             if(this.removable && !this.editable && !this.tickable){
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         {
9844                             tag: 'button',
9845                             html : 'x',
9846                             cls : 'roo-combo-removable-btn close'
9847                         },
9848                         feedback
9849                     ] 
9850                 };
9851             } else {
9852                 inputblock = {
9853                     cls : 'has-feedback',
9854                     cn :  [
9855                         inputblock,
9856                         feedback
9857                     ] 
9858                 };
9859             }
9860
9861         } else {
9862             if(this.removable && !this.editable && !this.tickable){
9863                 inputblock = {
9864                     cls : 'roo-removable',
9865                     cn :  [
9866                         inputblock,
9867                         {
9868                             tag: 'button',
9869                             html : 'x',
9870                             cls : 'roo-combo-removable-btn close'
9871                         }
9872                     ] 
9873                 };
9874             }
9875         }
9876         
9877         if (this.before || this.after) {
9878             
9879             inputblock = {
9880                 cls : 'input-group',
9881                 cn :  [] 
9882             };
9883             if (this.before) {
9884                 inputblock.cn.push({
9885                     tag :'span',
9886                     cls : 'input-group-addon',
9887                     html : this.before
9888                 });
9889             }
9890             
9891             inputblock.cn.push(input);
9892             
9893             if(this.hasFeedback && !this.allowBlank){
9894                 inputblock.cls += ' has-feedback';
9895                 inputblock.cn.push(feedback);
9896             }
9897             
9898             if (this.after) {
9899                 inputblock.cn.push({
9900                     tag :'span',
9901                     cls : 'input-group-addon',
9902                     html : this.after
9903                 });
9904             }
9905             
9906         };
9907         
9908         var box = {
9909             tag: 'div',
9910             cn: [
9911                 {
9912                     tag: 'input',
9913                     type : 'hidden',
9914                     cls: 'form-hidden-field'
9915                 },
9916                 inputblock
9917             ]
9918             
9919         };
9920         
9921         if(this.multiple){
9922             box = {
9923                 tag: 'div',
9924                 cn: [
9925                     {
9926                         tag: 'input',
9927                         type : 'hidden',
9928                         cls: 'form-hidden-field'
9929                     },
9930                     {
9931                         tag: 'ul',
9932                         cls: 'roo-select2-choices',
9933                         cn:[
9934                             {
9935                                 tag: 'li',
9936                                 cls: 'roo-select2-search-field',
9937                                 cn: [
9938
9939                                     inputblock
9940                                 ]
9941                             }
9942                         ]
9943                     }
9944                 ]
9945             }
9946         };
9947         
9948         var combobox = {
9949             cls: 'roo-select2-container input-group',
9950             cn: [
9951                 box
9952 //                {
9953 //                    tag: 'ul',
9954 //                    cls: 'typeahead typeahead-long dropdown-menu',
9955 //                    style: 'display:none'
9956 //                }
9957             ]
9958         };
9959         
9960         if(!this.multiple && this.showToggleBtn){
9961             
9962             var caret = {
9963                         tag: 'span',
9964                         cls: 'caret'
9965              };
9966             if (this.caret != false) {
9967                 caret = {
9968                      tag: 'i',
9969                      cls: 'fa fa-' + this.caret
9970                 };
9971                 
9972             }
9973             
9974             combobox.cn.push({
9975                 tag :'span',
9976                 cls : 'input-group-addon btn dropdown-toggle',
9977                 cn : [
9978                     caret,
9979                     {
9980                         tag: 'span',
9981                         cls: 'combobox-clear',
9982                         cn  : [
9983                             {
9984                                 tag : 'i',
9985                                 cls: 'icon-remove'
9986                             }
9987                         ]
9988                     }
9989                 ]
9990
9991             })
9992         }
9993         
9994         if(this.multiple){
9995             combobox.cls += ' roo-select2-container-multi';
9996         }
9997         
9998         if (align ==='left' && this.fieldLabel.length) {
9999             
10000             cfg.cls += ' roo-form-group-label-left';
10001
10002             cfg.cn = [
10003                 {
10004                     tag : 'i',
10005                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10006                     tooltip : 'This field is required'
10007                 },
10008                 {
10009                     tag: 'label',
10010                     'for' :  id,
10011                     cls : 'control-label',
10012                     html : this.fieldLabel
10013
10014                 },
10015                 {
10016                     cls : "", 
10017                     cn: [
10018                         combobox
10019                     ]
10020                 }
10021
10022             ];
10023             
10024             var labelCfg = cfg.cn[1];
10025             var contentCfg = cfg.cn[2];
10026             
10027             if(this.indicatorpos == 'right'){
10028                 cfg.cn = [
10029                     {
10030                         tag: 'label',
10031                         'for' :  id,
10032                         cls : 'control-label',
10033                         cn : [
10034                             {
10035                                 tag : 'span',
10036                                 html : this.fieldLabel
10037                             },
10038                             {
10039                                 tag : 'i',
10040                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10041                                 tooltip : 'This field is required'
10042                             }
10043                         ]
10044                     },
10045                     {
10046                         cls : "", 
10047                         cn: [
10048                             combobox
10049                         ]
10050                     }
10051
10052                 ];
10053                 
10054                 labelCfg = cfg.cn[0];
10055                 contentCfg = cfg.cn[1];
10056             }
10057             
10058             if(this.labelWidth > 12){
10059                 labelCfg.style = "width: " + this.labelWidth + 'px';
10060             }
10061             
10062             if(this.labelWidth < 13 && this.labelmd == 0){
10063                 this.labelmd = this.labelWidth;
10064             }
10065             
10066             if(this.labellg > 0){
10067                 labelCfg.cls += ' col-lg-' + this.labellg;
10068                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10069             }
10070             
10071             if(this.labelmd > 0){
10072                 labelCfg.cls += ' col-md-' + this.labelmd;
10073                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10074             }
10075             
10076             if(this.labelsm > 0){
10077                 labelCfg.cls += ' col-sm-' + this.labelsm;
10078                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10079             }
10080             
10081             if(this.labelxs > 0){
10082                 labelCfg.cls += ' col-xs-' + this.labelxs;
10083                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10084             }
10085             
10086         } else if ( this.fieldLabel.length) {
10087 //                Roo.log(" label");
10088             cfg.cn = [
10089                 {
10090                    tag : 'i',
10091                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10092                    tooltip : 'This field is required'
10093                },
10094                {
10095                    tag: 'label',
10096                    //cls : 'input-group-addon',
10097                    html : this.fieldLabel
10098
10099                },
10100
10101                combobox
10102
10103             ];
10104             
10105             if(this.indicatorpos == 'right'){
10106                 
10107                 cfg.cn = [
10108                     {
10109                        tag: 'label',
10110                        cn : [
10111                            {
10112                                tag : 'span',
10113                                html : this.fieldLabel
10114                            },
10115                            {
10116                               tag : 'i',
10117                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10118                               tooltip : 'This field is required'
10119                            }
10120                        ]
10121
10122                     },
10123                     combobox
10124
10125                 ];
10126
10127             }
10128
10129         } else {
10130             
10131 //                Roo.log(" no label && no align");
10132                 cfg = combobox
10133                      
10134                 
10135         }
10136         
10137         var settings=this;
10138         ['xs','sm','md','lg'].map(function(size){
10139             if (settings[size]) {
10140                 cfg.cls += ' col-' + size + '-' + settings[size];
10141             }
10142         });
10143         
10144         return cfg;
10145         
10146     },
10147     
10148     
10149     
10150     // private
10151     onResize : function(w, h){
10152 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10153 //        if(typeof w == 'number'){
10154 //            var x = w - this.trigger.getWidth();
10155 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10156 //            this.trigger.setStyle('left', x+'px');
10157 //        }
10158     },
10159
10160     // private
10161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10162
10163     // private
10164     getResizeEl : function(){
10165         return this.inputEl();
10166     },
10167
10168     // private
10169     getPositionEl : function(){
10170         return this.inputEl();
10171     },
10172
10173     // private
10174     alignErrorIcon : function(){
10175         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10176     },
10177
10178     // private
10179     initEvents : function(){
10180         
10181         this.createList();
10182         
10183         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10184         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10185         if(!this.multiple && this.showToggleBtn){
10186             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10187             if(this.hideTrigger){
10188                 this.trigger.setDisplayed(false);
10189             }
10190             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10191         }
10192         
10193         if(this.multiple){
10194             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10195         }
10196         
10197         if(this.removable && !this.editable && !this.tickable){
10198             var close = this.closeTriggerEl();
10199             
10200             if(close){
10201                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10202                 close.on('click', this.removeBtnClick, this, close);
10203             }
10204         }
10205         
10206         //this.trigger.addClassOnOver('x-form-trigger-over');
10207         //this.trigger.addClassOnClick('x-form-trigger-click');
10208         
10209         //if(!this.width){
10210         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10211         //}
10212     },
10213     
10214     closeTriggerEl : function()
10215     {
10216         var close = this.el.select('.roo-combo-removable-btn', true).first();
10217         return close ? close : false;
10218     },
10219     
10220     removeBtnClick : function(e, h, el)
10221     {
10222         e.preventDefault();
10223         
10224         if(this.fireEvent("remove", this) !== false){
10225             this.reset();
10226             this.fireEvent("afterremove", this)
10227         }
10228     },
10229     
10230     createList : function()
10231     {
10232         this.list = Roo.get(document.body).createChild({
10233             tag: 'ul',
10234             cls: 'typeahead typeahead-long dropdown-menu',
10235             style: 'display:none'
10236         });
10237         
10238         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10239         
10240     },
10241
10242     // private
10243     initTrigger : function(){
10244        
10245     },
10246
10247     // private
10248     onDestroy : function(){
10249         if(this.trigger){
10250             this.trigger.removeAllListeners();
10251           //  this.trigger.remove();
10252         }
10253         //if(this.wrap){
10254         //    this.wrap.remove();
10255         //}
10256         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10257     },
10258
10259     // private
10260     onFocus : function(){
10261         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10262         /*
10263         if(!this.mimicing){
10264             this.wrap.addClass('x-trigger-wrap-focus');
10265             this.mimicing = true;
10266             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10267             if(this.monitorTab){
10268                 this.el.on("keydown", this.checkTab, this);
10269             }
10270         }
10271         */
10272     },
10273
10274     // private
10275     checkTab : function(e){
10276         if(e.getKey() == e.TAB){
10277             this.triggerBlur();
10278         }
10279     },
10280
10281     // private
10282     onBlur : function(){
10283         // do nothing
10284     },
10285
10286     // private
10287     mimicBlur : function(e, t){
10288         /*
10289         if(!this.wrap.contains(t) && this.validateBlur()){
10290             this.triggerBlur();
10291         }
10292         */
10293     },
10294
10295     // private
10296     triggerBlur : function(){
10297         this.mimicing = false;
10298         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10299         if(this.monitorTab){
10300             this.el.un("keydown", this.checkTab, this);
10301         }
10302         //this.wrap.removeClass('x-trigger-wrap-focus');
10303         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10304     },
10305
10306     // private
10307     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10308     validateBlur : function(e, t){
10309         return true;
10310     },
10311
10312     // private
10313     onDisable : function(){
10314         this.inputEl().dom.disabled = true;
10315         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10316         //if(this.wrap){
10317         //    this.wrap.addClass('x-item-disabled');
10318         //}
10319     },
10320
10321     // private
10322     onEnable : function(){
10323         this.inputEl().dom.disabled = false;
10324         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10325         //if(this.wrap){
10326         //    this.el.removeClass('x-item-disabled');
10327         //}
10328     },
10329
10330     // private
10331     onShow : function(){
10332         var ae = this.getActionEl();
10333         
10334         if(ae){
10335             ae.dom.style.display = '';
10336             ae.dom.style.visibility = 'visible';
10337         }
10338     },
10339
10340     // private
10341     
10342     onHide : function(){
10343         var ae = this.getActionEl();
10344         ae.dom.style.display = 'none';
10345     },
10346
10347     /**
10348      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10349      * by an implementing function.
10350      * @method
10351      * @param {EventObject} e
10352      */
10353     onTriggerClick : Roo.emptyFn
10354 });
10355  /*
10356  * Based on:
10357  * Ext JS Library 1.1.1
10358  * Copyright(c) 2006-2007, Ext JS, LLC.
10359  *
10360  * Originally Released Under LGPL - original licence link has changed is not relivant.
10361  *
10362  * Fork - LGPL
10363  * <script type="text/javascript">
10364  */
10365
10366
10367 /**
10368  * @class Roo.data.SortTypes
10369  * @singleton
10370  * Defines the default sorting (casting?) comparison functions used when sorting data.
10371  */
10372 Roo.data.SortTypes = {
10373     /**
10374      * Default sort that does nothing
10375      * @param {Mixed} s The value being converted
10376      * @return {Mixed} The comparison value
10377      */
10378     none : function(s){
10379         return s;
10380     },
10381     
10382     /**
10383      * The regular expression used to strip tags
10384      * @type {RegExp}
10385      * @property
10386      */
10387     stripTagsRE : /<\/?[^>]+>/gi,
10388     
10389     /**
10390      * Strips all HTML tags to sort on text only
10391      * @param {Mixed} s The value being converted
10392      * @return {String} The comparison value
10393      */
10394     asText : function(s){
10395         return String(s).replace(this.stripTagsRE, "");
10396     },
10397     
10398     /**
10399      * Strips all HTML tags to sort on text only - Case insensitive
10400      * @param {Mixed} s The value being converted
10401      * @return {String} The comparison value
10402      */
10403     asUCText : function(s){
10404         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10405     },
10406     
10407     /**
10408      * Case insensitive string
10409      * @param {Mixed} s The value being converted
10410      * @return {String} The comparison value
10411      */
10412     asUCString : function(s) {
10413         return String(s).toUpperCase();
10414     },
10415     
10416     /**
10417      * Date sorting
10418      * @param {Mixed} s The value being converted
10419      * @return {Number} The comparison value
10420      */
10421     asDate : function(s) {
10422         if(!s){
10423             return 0;
10424         }
10425         if(s instanceof Date){
10426             return s.getTime();
10427         }
10428         return Date.parse(String(s));
10429     },
10430     
10431     /**
10432      * Float sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Float} The comparison value
10435      */
10436     asFloat : function(s) {
10437         var val = parseFloat(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     },
10443     
10444     /**
10445      * Integer sorting
10446      * @param {Mixed} s The value being converted
10447      * @return {Number} The comparison value
10448      */
10449     asInt : function(s) {
10450         var val = parseInt(String(s).replace(/,/g, ""));
10451         if(isNaN(val)) {
10452             val = 0;
10453         }
10454         return val;
10455     }
10456 };/*
10457  * Based on:
10458  * Ext JS Library 1.1.1
10459  * Copyright(c) 2006-2007, Ext JS, LLC.
10460  *
10461  * Originally Released Under LGPL - original licence link has changed is not relivant.
10462  *
10463  * Fork - LGPL
10464  * <script type="text/javascript">
10465  */
10466
10467 /**
10468 * @class Roo.data.Record
10469  * Instances of this class encapsulate both record <em>definition</em> information, and record
10470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10471  * to access Records cached in an {@link Roo.data.Store} object.<br>
10472  * <p>
10473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10475  * objects.<br>
10476  * <p>
10477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10478  * @constructor
10479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10480  * {@link #create}. The parameters are the same.
10481  * @param {Array} data An associative Array of data values keyed by the field name.
10482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10484  * not specified an integer id is generated.
10485  */
10486 Roo.data.Record = function(data, id){
10487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10488     this.data = data;
10489 };
10490
10491 /**
10492  * Generate a constructor for a specific record layout.
10493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10495  * Each field definition object may contain the following properties: <ul>
10496  * <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,
10497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10503  * this may be omitted.</p></li>
10504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10505  * <ul><li>auto (Default, implies no conversion)</li>
10506  * <li>string</li>
10507  * <li>int</li>
10508  * <li>float</li>
10509  * <li>boolean</li>
10510  * <li>date</li></ul></p></li>
10511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10514  * by the Reader into an object that will be stored in the Record. It is passed the
10515  * following parameters:<ul>
10516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10517  * </ul></p></li>
10518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10519  * </ul>
10520  * <br>usage:<br><pre><code>
10521 var TopicRecord = Roo.data.Record.create(
10522     {name: 'title', mapping: 'topic_title'},
10523     {name: 'author', mapping: 'username'},
10524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10526     {name: 'lastPoster', mapping: 'user2'},
10527     {name: 'excerpt', mapping: 'post_text'}
10528 );
10529
10530 var myNewRecord = new TopicRecord({
10531     title: 'Do my job please',
10532     author: 'noobie',
10533     totalPosts: 1,
10534     lastPost: new Date(),
10535     lastPoster: 'Animal',
10536     excerpt: 'No way dude!'
10537 });
10538 myStore.add(myNewRecord);
10539 </code></pre>
10540  * @method create
10541  * @static
10542  */
10543 Roo.data.Record.create = function(o){
10544     var f = function(){
10545         f.superclass.constructor.apply(this, arguments);
10546     };
10547     Roo.extend(f, Roo.data.Record);
10548     var p = f.prototype;
10549     p.fields = new Roo.util.MixedCollection(false, function(field){
10550         return field.name;
10551     });
10552     for(var i = 0, len = o.length; i < len; i++){
10553         p.fields.add(new Roo.data.Field(o[i]));
10554     }
10555     f.getField = function(name){
10556         return p.fields.get(name);  
10557     };
10558     return f;
10559 };
10560
10561 Roo.data.Record.AUTO_ID = 1000;
10562 Roo.data.Record.EDIT = 'edit';
10563 Roo.data.Record.REJECT = 'reject';
10564 Roo.data.Record.COMMIT = 'commit';
10565
10566 Roo.data.Record.prototype = {
10567     /**
10568      * Readonly flag - true if this record has been modified.
10569      * @type Boolean
10570      */
10571     dirty : false,
10572     editing : false,
10573     error: null,
10574     modified: null,
10575
10576     // private
10577     join : function(store){
10578         this.store = store;
10579     },
10580
10581     /**
10582      * Set the named field to the specified value.
10583      * @param {String} name The name of the field to set.
10584      * @param {Object} value The value to set the field to.
10585      */
10586     set : function(name, value){
10587         if(this.data[name] == value){
10588             return;
10589         }
10590         this.dirty = true;
10591         if(!this.modified){
10592             this.modified = {};
10593         }
10594         if(typeof this.modified[name] == 'undefined'){
10595             this.modified[name] = this.data[name];
10596         }
10597         this.data[name] = value;
10598         if(!this.editing && this.store){
10599             this.store.afterEdit(this);
10600         }       
10601     },
10602
10603     /**
10604      * Get the value of the named field.
10605      * @param {String} name The name of the field to get the value of.
10606      * @return {Object} The value of the field.
10607      */
10608     get : function(name){
10609         return this.data[name]; 
10610     },
10611
10612     // private
10613     beginEdit : function(){
10614         this.editing = true;
10615         this.modified = {}; 
10616     },
10617
10618     // private
10619     cancelEdit : function(){
10620         this.editing = false;
10621         delete this.modified;
10622     },
10623
10624     // private
10625     endEdit : function(){
10626         this.editing = false;
10627         if(this.dirty && this.store){
10628             this.store.afterEdit(this);
10629         }
10630     },
10631
10632     /**
10633      * Usually called by the {@link Roo.data.Store} which owns the Record.
10634      * Rejects all changes made to the Record since either creation, or the last commit operation.
10635      * Modified fields are reverted to their original values.
10636      * <p>
10637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10638      * of reject operations.
10639      */
10640     reject : function(){
10641         var m = this.modified;
10642         for(var n in m){
10643             if(typeof m[n] != "function"){
10644                 this.data[n] = m[n];
10645             }
10646         }
10647         this.dirty = false;
10648         delete this.modified;
10649         this.editing = false;
10650         if(this.store){
10651             this.store.afterReject(this);
10652         }
10653     },
10654
10655     /**
10656      * Usually called by the {@link Roo.data.Store} which owns the Record.
10657      * Commits all changes made to the Record since either creation, or the last commit operation.
10658      * <p>
10659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10660      * of commit operations.
10661      */
10662     commit : function(){
10663         this.dirty = false;
10664         delete this.modified;
10665         this.editing = false;
10666         if(this.store){
10667             this.store.afterCommit(this);
10668         }
10669     },
10670
10671     // private
10672     hasError : function(){
10673         return this.error != null;
10674     },
10675
10676     // private
10677     clearError : function(){
10678         this.error = null;
10679     },
10680
10681     /**
10682      * Creates a copy of this record.
10683      * @param {String} id (optional) A new record id if you don't want to use this record's id
10684      * @return {Record}
10685      */
10686     copy : function(newId) {
10687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10688     }
10689 };/*
10690  * Based on:
10691  * Ext JS Library 1.1.1
10692  * Copyright(c) 2006-2007, Ext JS, LLC.
10693  *
10694  * Originally Released Under LGPL - original licence link has changed is not relivant.
10695  *
10696  * Fork - LGPL
10697  * <script type="text/javascript">
10698  */
10699
10700
10701
10702 /**
10703  * @class Roo.data.Store
10704  * @extends Roo.util.Observable
10705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10707  * <p>
10708  * 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
10709  * has no knowledge of the format of the data returned by the Proxy.<br>
10710  * <p>
10711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10712  * instances from the data object. These records are cached and made available through accessor functions.
10713  * @constructor
10714  * Creates a new Store.
10715  * @param {Object} config A config object containing the objects needed for the Store to access data,
10716  * and read the data into Records.
10717  */
10718 Roo.data.Store = function(config){
10719     this.data = new Roo.util.MixedCollection(false);
10720     this.data.getKey = function(o){
10721         return o.id;
10722     };
10723     this.baseParams = {};
10724     // private
10725     this.paramNames = {
10726         "start" : "start",
10727         "limit" : "limit",
10728         "sort" : "sort",
10729         "dir" : "dir",
10730         "multisort" : "_multisort"
10731     };
10732
10733     if(config && config.data){
10734         this.inlineData = config.data;
10735         delete config.data;
10736     }
10737
10738     Roo.apply(this, config);
10739     
10740     if(this.reader){ // reader passed
10741         this.reader = Roo.factory(this.reader, Roo.data);
10742         this.reader.xmodule = this.xmodule || false;
10743         if(!this.recordType){
10744             this.recordType = this.reader.recordType;
10745         }
10746         if(this.reader.onMetaChange){
10747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10748         }
10749     }
10750
10751     if(this.recordType){
10752         this.fields = this.recordType.prototype.fields;
10753     }
10754     this.modified = [];
10755
10756     this.addEvents({
10757         /**
10758          * @event datachanged
10759          * Fires when the data cache has changed, and a widget which is using this Store
10760          * as a Record cache should refresh its view.
10761          * @param {Store} this
10762          */
10763         datachanged : true,
10764         /**
10765          * @event metachange
10766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10767          * @param {Store} this
10768          * @param {Object} meta The JSON metadata
10769          */
10770         metachange : true,
10771         /**
10772          * @event add
10773          * Fires when Records have been added to the Store
10774          * @param {Store} this
10775          * @param {Roo.data.Record[]} records The array of Records added
10776          * @param {Number} index The index at which the record(s) were added
10777          */
10778         add : true,
10779         /**
10780          * @event remove
10781          * Fires when a Record has been removed from the Store
10782          * @param {Store} this
10783          * @param {Roo.data.Record} record The Record that was removed
10784          * @param {Number} index The index at which the record was removed
10785          */
10786         remove : true,
10787         /**
10788          * @event update
10789          * Fires when a Record has been updated
10790          * @param {Store} this
10791          * @param {Roo.data.Record} record The Record that was updated
10792          * @param {String} operation The update operation being performed.  Value may be one of:
10793          * <pre><code>
10794  Roo.data.Record.EDIT
10795  Roo.data.Record.REJECT
10796  Roo.data.Record.COMMIT
10797          * </code></pre>
10798          */
10799         update : true,
10800         /**
10801          * @event clear
10802          * Fires when the data cache has been cleared.
10803          * @param {Store} this
10804          */
10805         clear : true,
10806         /**
10807          * @event beforeload
10808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10809          * the load action will be canceled.
10810          * @param {Store} this
10811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10812          */
10813         beforeload : true,
10814         /**
10815          * @event beforeloadadd
10816          * Fires after a new set of Records has been loaded.
10817          * @param {Store} this
10818          * @param {Roo.data.Record[]} records The Records that were loaded
10819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10820          */
10821         beforeloadadd : true,
10822         /**
10823          * @event load
10824          * Fires after a new set of Records has been loaded, before they are added to the store.
10825          * @param {Store} this
10826          * @param {Roo.data.Record[]} records The Records that were loaded
10827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10828          * @params {Object} return from reader
10829          */
10830         load : true,
10831         /**
10832          * @event loadexception
10833          * Fires if an exception occurs in the Proxy during loading.
10834          * Called with the signature of the Proxy's "loadexception" event.
10835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10836          * 
10837          * @param {Proxy} 
10838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10839          * @param {Object} load options 
10840          * @param {Object} jsonData from your request (normally this contains the Exception)
10841          */
10842         loadexception : true
10843     });
10844     
10845     if(this.proxy){
10846         this.proxy = Roo.factory(this.proxy, Roo.data);
10847         this.proxy.xmodule = this.xmodule || false;
10848         this.relayEvents(this.proxy,  ["loadexception"]);
10849     }
10850     this.sortToggle = {};
10851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10852
10853     Roo.data.Store.superclass.constructor.call(this);
10854
10855     if(this.inlineData){
10856         this.loadData(this.inlineData);
10857         delete this.inlineData;
10858     }
10859 };
10860
10861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10862      /**
10863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10864     * without a remote query - used by combo/forms at present.
10865     */
10866     
10867     /**
10868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10869     */
10870     /**
10871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10872     */
10873     /**
10874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10876     */
10877     /**
10878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10879     * on any HTTP request
10880     */
10881     /**
10882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10883     */
10884     /**
10885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10886     */
10887     multiSort: false,
10888     /**
10889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10891     */
10892     remoteSort : false,
10893
10894     /**
10895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10896      * loaded or when a record is removed. (defaults to false).
10897     */
10898     pruneModifiedRecords : false,
10899
10900     // private
10901     lastOptions : null,
10902
10903     /**
10904      * Add Records to the Store and fires the add event.
10905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10906      */
10907     add : function(records){
10908         records = [].concat(records);
10909         for(var i = 0, len = records.length; i < len; i++){
10910             records[i].join(this);
10911         }
10912         var index = this.data.length;
10913         this.data.addAll(records);
10914         this.fireEvent("add", this, records, index);
10915     },
10916
10917     /**
10918      * Remove a Record from the Store and fires the remove event.
10919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10920      */
10921     remove : function(record){
10922         var index = this.data.indexOf(record);
10923         this.data.removeAt(index);
10924         if(this.pruneModifiedRecords){
10925             this.modified.remove(record);
10926         }
10927         this.fireEvent("remove", this, record, index);
10928     },
10929
10930     /**
10931      * Remove all Records from the Store and fires the clear event.
10932      */
10933     removeAll : function(){
10934         this.data.clear();
10935         if(this.pruneModifiedRecords){
10936             this.modified = [];
10937         }
10938         this.fireEvent("clear", this);
10939     },
10940
10941     /**
10942      * Inserts Records to the Store at the given index and fires the add event.
10943      * @param {Number} index The start index at which to insert the passed Records.
10944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10945      */
10946     insert : function(index, records){
10947         records = [].concat(records);
10948         for(var i = 0, len = records.length; i < len; i++){
10949             this.data.insert(index, records[i]);
10950             records[i].join(this);
10951         }
10952         this.fireEvent("add", this, records, index);
10953     },
10954
10955     /**
10956      * Get the index within the cache of the passed Record.
10957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10958      * @return {Number} The index of the passed Record. Returns -1 if not found.
10959      */
10960     indexOf : function(record){
10961         return this.data.indexOf(record);
10962     },
10963
10964     /**
10965      * Get the index within the cache of the Record with the passed id.
10966      * @param {String} id The id of the Record to find.
10967      * @return {Number} The index of the Record. Returns -1 if not found.
10968      */
10969     indexOfId : function(id){
10970         return this.data.indexOfKey(id);
10971     },
10972
10973     /**
10974      * Get the Record with the specified id.
10975      * @param {String} id The id of the Record to find.
10976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10977      */
10978     getById : function(id){
10979         return this.data.key(id);
10980     },
10981
10982     /**
10983      * Get the Record at the specified index.
10984      * @param {Number} index The index of the Record to find.
10985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10986      */
10987     getAt : function(index){
10988         return this.data.itemAt(index);
10989     },
10990
10991     /**
10992      * Returns a range of Records between specified indices.
10993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10995      * @return {Roo.data.Record[]} An array of Records
10996      */
10997     getRange : function(start, end){
10998         return this.data.getRange(start, end);
10999     },
11000
11001     // private
11002     storeOptions : function(o){
11003         o = Roo.apply({}, o);
11004         delete o.callback;
11005         delete o.scope;
11006         this.lastOptions = o;
11007     },
11008
11009     /**
11010      * Loads the Record cache from the configured Proxy using the configured Reader.
11011      * <p>
11012      * If using remote paging, then the first load call must specify the <em>start</em>
11013      * and <em>limit</em> properties in the options.params property to establish the initial
11014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11015      * <p>
11016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11017      * and this call will return before the new data has been loaded. Perform any post-processing
11018      * in a callback function, or in a "load" event handler.</strong>
11019      * <p>
11020      * @param {Object} options An object containing properties which control loading options:<ul>
11021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11023      * passed the following arguments:<ul>
11024      * <li>r : Roo.data.Record[]</li>
11025      * <li>options: Options object from the load call</li>
11026      * <li>success: Boolean success indicator</li></ul></li>
11027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11029      * </ul>
11030      */
11031     load : function(options){
11032         options = options || {};
11033         if(this.fireEvent("beforeload", this, options) !== false){
11034             this.storeOptions(options);
11035             var p = Roo.apply(options.params || {}, this.baseParams);
11036             // if meta was not loaded from remote source.. try requesting it.
11037             if (!this.reader.metaFromRemote) {
11038                 p._requestMeta = 1;
11039             }
11040             if(this.sortInfo && this.remoteSort){
11041                 var pn = this.paramNames;
11042                 p[pn["sort"]] = this.sortInfo.field;
11043                 p[pn["dir"]] = this.sortInfo.direction;
11044             }
11045             if (this.multiSort) {
11046                 var pn = this.paramNames;
11047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11048             }
11049             
11050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11051         }
11052     },
11053
11054     /**
11055      * Reloads the Record cache from the configured Proxy using the configured Reader and
11056      * the options from the last load operation performed.
11057      * @param {Object} options (optional) An object containing properties which may override the options
11058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11059      * the most recently used options are reused).
11060      */
11061     reload : function(options){
11062         this.load(Roo.applyIf(options||{}, this.lastOptions));
11063     },
11064
11065     // private
11066     // Called as a callback by the Reader during a load operation.
11067     loadRecords : function(o, options, success){
11068         if(!o || success === false){
11069             if(success !== false){
11070                 this.fireEvent("load", this, [], options, o);
11071             }
11072             if(options.callback){
11073                 options.callback.call(options.scope || this, [], options, false);
11074             }
11075             return;
11076         }
11077         // if data returned failure - throw an exception.
11078         if (o.success === false) {
11079             // show a message if no listener is registered.
11080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11082             }
11083             // loadmask wil be hooked into this..
11084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11085             return;
11086         }
11087         var r = o.records, t = o.totalRecords || r.length;
11088         
11089         this.fireEvent("beforeloadadd", this, r, options, o);
11090         
11091         if(!options || options.add !== true){
11092             if(this.pruneModifiedRecords){
11093                 this.modified = [];
11094             }
11095             for(var i = 0, len = r.length; i < len; i++){
11096                 r[i].join(this);
11097             }
11098             if(this.snapshot){
11099                 this.data = this.snapshot;
11100                 delete this.snapshot;
11101             }
11102             this.data.clear();
11103             this.data.addAll(r);
11104             this.totalLength = t;
11105             this.applySort();
11106             this.fireEvent("datachanged", this);
11107         }else{
11108             this.totalLength = Math.max(t, this.data.length+r.length);
11109             this.add(r);
11110         }
11111         
11112         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11113                 
11114             var e = new Roo.data.Record({});
11115
11116             e.set(this.parent.displayField, this.parent.emptyTitle);
11117             e.set(this.parent.valueField, '');
11118
11119             this.insert(0, e);
11120         }
11121             
11122         this.fireEvent("load", this, r, options, o);
11123         if(options.callback){
11124             options.callback.call(options.scope || this, r, options, true);
11125         }
11126     },
11127
11128
11129     /**
11130      * Loads data from a passed data block. A Reader which understands the format of the data
11131      * must have been configured in the constructor.
11132      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11133      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11134      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11135      */
11136     loadData : function(o, append){
11137         var r = this.reader.readRecords(o);
11138         this.loadRecords(r, {add: append}, true);
11139     },
11140
11141     /**
11142      * Gets the number of cached records.
11143      * <p>
11144      * <em>If using paging, this may not be the total size of the dataset. If the data object
11145      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11146      * the data set size</em>
11147      */
11148     getCount : function(){
11149         return this.data.length || 0;
11150     },
11151
11152     /**
11153      * Gets the total number of records in the dataset as returned by the server.
11154      * <p>
11155      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11156      * the dataset size</em>
11157      */
11158     getTotalCount : function(){
11159         return this.totalLength || 0;
11160     },
11161
11162     /**
11163      * Returns the sort state of the Store as an object with two properties:
11164      * <pre><code>
11165  field {String} The name of the field by which the Records are sorted
11166  direction {String} The sort order, "ASC" or "DESC"
11167      * </code></pre>
11168      */
11169     getSortState : function(){
11170         return this.sortInfo;
11171     },
11172
11173     // private
11174     applySort : function(){
11175         if(this.sortInfo && !this.remoteSort){
11176             var s = this.sortInfo, f = s.field;
11177             var st = this.fields.get(f).sortType;
11178             var fn = function(r1, r2){
11179                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11180                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11181             };
11182             this.data.sort(s.direction, fn);
11183             if(this.snapshot && this.snapshot != this.data){
11184                 this.snapshot.sort(s.direction, fn);
11185             }
11186         }
11187     },
11188
11189     /**
11190      * Sets the default sort column and order to be used by the next load operation.
11191      * @param {String} fieldName The name of the field to sort by.
11192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193      */
11194     setDefaultSort : function(field, dir){
11195         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11196     },
11197
11198     /**
11199      * Sort the Records.
11200      * If remote sorting is used, the sort is performed on the server, and the cache is
11201      * reloaded. If local sorting is used, the cache is sorted internally.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     sort : function(fieldName, dir){
11206         var f = this.fields.get(fieldName);
11207         if(!dir){
11208             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11209             
11210             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11211                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11212             }else{
11213                 dir = f.sortDir;
11214             }
11215         }
11216         this.sortToggle[f.name] = dir;
11217         this.sortInfo = {field: f.name, direction: dir};
11218         if(!this.remoteSort){
11219             this.applySort();
11220             this.fireEvent("datachanged", this);
11221         }else{
11222             this.load(this.lastOptions);
11223         }
11224     },
11225
11226     /**
11227      * Calls the specified function for each of the Records in the cache.
11228      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11229      * Returning <em>false</em> aborts and exits the iteration.
11230      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11231      */
11232     each : function(fn, scope){
11233         this.data.each(fn, scope);
11234     },
11235
11236     /**
11237      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11238      * (e.g., during paging).
11239      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11240      */
11241     getModifiedRecords : function(){
11242         return this.modified;
11243     },
11244
11245     // private
11246     createFilterFn : function(property, value, anyMatch){
11247         if(!value.exec){ // not a regex
11248             value = String(value);
11249             if(value.length == 0){
11250                 return false;
11251             }
11252             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11253         }
11254         return function(r){
11255             return value.test(r.data[property]);
11256         };
11257     },
11258
11259     /**
11260      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11261      * @param {String} property A field on your records
11262      * @param {Number} start The record index to start at (defaults to 0)
11263      * @param {Number} end The last record index to include (defaults to length - 1)
11264      * @return {Number} The sum
11265      */
11266     sum : function(property, start, end){
11267         var rs = this.data.items, v = 0;
11268         start = start || 0;
11269         end = (end || end === 0) ? end : rs.length-1;
11270
11271         for(var i = start; i <= end; i++){
11272             v += (rs[i].data[property] || 0);
11273         }
11274         return v;
11275     },
11276
11277     /**
11278      * Filter the records by a specified property.
11279      * @param {String} field A field on your records
11280      * @param {String/RegExp} value Either a string that the field
11281      * should start with or a RegExp to test against the field
11282      * @param {Boolean} anyMatch True to match any part not just the beginning
11283      */
11284     filter : function(property, value, anyMatch){
11285         var fn = this.createFilterFn(property, value, anyMatch);
11286         return fn ? this.filterBy(fn) : this.clearFilter();
11287     },
11288
11289     /**
11290      * Filter by a function. The specified function will be called with each
11291      * record in this data source. If the function returns true the record is included,
11292      * otherwise it is filtered.
11293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11294      * @param {Object} scope (optional) The scope of the function (defaults to this)
11295      */
11296     filterBy : function(fn, scope){
11297         this.snapshot = this.snapshot || this.data;
11298         this.data = this.queryBy(fn, scope||this);
11299         this.fireEvent("datachanged", this);
11300     },
11301
11302     /**
11303      * Query the records by a specified property.
11304      * @param {String} field A field on your records
11305      * @param {String/RegExp} value Either a string that the field
11306      * should start with or a RegExp to test against the field
11307      * @param {Boolean} anyMatch True to match any part not just the beginning
11308      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      */
11310     query : function(property, value, anyMatch){
11311         var fn = this.createFilterFn(property, value, anyMatch);
11312         return fn ? this.queryBy(fn) : this.data.clone();
11313     },
11314
11315     /**
11316      * Query by a function. The specified function will be called with each
11317      * record in this data source. If the function returns true the record is included
11318      * in the results.
11319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11320      * @param {Object} scope (optional) The scope of the function (defaults to this)
11321       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11322      **/
11323     queryBy : function(fn, scope){
11324         var data = this.snapshot || this.data;
11325         return data.filterBy(fn, scope||this);
11326     },
11327
11328     /**
11329      * Collects unique values for a particular dataIndex from this store.
11330      * @param {String} dataIndex The property to collect
11331      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11332      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11333      * @return {Array} An array of the unique values
11334      **/
11335     collect : function(dataIndex, allowNull, bypassFilter){
11336         var d = (bypassFilter === true && this.snapshot) ?
11337                 this.snapshot.items : this.data.items;
11338         var v, sv, r = [], l = {};
11339         for(var i = 0, len = d.length; i < len; i++){
11340             v = d[i].data[dataIndex];
11341             sv = String(v);
11342             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11343                 l[sv] = true;
11344                 r[r.length] = v;
11345             }
11346         }
11347         return r;
11348     },
11349
11350     /**
11351      * Revert to a view of the Record cache with no filtering applied.
11352      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11353      */
11354     clearFilter : function(suppressEvent){
11355         if(this.snapshot && this.snapshot != this.data){
11356             this.data = this.snapshot;
11357             delete this.snapshot;
11358             if(suppressEvent !== true){
11359                 this.fireEvent("datachanged", this);
11360             }
11361         }
11362     },
11363
11364     // private
11365     afterEdit : function(record){
11366         if(this.modified.indexOf(record) == -1){
11367             this.modified.push(record);
11368         }
11369         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11370     },
11371     
11372     // private
11373     afterReject : function(record){
11374         this.modified.remove(record);
11375         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11376     },
11377
11378     // private
11379     afterCommit : function(record){
11380         this.modified.remove(record);
11381         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11382     },
11383
11384     /**
11385      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11386      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11387      */
11388     commitChanges : function(){
11389         var m = this.modified.slice(0);
11390         this.modified = [];
11391         for(var i = 0, len = m.length; i < len; i++){
11392             m[i].commit();
11393         }
11394     },
11395
11396     /**
11397      * Cancel outstanding changes on all changed records.
11398      */
11399     rejectChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].reject();
11404         }
11405     },
11406
11407     onMetaChange : function(meta, rtype, o){
11408         this.recordType = rtype;
11409         this.fields = rtype.prototype.fields;
11410         delete this.snapshot;
11411         this.sortInfo = meta.sortInfo || this.sortInfo;
11412         this.modified = [];
11413         this.fireEvent('metachange', this, this.reader.meta);
11414     },
11415     
11416     moveIndex : function(data, type)
11417     {
11418         var index = this.indexOf(data);
11419         
11420         var newIndex = index + type;
11421         
11422         this.remove(data);
11423         
11424         this.insert(newIndex, data);
11425         
11426     }
11427 });/*
11428  * Based on:
11429  * Ext JS Library 1.1.1
11430  * Copyright(c) 2006-2007, Ext JS, LLC.
11431  *
11432  * Originally Released Under LGPL - original licence link has changed is not relivant.
11433  *
11434  * Fork - LGPL
11435  * <script type="text/javascript">
11436  */
11437
11438 /**
11439  * @class Roo.data.SimpleStore
11440  * @extends Roo.data.Store
11441  * Small helper class to make creating Stores from Array data easier.
11442  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11443  * @cfg {Array} fields An array of field definition objects, or field name strings.
11444  * @cfg {Array} data The multi-dimensional array of data
11445  * @constructor
11446  * @param {Object} config
11447  */
11448 Roo.data.SimpleStore = function(config){
11449     Roo.data.SimpleStore.superclass.constructor.call(this, {
11450         isLocal : true,
11451         reader: new Roo.data.ArrayReader({
11452                 id: config.id
11453             },
11454             Roo.data.Record.create(config.fields)
11455         ),
11456         proxy : new Roo.data.MemoryProxy(config.data)
11457     });
11458     this.load();
11459 };
11460 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471 /**
11472 /**
11473  * @extends Roo.data.Store
11474  * @class Roo.data.JsonStore
11475  * Small helper class to make creating Stores for JSON data easier. <br/>
11476 <pre><code>
11477 var store = new Roo.data.JsonStore({
11478     url: 'get-images.php',
11479     root: 'images',
11480     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11481 });
11482 </code></pre>
11483  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11484  * JsonReader and HttpProxy (unless inline data is provided).</b>
11485  * @cfg {Array} fields An array of field definition objects, or field name strings.
11486  * @constructor
11487  * @param {Object} config
11488  */
11489 Roo.data.JsonStore = function(c){
11490     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11491         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11492         reader: new Roo.data.JsonReader(c, c.fields)
11493     }));
11494 };
11495 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11496  * Based on:
11497  * Ext JS Library 1.1.1
11498  * Copyright(c) 2006-2007, Ext JS, LLC.
11499  *
11500  * Originally Released Under LGPL - original licence link has changed is not relivant.
11501  *
11502  * Fork - LGPL
11503  * <script type="text/javascript">
11504  */
11505
11506  
11507 Roo.data.Field = function(config){
11508     if(typeof config == "string"){
11509         config = {name: config};
11510     }
11511     Roo.apply(this, config);
11512     
11513     if(!this.type){
11514         this.type = "auto";
11515     }
11516     
11517     var st = Roo.data.SortTypes;
11518     // named sortTypes are supported, here we look them up
11519     if(typeof this.sortType == "string"){
11520         this.sortType = st[this.sortType];
11521     }
11522     
11523     // set default sortType for strings and dates
11524     if(!this.sortType){
11525         switch(this.type){
11526             case "string":
11527                 this.sortType = st.asUCString;
11528                 break;
11529             case "date":
11530                 this.sortType = st.asDate;
11531                 break;
11532             default:
11533                 this.sortType = st.none;
11534         }
11535     }
11536
11537     // define once
11538     var stripRe = /[\$,%]/g;
11539
11540     // prebuilt conversion function for this field, instead of
11541     // switching every time we're reading a value
11542     if(!this.convert){
11543         var cv, dateFormat = this.dateFormat;
11544         switch(this.type){
11545             case "":
11546             case "auto":
11547             case undefined:
11548                 cv = function(v){ return v; };
11549                 break;
11550             case "string":
11551                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11552                 break;
11553             case "int":
11554                 cv = function(v){
11555                     return v !== undefined && v !== null && v !== '' ?
11556                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11557                     };
11558                 break;
11559             case "float":
11560                 cv = function(v){
11561                     return v !== undefined && v !== null && v !== '' ?
11562                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11563                     };
11564                 break;
11565             case "bool":
11566             case "boolean":
11567                 cv = function(v){ return v === true || v === "true" || v == 1; };
11568                 break;
11569             case "date":
11570                 cv = function(v){
11571                     if(!v){
11572                         return '';
11573                     }
11574                     if(v instanceof Date){
11575                         return v;
11576                     }
11577                     if(dateFormat){
11578                         if(dateFormat == "timestamp"){
11579                             return new Date(v*1000);
11580                         }
11581                         return Date.parseDate(v, dateFormat);
11582                     }
11583                     var parsed = Date.parse(v);
11584                     return parsed ? new Date(parsed) : null;
11585                 };
11586              break;
11587             
11588         }
11589         this.convert = cv;
11590     }
11591 };
11592
11593 Roo.data.Field.prototype = {
11594     dateFormat: null,
11595     defaultValue: "",
11596     mapping: null,
11597     sortType : null,
11598     sortDir : "ASC"
11599 };/*
11600  * Based on:
11601  * Ext JS Library 1.1.1
11602  * Copyright(c) 2006-2007, Ext JS, LLC.
11603  *
11604  * Originally Released Under LGPL - original licence link has changed is not relivant.
11605  *
11606  * Fork - LGPL
11607  * <script type="text/javascript">
11608  */
11609  
11610 // Base class for reading structured data from a data source.  This class is intended to be
11611 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11612
11613 /**
11614  * @class Roo.data.DataReader
11615  * Base class for reading structured data from a data source.  This class is intended to be
11616  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11617  */
11618
11619 Roo.data.DataReader = function(meta, recordType){
11620     
11621     this.meta = meta;
11622     
11623     this.recordType = recordType instanceof Array ? 
11624         Roo.data.Record.create(recordType) : recordType;
11625 };
11626
11627 Roo.data.DataReader.prototype = {
11628      /**
11629      * Create an empty record
11630      * @param {Object} data (optional) - overlay some values
11631      * @return {Roo.data.Record} record created.
11632      */
11633     newRow :  function(d) {
11634         var da =  {};
11635         this.recordType.prototype.fields.each(function(c) {
11636             switch( c.type) {
11637                 case 'int' : da[c.name] = 0; break;
11638                 case 'date' : da[c.name] = new Date(); break;
11639                 case 'float' : da[c.name] = 0.0; break;
11640                 case 'boolean' : da[c.name] = false; break;
11641                 default : da[c.name] = ""; break;
11642             }
11643             
11644         });
11645         return new this.recordType(Roo.apply(da, d));
11646     }
11647     
11648 };/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660  * @class Roo.data.DataProxy
11661  * @extends Roo.data.Observable
11662  * This class is an abstract base class for implementations which provide retrieval of
11663  * unformatted data objects.<br>
11664  * <p>
11665  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11666  * (of the appropriate type which knows how to parse the data object) to provide a block of
11667  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11668  * <p>
11669  * Custom implementations must implement the load method as described in
11670  * {@link Roo.data.HttpProxy#load}.
11671  */
11672 Roo.data.DataProxy = function(){
11673     this.addEvents({
11674         /**
11675          * @event beforeload
11676          * Fires before a network request is made to retrieve a data object.
11677          * @param {Object} This DataProxy object.
11678          * @param {Object} params The params parameter to the load function.
11679          */
11680         beforeload : true,
11681         /**
11682          * @event load
11683          * Fires before the load method's callback is called.
11684          * @param {Object} This DataProxy object.
11685          * @param {Object} o The data object.
11686          * @param {Object} arg The callback argument object passed to the load function.
11687          */
11688         load : true,
11689         /**
11690          * @event loadexception
11691          * Fires if an Exception occurs during data retrieval.
11692          * @param {Object} This DataProxy object.
11693          * @param {Object} o The data object.
11694          * @param {Object} arg The callback argument object passed to the load function.
11695          * @param {Object} e The Exception.
11696          */
11697         loadexception : true
11698     });
11699     Roo.data.DataProxy.superclass.constructor.call(this);
11700 };
11701
11702 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11703
11704     /**
11705      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11706      */
11707 /*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717 /**
11718  * @class Roo.data.MemoryProxy
11719  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11720  * to the Reader when its load method is called.
11721  * @constructor
11722  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11723  */
11724 Roo.data.MemoryProxy = function(data){
11725     if (data.data) {
11726         data = data.data;
11727     }
11728     Roo.data.MemoryProxy.superclass.constructor.call(this);
11729     this.data = data;
11730 };
11731
11732 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11733     
11734     /**
11735      * Load data from the requested source (in this case an in-memory
11736      * data object passed to the constructor), read the data object into
11737      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11738      * process that block using the passed callback.
11739      * @param {Object} params This parameter is not used by the MemoryProxy class.
11740      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11741      * object into a block of Roo.data.Records.
11742      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11743      * The function must be passed <ul>
11744      * <li>The Record block object</li>
11745      * <li>The "arg" argument from the load function</li>
11746      * <li>A boolean success indicator</li>
11747      * </ul>
11748      * @param {Object} scope The scope in which to call the callback
11749      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11750      */
11751     load : function(params, reader, callback, scope, arg){
11752         params = params || {};
11753         var result;
11754         try {
11755             result = reader.readRecords(this.data);
11756         }catch(e){
11757             this.fireEvent("loadexception", this, arg, null, e);
11758             callback.call(scope, null, arg, false);
11759             return;
11760         }
11761         callback.call(scope, result, arg, true);
11762     },
11763     
11764     // private
11765     update : function(params, records){
11766         
11767     }
11768 });/*
11769  * Based on:
11770  * Ext JS Library 1.1.1
11771  * Copyright(c) 2006-2007, Ext JS, LLC.
11772  *
11773  * Originally Released Under LGPL - original licence link has changed is not relivant.
11774  *
11775  * Fork - LGPL
11776  * <script type="text/javascript">
11777  */
11778 /**
11779  * @class Roo.data.HttpProxy
11780  * @extends Roo.data.DataProxy
11781  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11782  * configured to reference a certain URL.<br><br>
11783  * <p>
11784  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11785  * from which the running page was served.<br><br>
11786  * <p>
11787  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11788  * <p>
11789  * Be aware that to enable the browser to parse an XML document, the server must set
11790  * the Content-Type header in the HTTP response to "text/xml".
11791  * @constructor
11792  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11793  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11794  * will be used to make the request.
11795  */
11796 Roo.data.HttpProxy = function(conn){
11797     Roo.data.HttpProxy.superclass.constructor.call(this);
11798     // is conn a conn config or a real conn?
11799     this.conn = conn;
11800     this.useAjax = !conn || !conn.events;
11801   
11802 };
11803
11804 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11805     // thse are take from connection...
11806     
11807     /**
11808      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11809      */
11810     /**
11811      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11812      * extra parameters to each request made by this object. (defaults to undefined)
11813      */
11814     /**
11815      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11816      *  to each request made by this object. (defaults to undefined)
11817      */
11818     /**
11819      * @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)
11820      */
11821     /**
11822      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11823      */
11824      /**
11825      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11826      * @type Boolean
11827      */
11828   
11829
11830     /**
11831      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11832      * @type Boolean
11833      */
11834     /**
11835      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11836      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11837      * a finer-grained basis than the DataProxy events.
11838      */
11839     getConnection : function(){
11840         return this.useAjax ? Roo.Ajax : this.conn;
11841     },
11842
11843     /**
11844      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11845      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11846      * process that block using the passed callback.
11847      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11848      * for the request to the remote server.
11849      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11850      * object into a block of Roo.data.Records.
11851      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11852      * The function must be passed <ul>
11853      * <li>The Record block object</li>
11854      * <li>The "arg" argument from the load function</li>
11855      * <li>A boolean success indicator</li>
11856      * </ul>
11857      * @param {Object} scope The scope in which to call the callback
11858      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11859      */
11860     load : function(params, reader, callback, scope, arg){
11861         if(this.fireEvent("beforeload", this, params) !== false){
11862             var  o = {
11863                 params : params || {},
11864                 request: {
11865                     callback : callback,
11866                     scope : scope,
11867                     arg : arg
11868                 },
11869                 reader: reader,
11870                 callback : this.loadResponse,
11871                 scope: this
11872             };
11873             if(this.useAjax){
11874                 Roo.applyIf(o, this.conn);
11875                 if(this.activeRequest){
11876                     Roo.Ajax.abort(this.activeRequest);
11877                 }
11878                 this.activeRequest = Roo.Ajax.request(o);
11879             }else{
11880                 this.conn.request(o);
11881             }
11882         }else{
11883             callback.call(scope||this, null, arg, false);
11884         }
11885     },
11886
11887     // private
11888     loadResponse : function(o, success, response){
11889         delete this.activeRequest;
11890         if(!success){
11891             this.fireEvent("loadexception", this, o, response);
11892             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11893             return;
11894         }
11895         var result;
11896         try {
11897             result = o.reader.read(response);
11898         }catch(e){
11899             this.fireEvent("loadexception", this, o, response, e);
11900             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11901             return;
11902         }
11903         
11904         this.fireEvent("load", this, o, o.request.arg);
11905         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11906     },
11907
11908     // private
11909     update : function(dataSet){
11910
11911     },
11912
11913     // private
11914     updateResponse : function(dataSet){
11915
11916     }
11917 });/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.ScriptTagProxy
11930  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11931  * other than the originating domain of the running page.<br><br>
11932  * <p>
11933  * <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
11934  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11935  * <p>
11936  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11937  * source code that is used as the source inside a &lt;script> tag.<br><br>
11938  * <p>
11939  * In order for the browser to process the returned data, the server must wrap the data object
11940  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11941  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11942  * depending on whether the callback name was passed:
11943  * <p>
11944  * <pre><code>
11945 boolean scriptTag = false;
11946 String cb = request.getParameter("callback");
11947 if (cb != null) {
11948     scriptTag = true;
11949     response.setContentType("text/javascript");
11950 } else {
11951     response.setContentType("application/x-json");
11952 }
11953 Writer out = response.getWriter();
11954 if (scriptTag) {
11955     out.write(cb + "(");
11956 }
11957 out.print(dataBlock.toJsonString());
11958 if (scriptTag) {
11959     out.write(");");
11960 }
11961 </pre></code>
11962  *
11963  * @constructor
11964  * @param {Object} config A configuration object.
11965  */
11966 Roo.data.ScriptTagProxy = function(config){
11967     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11968     Roo.apply(this, config);
11969     this.head = document.getElementsByTagName("head")[0];
11970 };
11971
11972 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11973
11974 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11975     /**
11976      * @cfg {String} url The URL from which to request the data object.
11977      */
11978     /**
11979      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11980      */
11981     timeout : 30000,
11982     /**
11983      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11984      * the server the name of the callback function set up by the load call to process the returned data object.
11985      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11986      * javascript output which calls this named function passing the data object as its only parameter.
11987      */
11988     callbackParam : "callback",
11989     /**
11990      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11991      * name to the request.
11992      */
11993     nocache : true,
11994
11995     /**
11996      * Load data from the configured URL, read the data object into
11997      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11998      * process that block using the passed callback.
11999      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12000      * for the request to the remote server.
12001      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12002      * object into a block of Roo.data.Records.
12003      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12004      * The function must be passed <ul>
12005      * <li>The Record block object</li>
12006      * <li>The "arg" argument from the load function</li>
12007      * <li>A boolean success indicator</li>
12008      * </ul>
12009      * @param {Object} scope The scope in which to call the callback
12010      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12011      */
12012     load : function(params, reader, callback, scope, arg){
12013         if(this.fireEvent("beforeload", this, params) !== false){
12014
12015             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12016
12017             var url = this.url;
12018             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12019             if(this.nocache){
12020                 url += "&_dc=" + (new Date().getTime());
12021             }
12022             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12023             var trans = {
12024                 id : transId,
12025                 cb : "stcCallback"+transId,
12026                 scriptId : "stcScript"+transId,
12027                 params : params,
12028                 arg : arg,
12029                 url : url,
12030                 callback : callback,
12031                 scope : scope,
12032                 reader : reader
12033             };
12034             var conn = this;
12035
12036             window[trans.cb] = function(o){
12037                 conn.handleResponse(o, trans);
12038             };
12039
12040             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12041
12042             if(this.autoAbort !== false){
12043                 this.abort();
12044             }
12045
12046             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12047
12048             var script = document.createElement("script");
12049             script.setAttribute("src", url);
12050             script.setAttribute("type", "text/javascript");
12051             script.setAttribute("id", trans.scriptId);
12052             this.head.appendChild(script);
12053
12054             this.trans = trans;
12055         }else{
12056             callback.call(scope||this, null, arg, false);
12057         }
12058     },
12059
12060     // private
12061     isLoading : function(){
12062         return this.trans ? true : false;
12063     },
12064
12065     /**
12066      * Abort the current server request.
12067      */
12068     abort : function(){
12069         if(this.isLoading()){
12070             this.destroyTrans(this.trans);
12071         }
12072     },
12073
12074     // private
12075     destroyTrans : function(trans, isLoaded){
12076         this.head.removeChild(document.getElementById(trans.scriptId));
12077         clearTimeout(trans.timeoutId);
12078         if(isLoaded){
12079             window[trans.cb] = undefined;
12080             try{
12081                 delete window[trans.cb];
12082             }catch(e){}
12083         }else{
12084             // if hasn't been loaded, wait for load to remove it to prevent script error
12085             window[trans.cb] = function(){
12086                 window[trans.cb] = undefined;
12087                 try{
12088                     delete window[trans.cb];
12089                 }catch(e){}
12090             };
12091         }
12092     },
12093
12094     // private
12095     handleResponse : function(o, trans){
12096         this.trans = false;
12097         this.destroyTrans(trans, true);
12098         var result;
12099         try {
12100             result = trans.reader.readRecords(o);
12101         }catch(e){
12102             this.fireEvent("loadexception", this, o, trans.arg, e);
12103             trans.callback.call(trans.scope||window, null, trans.arg, false);
12104             return;
12105         }
12106         this.fireEvent("load", this, o, trans.arg);
12107         trans.callback.call(trans.scope||window, result, trans.arg, true);
12108     },
12109
12110     // private
12111     handleFailure : function(trans){
12112         this.trans = false;
12113         this.destroyTrans(trans, false);
12114         this.fireEvent("loadexception", this, null, trans.arg);
12115         trans.callback.call(trans.scope||window, null, trans.arg, false);
12116     }
12117 });/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128 /**
12129  * @class Roo.data.JsonReader
12130  * @extends Roo.data.DataReader
12131  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12132  * based on mappings in a provided Roo.data.Record constructor.
12133  * 
12134  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12135  * in the reply previously. 
12136  * 
12137  * <p>
12138  * Example code:
12139  * <pre><code>
12140 var RecordDef = Roo.data.Record.create([
12141     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12142     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12143 ]);
12144 var myReader = new Roo.data.JsonReader({
12145     totalProperty: "results",    // The property which contains the total dataset size (optional)
12146     root: "rows",                // The property which contains an Array of row objects
12147     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12148 }, RecordDef);
12149 </code></pre>
12150  * <p>
12151  * This would consume a JSON file like this:
12152  * <pre><code>
12153 { 'results': 2, 'rows': [
12154     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12155     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12156 }
12157 </code></pre>
12158  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12159  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12160  * paged from the remote server.
12161  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12162  * @cfg {String} root name of the property which contains the Array of row objects.
12163  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12164  * @cfg {Array} fields Array of field definition objects
12165  * @constructor
12166  * Create a new JsonReader
12167  * @param {Object} meta Metadata configuration options
12168  * @param {Object} recordType Either an Array of field definition objects,
12169  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12170  */
12171 Roo.data.JsonReader = function(meta, recordType){
12172     
12173     meta = meta || {};
12174     // set some defaults:
12175     Roo.applyIf(meta, {
12176         totalProperty: 'total',
12177         successProperty : 'success',
12178         root : 'data',
12179         id : 'id'
12180     });
12181     
12182     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12183 };
12184 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12185     
12186     /**
12187      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12188      * Used by Store query builder to append _requestMeta to params.
12189      * 
12190      */
12191     metaFromRemote : false,
12192     /**
12193      * This method is only used by a DataProxy which has retrieved data from a remote server.
12194      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12195      * @return {Object} data A data block which is used by an Roo.data.Store object as
12196      * a cache of Roo.data.Records.
12197      */
12198     read : function(response){
12199         var json = response.responseText;
12200        
12201         var o = /* eval:var:o */ eval("("+json+")");
12202         if(!o) {
12203             throw {message: "JsonReader.read: Json object not found"};
12204         }
12205         
12206         if(o.metaData){
12207             
12208             delete this.ef;
12209             this.metaFromRemote = true;
12210             this.meta = o.metaData;
12211             this.recordType = Roo.data.Record.create(o.metaData.fields);
12212             this.onMetaChange(this.meta, this.recordType, o);
12213         }
12214         return this.readRecords(o);
12215     },
12216
12217     // private function a store will implement
12218     onMetaChange : function(meta, recordType, o){
12219
12220     },
12221
12222     /**
12223          * @ignore
12224          */
12225     simpleAccess: function(obj, subsc) {
12226         return obj[subsc];
12227     },
12228
12229         /**
12230          * @ignore
12231          */
12232     getJsonAccessor: function(){
12233         var re = /[\[\.]/;
12234         return function(expr) {
12235             try {
12236                 return(re.test(expr))
12237                     ? new Function("obj", "return obj." + expr)
12238                     : function(obj){
12239                         return obj[expr];
12240                     };
12241             } catch(e){}
12242             return Roo.emptyFn;
12243         };
12244     }(),
12245
12246     /**
12247      * Create a data block containing Roo.data.Records from an XML document.
12248      * @param {Object} o An object which contains an Array of row objects in the property specified
12249      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12250      * which contains the total size of the dataset.
12251      * @return {Object} data A data block which is used by an Roo.data.Store object as
12252      * a cache of Roo.data.Records.
12253      */
12254     readRecords : function(o){
12255         /**
12256          * After any data loads, the raw JSON data is available for further custom processing.
12257          * @type Object
12258          */
12259         this.o = o;
12260         var s = this.meta, Record = this.recordType,
12261             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12262
12263 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12264         if (!this.ef) {
12265             if(s.totalProperty) {
12266                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12267                 }
12268                 if(s.successProperty) {
12269                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12270                 }
12271                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12272                 if (s.id) {
12273                         var g = this.getJsonAccessor(s.id);
12274                         this.getId = function(rec) {
12275                                 var r = g(rec);  
12276                                 return (r === undefined || r === "") ? null : r;
12277                         };
12278                 } else {
12279                         this.getId = function(){return null;};
12280                 }
12281             this.ef = [];
12282             for(var jj = 0; jj < fl; jj++){
12283                 f = fi[jj];
12284                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12285                 this.ef[jj] = this.getJsonAccessor(map);
12286             }
12287         }
12288
12289         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12290         if(s.totalProperty){
12291             var vt = parseInt(this.getTotal(o), 10);
12292             if(!isNaN(vt)){
12293                 totalRecords = vt;
12294             }
12295         }
12296         if(s.successProperty){
12297             var vs = this.getSuccess(o);
12298             if(vs === false || vs === 'false'){
12299                 success = false;
12300             }
12301         }
12302         var records = [];
12303         for(var i = 0; i < c; i++){
12304                 var n = root[i];
12305             var values = {};
12306             var id = this.getId(n);
12307             for(var j = 0; j < fl; j++){
12308                 f = fi[j];
12309             var v = this.ef[j](n);
12310             if (!f.convert) {
12311                 Roo.log('missing convert for ' + f.name);
12312                 Roo.log(f);
12313                 continue;
12314             }
12315             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12316             }
12317             var record = new Record(values, id);
12318             record.json = n;
12319             records[i] = record;
12320         }
12321         return {
12322             raw : o,
12323             success : success,
12324             records : records,
12325             totalRecords : totalRecords
12326         };
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.ArrayReader
12341  * @extends Roo.data.DataReader
12342  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12343  * Each element of that Array represents a row of data fields. The
12344  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12345  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12346  * <p>
12347  * Example code:.
12348  * <pre><code>
12349 var RecordDef = Roo.data.Record.create([
12350     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12351     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12352 ]);
12353 var myReader = new Roo.data.ArrayReader({
12354     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12355 }, RecordDef);
12356 </code></pre>
12357  * <p>
12358  * This would consume an Array like this:
12359  * <pre><code>
12360 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12361   </code></pre>
12362  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12363  * @constructor
12364  * Create a new JsonReader
12365  * @param {Object} meta Metadata configuration options.
12366  * @param {Object} recordType Either an Array of field definition objects
12367  * as specified to {@link Roo.data.Record#create},
12368  * or an {@link Roo.data.Record} object
12369  * created using {@link Roo.data.Record#create}.
12370  */
12371 Roo.data.ArrayReader = function(meta, recordType){
12372     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12373 };
12374
12375 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12376     /**
12377      * Create a data block containing Roo.data.Records from an XML document.
12378      * @param {Object} o An Array of row objects which represents the dataset.
12379      * @return {Object} data A data block which is used by an Roo.data.Store object as
12380      * a cache of Roo.data.Records.
12381      */
12382     readRecords : function(o){
12383         var sid = this.meta ? this.meta.id : null;
12384         var recordType = this.recordType, fields = recordType.prototype.fields;
12385         var records = [];
12386         var root = o;
12387             for(var i = 0; i < root.length; i++){
12388                     var n = root[i];
12389                 var values = {};
12390                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12391                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12392                 var f = fields.items[j];
12393                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12394                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12395                 v = f.convert(v);
12396                 values[f.name] = v;
12397             }
12398                 var record = new recordType(values, id);
12399                 record.json = n;
12400                 records[records.length] = record;
12401             }
12402             return {
12403                 records : records,
12404                 totalRecords : records.length
12405             };
12406     }
12407 });/*
12408  * - LGPL
12409  * * 
12410  */
12411
12412 /**
12413  * @class Roo.bootstrap.ComboBox
12414  * @extends Roo.bootstrap.TriggerField
12415  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12416  * @cfg {Boolean} append (true|false) default false
12417  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12418  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12419  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12420  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12421  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12422  * @cfg {Boolean} animate default true
12423  * @cfg {Boolean} emptyResultText only for touch device
12424  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12425  * @cfg {String} emptyTitle default ''
12426  * @constructor
12427  * Create a new ComboBox.
12428  * @param {Object} config Configuration options
12429  */
12430 Roo.bootstrap.ComboBox = function(config){
12431     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12432     this.addEvents({
12433         /**
12434          * @event expand
12435          * Fires when the dropdown list is expanded
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         */
12438         'expand' : true,
12439         /**
12440          * @event collapse
12441          * Fires when the dropdown list is collapsed
12442         * @param {Roo.bootstrap.ComboBox} combo This combo box
12443         */
12444         'collapse' : true,
12445         /**
12446          * @event beforeselect
12447          * Fires before a list item is selected. Return false to cancel the selection.
12448         * @param {Roo.bootstrap.ComboBox} combo This combo box
12449         * @param {Roo.data.Record} record The data record returned from the underlying store
12450         * @param {Number} index The index of the selected item in the dropdown list
12451         */
12452         'beforeselect' : true,
12453         /**
12454          * @event select
12455          * Fires when a list item is selected
12456         * @param {Roo.bootstrap.ComboBox} combo This combo box
12457         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12458         * @param {Number} index The index of the selected item in the dropdown list
12459         */
12460         'select' : true,
12461         /**
12462          * @event beforequery
12463          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12464          * The event object passed has these properties:
12465         * @param {Roo.bootstrap.ComboBox} combo This combo box
12466         * @param {String} query The query
12467         * @param {Boolean} forceAll true to force "all" query
12468         * @param {Boolean} cancel true to cancel the query
12469         * @param {Object} e The query event object
12470         */
12471         'beforequery': true,
12472          /**
12473          * @event add
12474          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'add' : true,
12478         /**
12479          * @event edit
12480          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12483         */
12484         'edit' : true,
12485         /**
12486          * @event remove
12487          * Fires when the remove value from the combobox array
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         */
12490         'remove' : true,
12491         /**
12492          * @event afterremove
12493          * Fires when the remove value from the combobox array
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         */
12496         'afterremove' : true,
12497         /**
12498          * @event specialfilter
12499          * Fires when specialfilter
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             */
12502         'specialfilter' : true,
12503         /**
12504          * @event tick
12505          * Fires when tick the element
12506             * @param {Roo.bootstrap.ComboBox} combo This combo box
12507             */
12508         'tick' : true,
12509         /**
12510          * @event touchviewdisplay
12511          * Fires when touch view require special display (default is using displayField)
12512             * @param {Roo.bootstrap.ComboBox} combo This combo box
12513             * @param {Object} cfg set html .
12514             */
12515         'touchviewdisplay' : true
12516         
12517     });
12518     
12519     this.item = [];
12520     this.tickItems = [];
12521     
12522     this.selectedIndex = -1;
12523     if(this.mode == 'local'){
12524         if(config.queryDelay === undefined){
12525             this.queryDelay = 10;
12526         }
12527         if(config.minChars === undefined){
12528             this.minChars = 0;
12529         }
12530     }
12531 };
12532
12533 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12534      
12535     /**
12536      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12537      * rendering into an Roo.Editor, defaults to false)
12538      */
12539     /**
12540      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12541      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12542      */
12543     /**
12544      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12545      */
12546     /**
12547      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12548      * the dropdown list (defaults to undefined, with no header element)
12549      */
12550
12551      /**
12552      * @cfg {String/Roo.Template} tpl The template to use to render the output
12553      */
12554      
12555      /**
12556      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12557      */
12558     listWidth: undefined,
12559     /**
12560      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12561      * mode = 'remote' or 'text' if mode = 'local')
12562      */
12563     displayField: undefined,
12564     
12565     /**
12566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12567      * mode = 'remote' or 'value' if mode = 'local'). 
12568      * Note: use of a valueField requires the user make a selection
12569      * in order for a value to be mapped.
12570      */
12571     valueField: undefined,
12572     /**
12573      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12574      */
12575     modalTitle : '',
12576     
12577     /**
12578      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12579      * field's data value (defaults to the underlying DOM element's name)
12580      */
12581     hiddenName: undefined,
12582     /**
12583      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12584      */
12585     listClass: '',
12586     /**
12587      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12588      */
12589     selectedClass: 'active',
12590     
12591     /**
12592      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12593      */
12594     shadow:'sides',
12595     /**
12596      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12597      * anchor positions (defaults to 'tl-bl')
12598      */
12599     listAlign: 'tl-bl?',
12600     /**
12601      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12602      */
12603     maxHeight: 300,
12604     /**
12605      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12606      * query specified by the allQuery config option (defaults to 'query')
12607      */
12608     triggerAction: 'query',
12609     /**
12610      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12611      * (defaults to 4, does not apply if editable = false)
12612      */
12613     minChars : 4,
12614     /**
12615      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12616      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12617      */
12618     typeAhead: false,
12619     /**
12620      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12621      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12622      */
12623     queryDelay: 500,
12624     /**
12625      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12626      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12627      */
12628     pageSize: 0,
12629     /**
12630      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12631      * when editable = true (defaults to false)
12632      */
12633     selectOnFocus:false,
12634     /**
12635      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12636      */
12637     queryParam: 'query',
12638     /**
12639      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12640      * when mode = 'remote' (defaults to 'Loading...')
12641      */
12642     loadingText: 'Loading...',
12643     /**
12644      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12645      */
12646     resizable: false,
12647     /**
12648      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12649      */
12650     handleHeight : 8,
12651     /**
12652      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12653      * traditional select (defaults to true)
12654      */
12655     editable: true,
12656     /**
12657      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12658      */
12659     allQuery: '',
12660     /**
12661      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12662      */
12663     mode: 'remote',
12664     /**
12665      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12666      * listWidth has a higher value)
12667      */
12668     minListWidth : 70,
12669     /**
12670      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12671      * allow the user to set arbitrary text into the field (defaults to false)
12672      */
12673     forceSelection:false,
12674     /**
12675      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12676      * if typeAhead = true (defaults to 250)
12677      */
12678     typeAheadDelay : 250,
12679     /**
12680      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12681      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12682      */
12683     valueNotFoundText : undefined,
12684     /**
12685      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12686      */
12687     blockFocus : false,
12688     
12689     /**
12690      * @cfg {Boolean} disableClear Disable showing of clear button.
12691      */
12692     disableClear : false,
12693     /**
12694      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12695      */
12696     alwaysQuery : false,
12697     
12698     /**
12699      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12700      */
12701     multiple : false,
12702     
12703     /**
12704      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12705      */
12706     invalidClass : "has-warning",
12707     
12708     /**
12709      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12710      */
12711     validClass : "has-success",
12712     
12713     /**
12714      * @cfg {Boolean} specialFilter (true|false) special filter default false
12715      */
12716     specialFilter : false,
12717     
12718     /**
12719      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12720      */
12721     mobileTouchView : true,
12722     
12723     /**
12724      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12725      */
12726     useNativeIOS : false,
12727     
12728     ios_options : false,
12729     
12730     //private
12731     addicon : false,
12732     editicon: false,
12733     
12734     page: 0,
12735     hasQuery: false,
12736     append: false,
12737     loadNext: false,
12738     autoFocus : true,
12739     tickable : false,
12740     btnPosition : 'right',
12741     triggerList : true,
12742     showToggleBtn : true,
12743     animate : true,
12744     emptyResultText: 'Empty',
12745     triggerText : 'Select',
12746     emptyTitle : '',
12747     
12748     // element that contains real text value.. (when hidden is used..)
12749     
12750     getAutoCreate : function()
12751     {   
12752         var cfg = false;
12753         //render
12754         /*
12755          * Render classic select for iso
12756          */
12757         
12758         if(Roo.isIOS && this.useNativeIOS){
12759             cfg = this.getAutoCreateNativeIOS();
12760             return cfg;
12761         }
12762         
12763         /*
12764          * Touch Devices
12765          */
12766         
12767         if(Roo.isTouch && this.mobileTouchView){
12768             cfg = this.getAutoCreateTouchView();
12769             return cfg;;
12770         }
12771         
12772         /*
12773          *  Normal ComboBox
12774          */
12775         if(!this.tickable){
12776             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12777             return cfg;
12778         }
12779         
12780         /*
12781          *  ComboBox with tickable selections
12782          */
12783              
12784         var align = this.labelAlign || this.parentLabelAlign();
12785         
12786         cfg = {
12787             cls : 'form-group roo-combobox-tickable' //input-group
12788         };
12789         
12790         var btn_text_select = '';
12791         var btn_text_done = '';
12792         var btn_text_cancel = '';
12793         
12794         if (this.btn_text_show) {
12795             btn_text_select = 'Select';
12796             btn_text_done = 'Done';
12797             btn_text_cancel = 'Cancel'; 
12798         }
12799         
12800         var buttons = {
12801             tag : 'div',
12802             cls : 'tickable-buttons',
12803             cn : [
12804                 {
12805                     tag : 'button',
12806                     type : 'button',
12807                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12808                     //html : this.triggerText
12809                     html: btn_text_select
12810                 },
12811                 {
12812                     tag : 'button',
12813                     type : 'button',
12814                     name : 'ok',
12815                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12816                     //html : 'Done'
12817                     html: btn_text_done
12818                 },
12819                 {
12820                     tag : 'button',
12821                     type : 'button',
12822                     name : 'cancel',
12823                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12824                     //html : 'Cancel'
12825                     html: btn_text_cancel
12826                 }
12827             ]
12828         };
12829         
12830         if(this.editable){
12831             buttons.cn.unshift({
12832                 tag: 'input',
12833                 cls: 'roo-select2-search-field-input'
12834             });
12835         }
12836         
12837         var _this = this;
12838         
12839         Roo.each(buttons.cn, function(c){
12840             if (_this.size) {
12841                 c.cls += ' btn-' + _this.size;
12842             }
12843
12844             if (_this.disabled) {
12845                 c.disabled = true;
12846             }
12847         });
12848         
12849         var box = {
12850             tag: 'div',
12851             cn: [
12852                 {
12853                     tag: 'input',
12854                     type : 'hidden',
12855                     cls: 'form-hidden-field'
12856                 },
12857                 {
12858                     tag: 'ul',
12859                     cls: 'roo-select2-choices',
12860                     cn:[
12861                         {
12862                             tag: 'li',
12863                             cls: 'roo-select2-search-field',
12864                             cn: [
12865                                 buttons
12866                             ]
12867                         }
12868                     ]
12869                 }
12870             ]
12871         };
12872         
12873         var combobox = {
12874             cls: 'roo-select2-container input-group roo-select2-container-multi',
12875             cn: [
12876                 box
12877 //                {
12878 //                    tag: 'ul',
12879 //                    cls: 'typeahead typeahead-long dropdown-menu',
12880 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12881 //                }
12882             ]
12883         };
12884         
12885         if(this.hasFeedback && !this.allowBlank){
12886             
12887             var feedback = {
12888                 tag: 'span',
12889                 cls: 'glyphicon form-control-feedback'
12890             };
12891
12892             combobox.cn.push(feedback);
12893         }
12894         
12895         
12896         if (align ==='left' && this.fieldLabel.length) {
12897             
12898             cfg.cls += ' roo-form-group-label-left';
12899             
12900             cfg.cn = [
12901                 {
12902                     tag : 'i',
12903                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12904                     tooltip : 'This field is required'
12905                 },
12906                 {
12907                     tag: 'label',
12908                     'for' :  id,
12909                     cls : 'control-label',
12910                     html : this.fieldLabel
12911
12912                 },
12913                 {
12914                     cls : "", 
12915                     cn: [
12916                         combobox
12917                     ]
12918                 }
12919
12920             ];
12921             
12922             var labelCfg = cfg.cn[1];
12923             var contentCfg = cfg.cn[2];
12924             
12925
12926             if(this.indicatorpos == 'right'){
12927                 
12928                 cfg.cn = [
12929                     {
12930                         tag: 'label',
12931                         'for' :  id,
12932                         cls : 'control-label',
12933                         cn : [
12934                             {
12935                                 tag : 'span',
12936                                 html : this.fieldLabel
12937                             },
12938                             {
12939                                 tag : 'i',
12940                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12941                                 tooltip : 'This field is required'
12942                             }
12943                         ]
12944                     },
12945                     {
12946                         cls : "",
12947                         cn: [
12948                             combobox
12949                         ]
12950                     }
12951
12952                 ];
12953                 
12954                 
12955                 
12956                 labelCfg = cfg.cn[0];
12957                 contentCfg = cfg.cn[1];
12958             
12959             }
12960             
12961             if(this.labelWidth > 12){
12962                 labelCfg.style = "width: " + this.labelWidth + 'px';
12963             }
12964             
12965             if(this.labelWidth < 13 && this.labelmd == 0){
12966                 this.labelmd = this.labelWidth;
12967             }
12968             
12969             if(this.labellg > 0){
12970                 labelCfg.cls += ' col-lg-' + this.labellg;
12971                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12972             }
12973             
12974             if(this.labelmd > 0){
12975                 labelCfg.cls += ' col-md-' + this.labelmd;
12976                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12977             }
12978             
12979             if(this.labelsm > 0){
12980                 labelCfg.cls += ' col-sm-' + this.labelsm;
12981                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12982             }
12983             
12984             if(this.labelxs > 0){
12985                 labelCfg.cls += ' col-xs-' + this.labelxs;
12986                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12987             }
12988                 
12989                 
12990         } else if ( this.fieldLabel.length) {
12991 //                Roo.log(" label");
12992                  cfg.cn = [
12993                     {
12994                         tag : 'i',
12995                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12996                         tooltip : 'This field is required'
12997                     },
12998                     {
12999                         tag: 'label',
13000                         //cls : 'input-group-addon',
13001                         html : this.fieldLabel
13002                     },
13003                     combobox
13004                 ];
13005                 
13006                 if(this.indicatorpos == 'right'){
13007                     cfg.cn = [
13008                         {
13009                             tag: 'label',
13010                             //cls : 'input-group-addon',
13011                             html : this.fieldLabel
13012                         },
13013                         {
13014                             tag : 'i',
13015                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13016                             tooltip : 'This field is required'
13017                         },
13018                         combobox
13019                     ];
13020                     
13021                 }
13022
13023         } else {
13024             
13025 //                Roo.log(" no label && no align");
13026                 cfg = combobox
13027                      
13028                 
13029         }
13030          
13031         var settings=this;
13032         ['xs','sm','md','lg'].map(function(size){
13033             if (settings[size]) {
13034                 cfg.cls += ' col-' + size + '-' + settings[size];
13035             }
13036         });
13037         
13038         return cfg;
13039         
13040     },
13041     
13042     _initEventsCalled : false,
13043     
13044     // private
13045     initEvents: function()
13046     {   
13047         if (this._initEventsCalled) { // as we call render... prevent looping...
13048             return;
13049         }
13050         this._initEventsCalled = true;
13051         
13052         if (!this.store) {
13053             throw "can not find store for combo";
13054         }
13055         
13056         this.indicator = this.indicatorEl();
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.parent = this;
13060         
13061         // if we are building from html. then this element is so complex, that we can not really
13062         // use the rendered HTML.
13063         // so we have to trash and replace the previous code.
13064         if (Roo.XComponent.build_from_html) {
13065             // remove this element....
13066             var e = this.el.dom, k=0;
13067             while (e ) { e = e.previousSibling;  ++k;}
13068
13069             this.el.remove();
13070             
13071             this.el=false;
13072             this.rendered = false;
13073             
13074             this.render(this.parent().getChildContainer(true), k);
13075         }
13076         
13077         if(Roo.isIOS && this.useNativeIOS){
13078             this.initIOSView();
13079             return;
13080         }
13081         
13082         /*
13083          * Touch Devices
13084          */
13085         
13086         if(Roo.isTouch && this.mobileTouchView){
13087             this.initTouchView();
13088             return;
13089         }
13090         
13091         if(this.tickable){
13092             this.initTickableEvents();
13093             return;
13094         }
13095         
13096         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13097         
13098         if(this.hiddenName){
13099             
13100             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13101             
13102             this.hiddenField.dom.value =
13103                 this.hiddenValue !== undefined ? this.hiddenValue :
13104                 this.value !== undefined ? this.value : '';
13105
13106             // prevent input submission
13107             this.el.dom.removeAttribute('name');
13108             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13109              
13110              
13111         }
13112         //if(Roo.isGecko){
13113         //    this.el.dom.setAttribute('autocomplete', 'off');
13114         //}
13115         
13116         var cls = 'x-combo-list';
13117         
13118         //this.list = new Roo.Layer({
13119         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13120         //});
13121         
13122         var _this = this;
13123         
13124         (function(){
13125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13126             _this.list.setWidth(lw);
13127         }).defer(100);
13128         
13129         this.list.on('mouseover', this.onViewOver, this);
13130         this.list.on('mousemove', this.onViewMove, this);
13131         this.list.on('scroll', this.onViewScroll, this);
13132         
13133         /*
13134         this.list.swallowEvent('mousewheel');
13135         this.assetHeight = 0;
13136
13137         if(this.title){
13138             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13139             this.assetHeight += this.header.getHeight();
13140         }
13141
13142         this.innerList = this.list.createChild({cls:cls+'-inner'});
13143         this.innerList.on('mouseover', this.onViewOver, this);
13144         this.innerList.on('mousemove', this.onViewMove, this);
13145         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13146         
13147         if(this.allowBlank && !this.pageSize && !this.disableClear){
13148             this.footer = this.list.createChild({cls:cls+'-ft'});
13149             this.pageTb = new Roo.Toolbar(this.footer);
13150            
13151         }
13152         if(this.pageSize){
13153             this.footer = this.list.createChild({cls:cls+'-ft'});
13154             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13155                     {pageSize: this.pageSize});
13156             
13157         }
13158         
13159         if (this.pageTb && this.allowBlank && !this.disableClear) {
13160             var _this = this;
13161             this.pageTb.add(new Roo.Toolbar.Fill(), {
13162                 cls: 'x-btn-icon x-btn-clear',
13163                 text: '&#160;',
13164                 handler: function()
13165                 {
13166                     _this.collapse();
13167                     _this.clearValue();
13168                     _this.onSelect(false, -1);
13169                 }
13170             });
13171         }
13172         if (this.footer) {
13173             this.assetHeight += this.footer.getHeight();
13174         }
13175         */
13176             
13177         if(!this.tpl){
13178             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13179         }
13180
13181         this.view = new Roo.View(this.list, this.tpl, {
13182             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13183         });
13184         //this.view.wrapEl.setDisplayed(false);
13185         this.view.on('click', this.onViewClick, this);
13186         
13187         
13188         this.store.on('beforeload', this.onBeforeLoad, this);
13189         this.store.on('load', this.onLoad, this);
13190         this.store.on('loadexception', this.onLoadException, this);
13191         /*
13192         if(this.resizable){
13193             this.resizer = new Roo.Resizable(this.list,  {
13194                pinned:true, handles:'se'
13195             });
13196             this.resizer.on('resize', function(r, w, h){
13197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13198                 this.listWidth = w;
13199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13200                 this.restrictHeight();
13201             }, this);
13202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13203         }
13204         */
13205         if(!this.editable){
13206             this.editable = true;
13207             this.setEditable(false);
13208         }
13209         
13210         /*
13211         
13212         if (typeof(this.events.add.listeners) != 'undefined') {
13213             
13214             this.addicon = this.wrap.createChild(
13215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13216        
13217             this.addicon.on('click', function(e) {
13218                 this.fireEvent('add', this);
13219             }, this);
13220         }
13221         if (typeof(this.events.edit.listeners) != 'undefined') {
13222             
13223             this.editicon = this.wrap.createChild(
13224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13225             if (this.addicon) {
13226                 this.editicon.setStyle('margin-left', '40px');
13227             }
13228             this.editicon.on('click', function(e) {
13229                 
13230                 // we fire even  if inothing is selected..
13231                 this.fireEvent('edit', this, this.lastData );
13232                 
13233             }, this);
13234         }
13235         */
13236         
13237         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13238             "up" : function(e){
13239                 this.inKeyMode = true;
13240                 this.selectPrev();
13241             },
13242
13243             "down" : function(e){
13244                 if(!this.isExpanded()){
13245                     this.onTriggerClick();
13246                 }else{
13247                     this.inKeyMode = true;
13248                     this.selectNext();
13249                 }
13250             },
13251
13252             "enter" : function(e){
13253 //                this.onViewClick();
13254                 //return true;
13255                 this.collapse();
13256                 
13257                 if(this.fireEvent("specialkey", this, e)){
13258                     this.onViewClick(false);
13259                 }
13260                 
13261                 return true;
13262             },
13263
13264             "esc" : function(e){
13265                 this.collapse();
13266             },
13267
13268             "tab" : function(e){
13269                 this.collapse();
13270                 
13271                 if(this.fireEvent("specialkey", this, e)){
13272                     this.onViewClick(false);
13273                 }
13274                 
13275                 return true;
13276             },
13277
13278             scope : this,
13279
13280             doRelay : function(foo, bar, hname){
13281                 if(hname == 'down' || this.scope.isExpanded()){
13282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13283                 }
13284                 return true;
13285             },
13286
13287             forceKeyDown: true
13288         });
13289         
13290         
13291         this.queryDelay = Math.max(this.queryDelay || 10,
13292                 this.mode == 'local' ? 10 : 250);
13293         
13294         
13295         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13296         
13297         if(this.typeAhead){
13298             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13299         }
13300         if(this.editable !== false){
13301             this.inputEl().on("keyup", this.onKeyUp, this);
13302         }
13303         if(this.forceSelection){
13304             this.inputEl().on('blur', this.doForce, this);
13305         }
13306         
13307         if(this.multiple){
13308             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13309             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13310         }
13311     },
13312     
13313     initTickableEvents: function()
13314     {   
13315         this.createList();
13316         
13317         if(this.hiddenName){
13318             
13319             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13320             
13321             this.hiddenField.dom.value =
13322                 this.hiddenValue !== undefined ? this.hiddenValue :
13323                 this.value !== undefined ? this.value : '';
13324
13325             // prevent input submission
13326             this.el.dom.removeAttribute('name');
13327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328              
13329              
13330         }
13331         
13332 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13333         
13334         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13335         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13336         if(this.triggerList){
13337             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13338         }
13339          
13340         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13341         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13342         
13343         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13344         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13345         
13346         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13347         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13348         
13349         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13350         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13351         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13352         
13353         this.okBtn.hide();
13354         this.cancelBtn.hide();
13355         
13356         var _this = this;
13357         
13358         (function(){
13359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13360             _this.list.setWidth(lw);
13361         }).defer(100);
13362         
13363         this.list.on('mouseover', this.onViewOver, this);
13364         this.list.on('mousemove', this.onViewMove, this);
13365         
13366         this.list.on('scroll', this.onViewScroll, this);
13367         
13368         if(!this.tpl){
13369             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>';
13370         }
13371
13372         this.view = new Roo.View(this.list, this.tpl, {
13373             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13374         });
13375         
13376         //this.view.wrapEl.setDisplayed(false);
13377         this.view.on('click', this.onViewClick, this);
13378         
13379         
13380         
13381         this.store.on('beforeload', this.onBeforeLoad, this);
13382         this.store.on('load', this.onLoad, this);
13383         this.store.on('loadexception', this.onLoadException, this);
13384         
13385         if(this.editable){
13386             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13387                 "up" : function(e){
13388                     this.inKeyMode = true;
13389                     this.selectPrev();
13390                 },
13391
13392                 "down" : function(e){
13393                     this.inKeyMode = true;
13394                     this.selectNext();
13395                 },
13396
13397                 "enter" : function(e){
13398                     if(this.fireEvent("specialkey", this, e)){
13399                         this.onViewClick(false);
13400                     }
13401                     
13402                     return true;
13403                 },
13404
13405                 "esc" : function(e){
13406                     this.onTickableFooterButtonClick(e, false, false);
13407                 },
13408
13409                 "tab" : function(e){
13410                     this.fireEvent("specialkey", this, e);
13411                     
13412                     this.onTickableFooterButtonClick(e, false, false);
13413                     
13414                     return true;
13415                 },
13416
13417                 scope : this,
13418
13419                 doRelay : function(e, fn, key){
13420                     if(this.scope.isExpanded()){
13421                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13422                     }
13423                     return true;
13424                 },
13425
13426                 forceKeyDown: true
13427             });
13428         }
13429         
13430         this.queryDelay = Math.max(this.queryDelay || 10,
13431                 this.mode == 'local' ? 10 : 250);
13432         
13433         
13434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13435         
13436         if(this.typeAhead){
13437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13438         }
13439         
13440         if(this.editable !== false){
13441             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13442         }
13443         
13444         this.indicator = this.indicatorEl();
13445         
13446         if(this.indicator){
13447             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13448             this.indicator.hide();
13449         }
13450         
13451     },
13452
13453     onDestroy : function(){
13454         if(this.view){
13455             this.view.setStore(null);
13456             this.view.el.removeAllListeners();
13457             this.view.el.remove();
13458             this.view.purgeListeners();
13459         }
13460         if(this.list){
13461             this.list.dom.innerHTML  = '';
13462         }
13463         
13464         if(this.store){
13465             this.store.un('beforeload', this.onBeforeLoad, this);
13466             this.store.un('load', this.onLoad, this);
13467             this.store.un('loadexception', this.onLoadException, this);
13468         }
13469         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13470     },
13471
13472     // private
13473     fireKey : function(e){
13474         if(e.isNavKeyPress() && !this.list.isVisible()){
13475             this.fireEvent("specialkey", this, e);
13476         }
13477     },
13478
13479     // private
13480     onResize: function(w, h){
13481 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13482 //        
13483 //        if(typeof w != 'number'){
13484 //            // we do not handle it!?!?
13485 //            return;
13486 //        }
13487 //        var tw = this.trigger.getWidth();
13488 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13489 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13490 //        var x = w - tw;
13491 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13492 //            
13493 //        //this.trigger.setStyle('left', x+'px');
13494 //        
13495 //        if(this.list && this.listWidth === undefined){
13496 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13497 //            this.list.setWidth(lw);
13498 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13499 //        }
13500         
13501     
13502         
13503     },
13504
13505     /**
13506      * Allow or prevent the user from directly editing the field text.  If false is passed,
13507      * the user will only be able to select from the items defined in the dropdown list.  This method
13508      * is the runtime equivalent of setting the 'editable' config option at config time.
13509      * @param {Boolean} value True to allow the user to directly edit the field text
13510      */
13511     setEditable : function(value){
13512         if(value == this.editable){
13513             return;
13514         }
13515         this.editable = value;
13516         if(!value){
13517             this.inputEl().dom.setAttribute('readOnly', true);
13518             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13519             this.inputEl().addClass('x-combo-noedit');
13520         }else{
13521             this.inputEl().dom.setAttribute('readOnly', false);
13522             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13523             this.inputEl().removeClass('x-combo-noedit');
13524         }
13525     },
13526
13527     // private
13528     
13529     onBeforeLoad : function(combo,opts){
13530         if(!this.hasFocus){
13531             return;
13532         }
13533          if (!opts.add) {
13534             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13535          }
13536         this.restrictHeight();
13537         this.selectedIndex = -1;
13538     },
13539
13540     // private
13541     onLoad : function(){
13542         
13543         this.hasQuery = false;
13544         
13545         if(!this.hasFocus){
13546             return;
13547         }
13548         
13549         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13550             this.loading.hide();
13551         }
13552         
13553         if(this.store.getCount() > 0){
13554             
13555             this.expand();
13556             this.restrictHeight();
13557             if(this.lastQuery == this.allQuery){
13558                 if(this.editable && !this.tickable){
13559                     this.inputEl().dom.select();
13560                 }
13561                 
13562                 if(
13563                     !this.selectByValue(this.value, true) &&
13564                     this.autoFocus && 
13565                     (
13566                         !this.store.lastOptions ||
13567                         typeof(this.store.lastOptions.add) == 'undefined' || 
13568                         this.store.lastOptions.add != true
13569                     )
13570                 ){
13571                     this.select(0, true);
13572                 }
13573             }else{
13574                 if(this.autoFocus){
13575                     this.selectNext();
13576                 }
13577                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13578                     this.taTask.delay(this.typeAheadDelay);
13579                 }
13580             }
13581         }else{
13582             this.onEmptyResults();
13583         }
13584         
13585         //this.el.focus();
13586     },
13587     // private
13588     onLoadException : function()
13589     {
13590         this.hasQuery = false;
13591         
13592         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13593             this.loading.hide();
13594         }
13595         
13596         if(this.tickable && this.editable){
13597             return;
13598         }
13599         
13600         this.collapse();
13601         // only causes errors at present
13602         //Roo.log(this.store.reader.jsonData);
13603         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13604             // fixme
13605             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13606         //}
13607         
13608         
13609     },
13610     // private
13611     onTypeAhead : function(){
13612         if(this.store.getCount() > 0){
13613             var r = this.store.getAt(0);
13614             var newValue = r.data[this.displayField];
13615             var len = newValue.length;
13616             var selStart = this.getRawValue().length;
13617             
13618             if(selStart != len){
13619                 this.setRawValue(newValue);
13620                 this.selectText(selStart, newValue.length);
13621             }
13622         }
13623     },
13624
13625     // private
13626     onSelect : function(record, index){
13627         
13628         if(this.fireEvent('beforeselect', this, record, index) !== false){
13629         
13630             this.setFromData(index > -1 ? record.data : false);
13631             
13632             this.collapse();
13633             this.fireEvent('select', this, record, index);
13634         }
13635     },
13636
13637     /**
13638      * Returns the currently selected field value or empty string if no value is set.
13639      * @return {String} value The selected value
13640      */
13641     getValue : function()
13642     {
13643         if(Roo.isIOS && this.useNativeIOS){
13644             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13645         }
13646         
13647         if(this.multiple){
13648             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13649         }
13650         
13651         if(this.valueField){
13652             return typeof this.value != 'undefined' ? this.value : '';
13653         }else{
13654             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13655         }
13656     },
13657     
13658     getRawValue : function()
13659     {
13660         if(Roo.isIOS && this.useNativeIOS){
13661             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13662         }
13663         
13664         var v = this.inputEl().getValue();
13665         
13666         return v;
13667     },
13668
13669     /**
13670      * Clears any text/value currently set in the field
13671      */
13672     clearValue : function(){
13673         
13674         if(this.hiddenField){
13675             this.hiddenField.dom.value = '';
13676         }
13677         this.value = '';
13678         this.setRawValue('');
13679         this.lastSelectionText = '';
13680         this.lastData = false;
13681         
13682         var close = this.closeTriggerEl();
13683         
13684         if(close){
13685             close.hide();
13686         }
13687         
13688         this.validate();
13689         
13690     },
13691
13692     /**
13693      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13694      * will be displayed in the field.  If the value does not match the data value of an existing item,
13695      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13696      * Otherwise the field will be blank (although the value will still be set).
13697      * @param {String} value The value to match
13698      */
13699     setValue : function(v)
13700     {
13701         if(Roo.isIOS && this.useNativeIOS){
13702             this.setIOSValue(v);
13703             return;
13704         }
13705         
13706         if(this.multiple){
13707             this.syncValue();
13708             return;
13709         }
13710         
13711         var text = v;
13712         if(this.valueField){
13713             var r = this.findRecord(this.valueField, v);
13714             if(r){
13715                 text = r.data[this.displayField];
13716             }else if(this.valueNotFoundText !== undefined){
13717                 text = this.valueNotFoundText;
13718             }
13719         }
13720         this.lastSelectionText = text;
13721         if(this.hiddenField){
13722             this.hiddenField.dom.value = v;
13723         }
13724         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13725         this.value = v;
13726         
13727         var close = this.closeTriggerEl();
13728         
13729         if(close){
13730             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13731         }
13732         
13733         this.validate();
13734     },
13735     /**
13736      * @property {Object} the last set data for the element
13737      */
13738     
13739     lastData : false,
13740     /**
13741      * Sets the value of the field based on a object which is related to the record format for the store.
13742      * @param {Object} value the value to set as. or false on reset?
13743      */
13744     setFromData : function(o){
13745         
13746         if(this.multiple){
13747             this.addItem(o);
13748             return;
13749         }
13750             
13751         var dv = ''; // display value
13752         var vv = ''; // value value..
13753         this.lastData = o;
13754         if (this.displayField) {
13755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13756         } else {
13757             // this is an error condition!!!
13758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13759         }
13760         
13761         if(this.valueField){
13762             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13763         }
13764         
13765         var close = this.closeTriggerEl();
13766         
13767         if(close){
13768             if(dv.length || vv * 1 > 0){
13769                 close.show() ;
13770                 this.blockFocus=true;
13771             } else {
13772                 close.hide();
13773             }             
13774         }
13775         
13776         if(this.hiddenField){
13777             this.hiddenField.dom.value = vv;
13778             
13779             this.lastSelectionText = dv;
13780             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13781             this.value = vv;
13782             return;
13783         }
13784         // no hidden field.. - we store the value in 'value', but still display
13785         // display field!!!!
13786         this.lastSelectionText = dv;
13787         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13788         this.value = vv;
13789         
13790         
13791         
13792     },
13793     // private
13794     reset : function(){
13795         // overridden so that last data is reset..
13796         
13797         if(this.multiple){
13798             this.clearItem();
13799             return;
13800         }
13801         
13802         this.setValue(this.originalValue);
13803         //this.clearInvalid();
13804         this.lastData = false;
13805         if (this.view) {
13806             this.view.clearSelections();
13807         }
13808         
13809         this.validate();
13810     },
13811     // private
13812     findRecord : function(prop, value){
13813         var record;
13814         if(this.store.getCount() > 0){
13815             this.store.each(function(r){
13816                 if(r.data[prop] == value){
13817                     record = r;
13818                     return false;
13819                 }
13820                 return true;
13821             });
13822         }
13823         return record;
13824     },
13825     
13826     getName: function()
13827     {
13828         // returns hidden if it's set..
13829         if (!this.rendered) {return ''};
13830         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13831         
13832     },
13833     // private
13834     onViewMove : function(e, t){
13835         this.inKeyMode = false;
13836     },
13837
13838     // private
13839     onViewOver : function(e, t){
13840         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13841             return;
13842         }
13843         var item = this.view.findItemFromChild(t);
13844         
13845         if(item){
13846             var index = this.view.indexOf(item);
13847             this.select(index, false);
13848         }
13849     },
13850
13851     // private
13852     onViewClick : function(view, doFocus, el, e)
13853     {
13854         var index = this.view.getSelectedIndexes()[0];
13855         
13856         var r = this.store.getAt(index);
13857         
13858         if(this.tickable){
13859             
13860             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13861                 return;
13862             }
13863             
13864             var rm = false;
13865             var _this = this;
13866             
13867             Roo.each(this.tickItems, function(v,k){
13868                 
13869                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13870                     Roo.log(v);
13871                     _this.tickItems.splice(k, 1);
13872                     
13873                     if(typeof(e) == 'undefined' && view == false){
13874                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13875                     }
13876                     
13877                     rm = true;
13878                     return;
13879                 }
13880             });
13881             
13882             if(rm){
13883                 return;
13884             }
13885             
13886             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13887                 this.tickItems.push(r.data);
13888             }
13889             
13890             if(typeof(e) == 'undefined' && view == false){
13891                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13892             }
13893                     
13894             return;
13895         }
13896         
13897         if(r){
13898             this.onSelect(r, index);
13899         }
13900         if(doFocus !== false && !this.blockFocus){
13901             this.inputEl().focus();
13902         }
13903     },
13904
13905     // private
13906     restrictHeight : function(){
13907         //this.innerList.dom.style.height = '';
13908         //var inner = this.innerList.dom;
13909         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13910         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13911         //this.list.beginUpdate();
13912         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13913         this.list.alignTo(this.inputEl(), this.listAlign);
13914         this.list.alignTo(this.inputEl(), this.listAlign);
13915         //this.list.endUpdate();
13916     },
13917
13918     // private
13919     onEmptyResults : function(){
13920         
13921         if(this.tickable && this.editable){
13922             this.restrictHeight();
13923             return;
13924         }
13925         
13926         this.collapse();
13927     },
13928
13929     /**
13930      * Returns true if the dropdown list is expanded, else false.
13931      */
13932     isExpanded : function(){
13933         return this.list.isVisible();
13934     },
13935
13936     /**
13937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13939      * @param {String} value The data value of the item to select
13940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13941      * selected item if it is not currently in view (defaults to true)
13942      * @return {Boolean} True if the value matched an item in the list, else false
13943      */
13944     selectByValue : function(v, scrollIntoView){
13945         if(v !== undefined && v !== null){
13946             var r = this.findRecord(this.valueField || this.displayField, v);
13947             if(r){
13948                 this.select(this.store.indexOf(r), scrollIntoView);
13949                 return true;
13950             }
13951         }
13952         return false;
13953     },
13954
13955     /**
13956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13958      * @param {Number} index The zero-based index of the list item to select
13959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13960      * selected item if it is not currently in view (defaults to true)
13961      */
13962     select : function(index, scrollIntoView){
13963         this.selectedIndex = index;
13964         this.view.select(index);
13965         if(scrollIntoView !== false){
13966             var el = this.view.getNode(index);
13967             /*
13968              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13969              */
13970             if(el){
13971                 this.list.scrollChildIntoView(el, false);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectNext : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex < ct-1){
13983                 this.select(this.selectedIndex+1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     selectPrev : function(){
13990         var ct = this.store.getCount();
13991         if(ct > 0){
13992             if(this.selectedIndex == -1){
13993                 this.select(0);
13994             }else if(this.selectedIndex != 0){
13995                 this.select(this.selectedIndex-1);
13996             }
13997         }
13998     },
13999
14000     // private
14001     onKeyUp : function(e){
14002         if(this.editable !== false && !e.isSpecialKey()){
14003             this.lastKey = e.getKey();
14004             this.dqTask.delay(this.queryDelay);
14005         }
14006     },
14007
14008     // private
14009     validateBlur : function(){
14010         return !this.list || !this.list.isVisible();   
14011     },
14012
14013     // private
14014     initQuery : function(){
14015         
14016         var v = this.getRawValue();
14017         
14018         if(this.tickable && this.editable){
14019             v = this.tickableInputEl().getValue();
14020         }
14021         
14022         this.doQuery(v);
14023     },
14024
14025     // private
14026     doForce : function(){
14027         if(this.inputEl().dom.value.length > 0){
14028             this.inputEl().dom.value =
14029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14030              
14031         }
14032     },
14033
14034     /**
14035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14036      * query allowing the query action to be canceled if needed.
14037      * @param {String} query The SQL query to execute
14038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14040      * saved in the current store (defaults to false)
14041      */
14042     doQuery : function(q, forceAll){
14043         
14044         if(q === undefined || q === null){
14045             q = '';
14046         }
14047         var qe = {
14048             query: q,
14049             forceAll: forceAll,
14050             combo: this,
14051             cancel:false
14052         };
14053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14054             return false;
14055         }
14056         q = qe.query;
14057         
14058         forceAll = qe.forceAll;
14059         if(forceAll === true || (q.length >= this.minChars)){
14060             
14061             this.hasQuery = true;
14062             
14063             if(this.lastQuery != q || this.alwaysQuery){
14064                 this.lastQuery = q;
14065                 if(this.mode == 'local'){
14066                     this.selectedIndex = -1;
14067                     if(forceAll){
14068                         this.store.clearFilter();
14069                     }else{
14070                         
14071                         if(this.specialFilter){
14072                             this.fireEvent('specialfilter', this);
14073                             this.onLoad();
14074                             return;
14075                         }
14076                         
14077                         this.store.filter(this.displayField, q);
14078                     }
14079                     
14080                     this.store.fireEvent("datachanged", this.store);
14081                     
14082                     this.onLoad();
14083                     
14084                     
14085                 }else{
14086                     
14087                     this.store.baseParams[this.queryParam] = q;
14088                     
14089                     var options = {params : this.getParams(q)};
14090                     
14091                     if(this.loadNext){
14092                         options.add = true;
14093                         options.params.start = this.page * this.pageSize;
14094                     }
14095                     
14096                     this.store.load(options);
14097                     
14098                     /*
14099                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14100                      *  we should expand the list on onLoad
14101                      *  so command out it
14102                      */
14103 //                    this.expand();
14104                 }
14105             }else{
14106                 this.selectedIndex = -1;
14107                 this.onLoad();   
14108             }
14109         }
14110         
14111         this.loadNext = false;
14112     },
14113     
14114     // private
14115     getParams : function(q){
14116         var p = {};
14117         //p[this.queryParam] = q;
14118         
14119         if(this.pageSize){
14120             p.start = 0;
14121             p.limit = this.pageSize;
14122         }
14123         return p;
14124     },
14125
14126     /**
14127      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14128      */
14129     collapse : function(){
14130         if(!this.isExpanded()){
14131             return;
14132         }
14133         
14134         this.list.hide();
14135         
14136         this.hasFocus = false;
14137         
14138         if(this.tickable){
14139             this.okBtn.hide();
14140             this.cancelBtn.hide();
14141             this.trigger.show();
14142             
14143             if(this.editable){
14144                 this.tickableInputEl().dom.value = '';
14145                 this.tickableInputEl().blur();
14146             }
14147             
14148         }
14149         
14150         Roo.get(document).un('mousedown', this.collapseIf, this);
14151         Roo.get(document).un('mousewheel', this.collapseIf, this);
14152         if (!this.editable) {
14153             Roo.get(document).un('keydown', this.listKeyPress, this);
14154         }
14155         this.fireEvent('collapse', this);
14156         
14157         this.validate();
14158     },
14159
14160     // private
14161     collapseIf : function(e){
14162         var in_combo  = e.within(this.el);
14163         var in_list =  e.within(this.list);
14164         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14165         
14166         if (in_combo || in_list || is_list) {
14167             //e.stopPropagation();
14168             return;
14169         }
14170         
14171         if(this.tickable){
14172             this.onTickableFooterButtonClick(e, false, false);
14173         }
14174
14175         this.collapse();
14176         
14177     },
14178
14179     /**
14180      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14181      */
14182     expand : function(){
14183        
14184         if(this.isExpanded() || !this.hasFocus){
14185             return;
14186         }
14187         
14188         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14189         this.list.setWidth(lw);
14190         
14191         Roo.log('expand');
14192         
14193         this.list.show();
14194         
14195         this.restrictHeight();
14196         
14197         if(this.tickable){
14198             
14199             this.tickItems = Roo.apply([], this.item);
14200             
14201             this.okBtn.show();
14202             this.cancelBtn.show();
14203             this.trigger.hide();
14204             
14205             if(this.editable){
14206                 this.tickableInputEl().focus();
14207             }
14208             
14209         }
14210         
14211         Roo.get(document).on('mousedown', this.collapseIf, this);
14212         Roo.get(document).on('mousewheel', this.collapseIf, this);
14213         if (!this.editable) {
14214             Roo.get(document).on('keydown', this.listKeyPress, this);
14215         }
14216         
14217         this.fireEvent('expand', this);
14218     },
14219
14220     // private
14221     // Implements the default empty TriggerField.onTriggerClick function
14222     onTriggerClick : function(e)
14223     {
14224         Roo.log('trigger click');
14225         
14226         if(this.disabled || !this.triggerList){
14227             return;
14228         }
14229         
14230         this.page = 0;
14231         this.loadNext = false;
14232         
14233         if(this.isExpanded()){
14234             this.collapse();
14235             if (!this.blockFocus) {
14236                 this.inputEl().focus();
14237             }
14238             
14239         }else {
14240             this.hasFocus = true;
14241             if(this.triggerAction == 'all') {
14242                 this.doQuery(this.allQuery, true);
14243             } else {
14244                 this.doQuery(this.getRawValue());
14245             }
14246             if (!this.blockFocus) {
14247                 this.inputEl().focus();
14248             }
14249         }
14250     },
14251     
14252     onTickableTriggerClick : function(e)
14253     {
14254         if(this.disabled){
14255             return;
14256         }
14257         
14258         this.page = 0;
14259         this.loadNext = false;
14260         this.hasFocus = true;
14261         
14262         if(this.triggerAction == 'all') {
14263             this.doQuery(this.allQuery, true);
14264         } else {
14265             this.doQuery(this.getRawValue());
14266         }
14267     },
14268     
14269     onSearchFieldClick : function(e)
14270     {
14271         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14272             this.onTickableFooterButtonClick(e, false, false);
14273             return;
14274         }
14275         
14276         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14277             return;
14278         }
14279         
14280         this.page = 0;
14281         this.loadNext = false;
14282         this.hasFocus = true;
14283         
14284         if(this.triggerAction == 'all') {
14285             this.doQuery(this.allQuery, true);
14286         } else {
14287             this.doQuery(this.getRawValue());
14288         }
14289     },
14290     
14291     listKeyPress : function(e)
14292     {
14293         //Roo.log('listkeypress');
14294         // scroll to first matching element based on key pres..
14295         if (e.isSpecialKey()) {
14296             return false;
14297         }
14298         var k = String.fromCharCode(e.getKey()).toUpperCase();
14299         //Roo.log(k);
14300         var match  = false;
14301         var csel = this.view.getSelectedNodes();
14302         var cselitem = false;
14303         if (csel.length) {
14304             var ix = this.view.indexOf(csel[0]);
14305             cselitem  = this.store.getAt(ix);
14306             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14307                 cselitem = false;
14308             }
14309             
14310         }
14311         
14312         this.store.each(function(v) { 
14313             if (cselitem) {
14314                 // start at existing selection.
14315                 if (cselitem.id == v.id) {
14316                     cselitem = false;
14317                 }
14318                 return true;
14319             }
14320                 
14321             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14322                 match = this.store.indexOf(v);
14323                 return false;
14324             }
14325             return true;
14326         }, this);
14327         
14328         if (match === false) {
14329             return true; // no more action?
14330         }
14331         // scroll to?
14332         this.view.select(match);
14333         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14334         sn.scrollIntoView(sn.dom.parentNode, false);
14335     },
14336     
14337     onViewScroll : function(e, t){
14338         
14339         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){
14340             return;
14341         }
14342         
14343         this.hasQuery = true;
14344         
14345         this.loading = this.list.select('.loading', true).first();
14346         
14347         if(this.loading === null){
14348             this.list.createChild({
14349                 tag: 'div',
14350                 cls: 'loading roo-select2-more-results roo-select2-active',
14351                 html: 'Loading more results...'
14352             });
14353             
14354             this.loading = this.list.select('.loading', true).first();
14355             
14356             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14357             
14358             this.loading.hide();
14359         }
14360         
14361         this.loading.show();
14362         
14363         var _combo = this;
14364         
14365         this.page++;
14366         this.loadNext = true;
14367         
14368         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14369         
14370         return;
14371     },
14372     
14373     addItem : function(o)
14374     {   
14375         var dv = ''; // display value
14376         
14377         if (this.displayField) {
14378             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14379         } else {
14380             // this is an error condition!!!
14381             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14382         }
14383         
14384         if(!dv.length){
14385             return;
14386         }
14387         
14388         var choice = this.choices.createChild({
14389             tag: 'li',
14390             cls: 'roo-select2-search-choice',
14391             cn: [
14392                 {
14393                     tag: 'div',
14394                     html: dv
14395                 },
14396                 {
14397                     tag: 'a',
14398                     href: '#',
14399                     cls: 'roo-select2-search-choice-close fa fa-times',
14400                     tabindex: '-1'
14401                 }
14402             ]
14403             
14404         }, this.searchField);
14405         
14406         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14407         
14408         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14409         
14410         this.item.push(o);
14411         
14412         this.lastData = o;
14413         
14414         this.syncValue();
14415         
14416         this.inputEl().dom.value = '';
14417         
14418         this.validate();
14419     },
14420     
14421     onRemoveItem : function(e, _self, o)
14422     {
14423         e.preventDefault();
14424         
14425         this.lastItem = Roo.apply([], this.item);
14426         
14427         var index = this.item.indexOf(o.data) * 1;
14428         
14429         if( index < 0){
14430             Roo.log('not this item?!');
14431             return;
14432         }
14433         
14434         this.item.splice(index, 1);
14435         o.item.remove();
14436         
14437         this.syncValue();
14438         
14439         this.fireEvent('remove', this, e);
14440         
14441         this.validate();
14442         
14443     },
14444     
14445     syncValue : function()
14446     {
14447         if(!this.item.length){
14448             this.clearValue();
14449             return;
14450         }
14451             
14452         var value = [];
14453         var _this = this;
14454         Roo.each(this.item, function(i){
14455             if(_this.valueField){
14456                 value.push(i[_this.valueField]);
14457                 return;
14458             }
14459
14460             value.push(i);
14461         });
14462
14463         this.value = value.join(',');
14464
14465         if(this.hiddenField){
14466             this.hiddenField.dom.value = this.value;
14467         }
14468         
14469         this.store.fireEvent("datachanged", this.store);
14470         
14471         this.validate();
14472     },
14473     
14474     clearItem : function()
14475     {
14476         if(!this.multiple){
14477             return;
14478         }
14479         
14480         this.item = [];
14481         
14482         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14483            c.remove();
14484         });
14485         
14486         this.syncValue();
14487         
14488         this.validate();
14489         
14490         if(this.tickable && !Roo.isTouch){
14491             this.view.refresh();
14492         }
14493     },
14494     
14495     inputEl: function ()
14496     {
14497         if(Roo.isIOS && this.useNativeIOS){
14498             return this.el.select('select.roo-ios-select', true).first();
14499         }
14500         
14501         if(Roo.isTouch && this.mobileTouchView){
14502             return this.el.select('input.form-control',true).first();
14503         }
14504         
14505         if(this.tickable){
14506             return this.searchField;
14507         }
14508         
14509         return this.el.select('input.form-control',true).first();
14510     },
14511     
14512     onTickableFooterButtonClick : function(e, btn, el)
14513     {
14514         e.preventDefault();
14515         
14516         this.lastItem = Roo.apply([], this.item);
14517         
14518         if(btn && btn.name == 'cancel'){
14519             this.tickItems = Roo.apply([], this.item);
14520             this.collapse();
14521             return;
14522         }
14523         
14524         this.clearItem();
14525         
14526         var _this = this;
14527         
14528         Roo.each(this.tickItems, function(o){
14529             _this.addItem(o);
14530         });
14531         
14532         this.collapse();
14533         
14534     },
14535     
14536     validate : function()
14537     {
14538         var v = this.getRawValue();
14539         
14540         if(this.multiple){
14541             v = this.getValue();
14542         }
14543         
14544         if(this.disabled || this.allowBlank || v.length){
14545             this.markValid();
14546             return true;
14547         }
14548         
14549         this.markInvalid();
14550         return false;
14551     },
14552     
14553     tickableInputEl : function()
14554     {
14555         if(!this.tickable || !this.editable){
14556             return this.inputEl();
14557         }
14558         
14559         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14560     },
14561     
14562     
14563     getAutoCreateTouchView : function()
14564     {
14565         var id = Roo.id();
14566         
14567         var cfg = {
14568             cls: 'form-group' //input-group
14569         };
14570         
14571         var input =  {
14572             tag: 'input',
14573             id : id,
14574             type : this.inputType,
14575             cls : 'form-control x-combo-noedit',
14576             autocomplete: 'new-password',
14577             placeholder : this.placeholder || '',
14578             readonly : true
14579         };
14580         
14581         if (this.name) {
14582             input.name = this.name;
14583         }
14584         
14585         if (this.size) {
14586             input.cls += ' input-' + this.size;
14587         }
14588         
14589         if (this.disabled) {
14590             input.disabled = true;
14591         }
14592         
14593         var inputblock = {
14594             cls : '',
14595             cn : [
14596                 input
14597             ]
14598         };
14599         
14600         if(this.before){
14601             inputblock.cls += ' input-group';
14602             
14603             inputblock.cn.unshift({
14604                 tag :'span',
14605                 cls : 'input-group-addon',
14606                 html : this.before
14607             });
14608         }
14609         
14610         if(this.removable && !this.multiple){
14611             inputblock.cls += ' roo-removable';
14612             
14613             inputblock.cn.push({
14614                 tag: 'button',
14615                 html : 'x',
14616                 cls : 'roo-combo-removable-btn close'
14617             });
14618         }
14619
14620         if(this.hasFeedback && !this.allowBlank){
14621             
14622             inputblock.cls += ' has-feedback';
14623             
14624             inputblock.cn.push({
14625                 tag: 'span',
14626                 cls: 'glyphicon form-control-feedback'
14627             });
14628             
14629         }
14630         
14631         if (this.after) {
14632             
14633             inputblock.cls += (this.before) ? '' : ' input-group';
14634             
14635             inputblock.cn.push({
14636                 tag :'span',
14637                 cls : 'input-group-addon',
14638                 html : this.after
14639             });
14640         }
14641
14642         var box = {
14643             tag: 'div',
14644             cn: [
14645                 {
14646                     tag: 'input',
14647                     type : 'hidden',
14648                     cls: 'form-hidden-field'
14649                 },
14650                 inputblock
14651             ]
14652             
14653         };
14654         
14655         if(this.multiple){
14656             box = {
14657                 tag: 'div',
14658                 cn: [
14659                     {
14660                         tag: 'input',
14661                         type : 'hidden',
14662                         cls: 'form-hidden-field'
14663                     },
14664                     {
14665                         tag: 'ul',
14666                         cls: 'roo-select2-choices',
14667                         cn:[
14668                             {
14669                                 tag: 'li',
14670                                 cls: 'roo-select2-search-field',
14671                                 cn: [
14672
14673                                     inputblock
14674                                 ]
14675                             }
14676                         ]
14677                     }
14678                 ]
14679             }
14680         };
14681         
14682         var combobox = {
14683             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14684             cn: [
14685                 box
14686             ]
14687         };
14688         
14689         if(!this.multiple && this.showToggleBtn){
14690             
14691             var caret = {
14692                         tag: 'span',
14693                         cls: 'caret'
14694             };
14695             
14696             if (this.caret != false) {
14697                 caret = {
14698                      tag: 'i',
14699                      cls: 'fa fa-' + this.caret
14700                 };
14701                 
14702             }
14703             
14704             combobox.cn.push({
14705                 tag :'span',
14706                 cls : 'input-group-addon btn dropdown-toggle',
14707                 cn : [
14708                     caret,
14709                     {
14710                         tag: 'span',
14711                         cls: 'combobox-clear',
14712                         cn  : [
14713                             {
14714                                 tag : 'i',
14715                                 cls: 'icon-remove'
14716                             }
14717                         ]
14718                     }
14719                 ]
14720
14721             })
14722         }
14723         
14724         if(this.multiple){
14725             combobox.cls += ' roo-select2-container-multi';
14726         }
14727         
14728         var align = this.labelAlign || this.parentLabelAlign();
14729         
14730         if (align ==='left' && this.fieldLabel.length) {
14731
14732             cfg.cn = [
14733                 {
14734                    tag : 'i',
14735                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14736                    tooltip : 'This field is required'
14737                 },
14738                 {
14739                     tag: 'label',
14740                     cls : 'control-label',
14741                     html : this.fieldLabel
14742
14743                 },
14744                 {
14745                     cls : '', 
14746                     cn: [
14747                         combobox
14748                     ]
14749                 }
14750             ];
14751             
14752             var labelCfg = cfg.cn[1];
14753             var contentCfg = cfg.cn[2];
14754             
14755
14756             if(this.indicatorpos == 'right'){
14757                 cfg.cn = [
14758                     {
14759                         tag: 'label',
14760                         'for' :  id,
14761                         cls : 'control-label',
14762                         cn : [
14763                             {
14764                                 tag : 'span',
14765                                 html : this.fieldLabel
14766                             },
14767                             {
14768                                 tag : 'i',
14769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14770                                 tooltip : 'This field is required'
14771                             }
14772                         ]
14773                     },
14774                     {
14775                         cls : "",
14776                         cn: [
14777                             combobox
14778                         ]
14779                     }
14780
14781                 ];
14782                 
14783                 labelCfg = cfg.cn[0];
14784                 contentCfg = cfg.cn[1];
14785             }
14786             
14787            
14788             
14789             if(this.labelWidth > 12){
14790                 labelCfg.style = "width: " + this.labelWidth + 'px';
14791             }
14792             
14793             if(this.labelWidth < 13 && this.labelmd == 0){
14794                 this.labelmd = this.labelWidth;
14795             }
14796             
14797             if(this.labellg > 0){
14798                 labelCfg.cls += ' col-lg-' + this.labellg;
14799                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14800             }
14801             
14802             if(this.labelmd > 0){
14803                 labelCfg.cls += ' col-md-' + this.labelmd;
14804                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14805             }
14806             
14807             if(this.labelsm > 0){
14808                 labelCfg.cls += ' col-sm-' + this.labelsm;
14809                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14810             }
14811             
14812             if(this.labelxs > 0){
14813                 labelCfg.cls += ' col-xs-' + this.labelxs;
14814                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14815             }
14816                 
14817                 
14818         } else if ( this.fieldLabel.length) {
14819             cfg.cn = [
14820                 {
14821                    tag : 'i',
14822                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14823                    tooltip : 'This field is required'
14824                 },
14825                 {
14826                     tag: 'label',
14827                     cls : 'control-label',
14828                     html : this.fieldLabel
14829
14830                 },
14831                 {
14832                     cls : '', 
14833                     cn: [
14834                         combobox
14835                     ]
14836                 }
14837             ];
14838             
14839             if(this.indicatorpos == 'right'){
14840                 cfg.cn = [
14841                     {
14842                         tag: 'label',
14843                         cls : 'control-label',
14844                         html : this.fieldLabel,
14845                         cn : [
14846                             {
14847                                tag : 'i',
14848                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14849                                tooltip : 'This field is required'
14850                             }
14851                         ]
14852                     },
14853                     {
14854                         cls : '', 
14855                         cn: [
14856                             combobox
14857                         ]
14858                     }
14859                 ];
14860             }
14861         } else {
14862             cfg.cn = combobox;    
14863         }
14864         
14865         
14866         var settings = this;
14867         
14868         ['xs','sm','md','lg'].map(function(size){
14869             if (settings[size]) {
14870                 cfg.cls += ' col-' + size + '-' + settings[size];
14871             }
14872         });
14873         
14874         return cfg;
14875     },
14876     
14877     initTouchView : function()
14878     {
14879         this.renderTouchView();
14880         
14881         this.touchViewEl.on('scroll', function(){
14882             this.el.dom.scrollTop = 0;
14883         }, this);
14884         
14885         this.originalValue = this.getValue();
14886         
14887         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14888         
14889         this.inputEl().on("click", this.showTouchView, this);
14890         if (this.triggerEl) {
14891             this.triggerEl.on("click", this.showTouchView, this);
14892         }
14893         
14894         
14895         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14896         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14897         
14898         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14899         
14900         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14901         this.store.on('load', this.onTouchViewLoad, this);
14902         this.store.on('loadexception', this.onTouchViewLoadException, this);
14903         
14904         if(this.hiddenName){
14905             
14906             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14907             
14908             this.hiddenField.dom.value =
14909                 this.hiddenValue !== undefined ? this.hiddenValue :
14910                 this.value !== undefined ? this.value : '';
14911         
14912             this.el.dom.removeAttribute('name');
14913             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14914         }
14915         
14916         if(this.multiple){
14917             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14918             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14919         }
14920         
14921         if(this.removable && !this.multiple){
14922             var close = this.closeTriggerEl();
14923             if(close){
14924                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14925                 close.on('click', this.removeBtnClick, this, close);
14926             }
14927         }
14928         /*
14929          * fix the bug in Safari iOS8
14930          */
14931         this.inputEl().on("focus", function(e){
14932             document.activeElement.blur();
14933         }, this);
14934         
14935         return;
14936         
14937         
14938     },
14939     
14940     renderTouchView : function()
14941     {
14942         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14943         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14944         
14945         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14946         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14947         
14948         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14949         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14950         this.touchViewBodyEl.setStyle('overflow', 'auto');
14951         
14952         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14953         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14954         
14955         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14956         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14957         
14958     },
14959     
14960     showTouchView : function()
14961     {
14962         if(this.disabled){
14963             return;
14964         }
14965         
14966         this.touchViewHeaderEl.hide();
14967
14968         if(this.modalTitle.length){
14969             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14970             this.touchViewHeaderEl.show();
14971         }
14972
14973         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14974         this.touchViewEl.show();
14975
14976         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14977         
14978         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14979         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14980
14981         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14982
14983         if(this.modalTitle.length){
14984             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14985         }
14986         
14987         this.touchViewBodyEl.setHeight(bodyHeight);
14988
14989         if(this.animate){
14990             var _this = this;
14991             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14992         }else{
14993             this.touchViewEl.addClass('in');
14994         }
14995
14996         this.doTouchViewQuery();
14997         
14998     },
14999     
15000     hideTouchView : function()
15001     {
15002         this.touchViewEl.removeClass('in');
15003
15004         if(this.animate){
15005             var _this = this;
15006             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15007         }else{
15008             this.touchViewEl.setStyle('display', 'none');
15009         }
15010         
15011     },
15012     
15013     setTouchViewValue : function()
15014     {
15015         if(this.multiple){
15016             this.clearItem();
15017         
15018             var _this = this;
15019
15020             Roo.each(this.tickItems, function(o){
15021                 this.addItem(o);
15022             }, this);
15023         }
15024         
15025         this.hideTouchView();
15026     },
15027     
15028     doTouchViewQuery : function()
15029     {
15030         var qe = {
15031             query: '',
15032             forceAll: true,
15033             combo: this,
15034             cancel:false
15035         };
15036         
15037         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15038             return false;
15039         }
15040         
15041         if(!this.alwaysQuery || this.mode == 'local'){
15042             this.onTouchViewLoad();
15043             return;
15044         }
15045         
15046         this.store.load();
15047     },
15048     
15049     onTouchViewBeforeLoad : function(combo,opts)
15050     {
15051         return;
15052     },
15053
15054     // private
15055     onTouchViewLoad : function()
15056     {
15057         if(this.store.getCount() < 1){
15058             this.onTouchViewEmptyResults();
15059             return;
15060         }
15061         
15062         this.clearTouchView();
15063         
15064         var rawValue = this.getRawValue();
15065         
15066         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15067         
15068         this.tickItems = [];
15069         
15070         this.store.data.each(function(d, rowIndex){
15071             var row = this.touchViewListGroup.createChild(template);
15072             
15073             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15074                 row.addClass(d.data.cls);
15075             }
15076             
15077             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15078                 var cfg = {
15079                     data : d.data,
15080                     html : d.data[this.displayField]
15081                 };
15082                 
15083                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15084                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15085                 }
15086             }
15087             row.removeClass('selected');
15088             if(!this.multiple && this.valueField &&
15089                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15090             {
15091                 // radio buttons..
15092                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15093                 row.addClass('selected');
15094             }
15095             
15096             if(this.multiple && this.valueField &&
15097                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15098             {
15099                 
15100                 // checkboxes...
15101                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15102                 this.tickItems.push(d.data);
15103             }
15104             
15105             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15106             
15107         }, this);
15108         
15109         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15110         
15111         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15112
15113         if(this.modalTitle.length){
15114             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15115         }
15116
15117         var listHeight = this.touchViewListGroup.getHeight();
15118         
15119         var _this = this;
15120         
15121         if(firstChecked && listHeight > bodyHeight){
15122             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15123         }
15124         
15125     },
15126     
15127     onTouchViewLoadException : function()
15128     {
15129         this.hideTouchView();
15130     },
15131     
15132     onTouchViewEmptyResults : function()
15133     {
15134         this.clearTouchView();
15135         
15136         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15137         
15138         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15139         
15140     },
15141     
15142     clearTouchView : function()
15143     {
15144         this.touchViewListGroup.dom.innerHTML = '';
15145     },
15146     
15147     onTouchViewClick : function(e, el, o)
15148     {
15149         e.preventDefault();
15150         
15151         var row = o.row;
15152         var rowIndex = o.rowIndex;
15153         
15154         var r = this.store.getAt(rowIndex);
15155         
15156         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15157             
15158             if(!this.multiple){
15159                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15160                     c.dom.removeAttribute('checked');
15161                 }, this);
15162
15163                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15164
15165                 this.setFromData(r.data);
15166
15167                 var close = this.closeTriggerEl();
15168
15169                 if(close){
15170                     close.show();
15171                 }
15172
15173                 this.hideTouchView();
15174
15175                 this.fireEvent('select', this, r, rowIndex);
15176
15177                 return;
15178             }
15179
15180             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15181                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15182                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15183                 return;
15184             }
15185
15186             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15187             this.addItem(r.data);
15188             this.tickItems.push(r.data);
15189         }
15190     },
15191     
15192     getAutoCreateNativeIOS : function()
15193     {
15194         var cfg = {
15195             cls: 'form-group' //input-group,
15196         };
15197         
15198         var combobox =  {
15199             tag: 'select',
15200             cls : 'roo-ios-select'
15201         };
15202         
15203         if (this.name) {
15204             combobox.name = this.name;
15205         }
15206         
15207         if (this.disabled) {
15208             combobox.disabled = true;
15209         }
15210         
15211         var settings = this;
15212         
15213         ['xs','sm','md','lg'].map(function(size){
15214             if (settings[size]) {
15215                 cfg.cls += ' col-' + size + '-' + settings[size];
15216             }
15217         });
15218         
15219         cfg.cn = combobox;
15220         
15221         return cfg;
15222         
15223     },
15224     
15225     initIOSView : function()
15226     {
15227         this.store.on('load', this.onIOSViewLoad, this);
15228         
15229         return;
15230     },
15231     
15232     onIOSViewLoad : function()
15233     {
15234         if(this.store.getCount() < 1){
15235             return;
15236         }
15237         
15238         this.clearIOSView();
15239         
15240         if(this.allowBlank) {
15241             
15242             var default_text = '-- SELECT --';
15243             
15244             if(this.placeholder.length){
15245                 default_text = this.placeholder;
15246             }
15247             
15248             if(this.emptyTitle.length){
15249                 default_text += ' - ' + this.emptyTitle + ' -';
15250             }
15251             
15252             var opt = this.inputEl().createChild({
15253                 tag: 'option',
15254                 value : 0,
15255                 html : default_text
15256             });
15257             
15258             var o = {};
15259             o[this.valueField] = 0;
15260             o[this.displayField] = default_text;
15261             
15262             this.ios_options.push({
15263                 data : o,
15264                 el : opt
15265             });
15266             
15267         }
15268         
15269         this.store.data.each(function(d, rowIndex){
15270             
15271             var html = '';
15272             
15273             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15274                 html = d.data[this.displayField];
15275             }
15276             
15277             var value = '';
15278             
15279             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15280                 value = d.data[this.valueField];
15281             }
15282             
15283             var option = {
15284                 tag: 'option',
15285                 value : value,
15286                 html : html
15287             };
15288             
15289             if(this.value == d.data[this.valueField]){
15290                 option['selected'] = true;
15291             }
15292             
15293             var opt = this.inputEl().createChild(option);
15294             
15295             this.ios_options.push({
15296                 data : d.data,
15297                 el : opt
15298             });
15299             
15300         }, this);
15301         
15302         this.inputEl().on('change', function(){
15303            this.fireEvent('select', this);
15304         }, this);
15305         
15306     },
15307     
15308     clearIOSView: function()
15309     {
15310         this.inputEl().dom.innerHTML = '';
15311         
15312         this.ios_options = [];
15313     },
15314     
15315     setIOSValue: function(v)
15316     {
15317         this.value = v;
15318         
15319         if(!this.ios_options){
15320             return;
15321         }
15322         
15323         Roo.each(this.ios_options, function(opts){
15324            
15325            opts.el.dom.removeAttribute('selected');
15326            
15327            if(opts.data[this.valueField] != v){
15328                return;
15329            }
15330            
15331            opts.el.dom.setAttribute('selected', true);
15332            
15333         }, this);
15334     }
15335
15336     /** 
15337     * @cfg {Boolean} grow 
15338     * @hide 
15339     */
15340     /** 
15341     * @cfg {Number} growMin 
15342     * @hide 
15343     */
15344     /** 
15345     * @cfg {Number} growMax 
15346     * @hide 
15347     */
15348     /**
15349      * @hide
15350      * @method autoSize
15351      */
15352 });
15353
15354 Roo.apply(Roo.bootstrap.ComboBox,  {
15355     
15356     header : {
15357         tag: 'div',
15358         cls: 'modal-header',
15359         cn: [
15360             {
15361                 tag: 'h4',
15362                 cls: 'modal-title'
15363             }
15364         ]
15365     },
15366     
15367     body : {
15368         tag: 'div',
15369         cls: 'modal-body',
15370         cn: [
15371             {
15372                 tag: 'ul',
15373                 cls: 'list-group'
15374             }
15375         ]
15376     },
15377     
15378     listItemRadio : {
15379         tag: 'li',
15380         cls: 'list-group-item',
15381         cn: [
15382             {
15383                 tag: 'span',
15384                 cls: 'roo-combobox-list-group-item-value'
15385             },
15386             {
15387                 tag: 'div',
15388                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15389                 cn: [
15390                     {
15391                         tag: 'input',
15392                         type: 'radio'
15393                     },
15394                     {
15395                         tag: 'label'
15396                     }
15397                 ]
15398             }
15399         ]
15400     },
15401     
15402     listItemCheckbox : {
15403         tag: 'li',
15404         cls: 'list-group-item',
15405         cn: [
15406             {
15407                 tag: 'span',
15408                 cls: 'roo-combobox-list-group-item-value'
15409             },
15410             {
15411                 tag: 'div',
15412                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15413                 cn: [
15414                     {
15415                         tag: 'input',
15416                         type: 'checkbox'
15417                     },
15418                     {
15419                         tag: 'label'
15420                     }
15421                 ]
15422             }
15423         ]
15424     },
15425     
15426     emptyResult : {
15427         tag: 'div',
15428         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15429     },
15430     
15431     footer : {
15432         tag: 'div',
15433         cls: 'modal-footer',
15434         cn: [
15435             {
15436                 tag: 'div',
15437                 cls: 'row',
15438                 cn: [
15439                     {
15440                         tag: 'div',
15441                         cls: 'col-xs-6 text-left',
15442                         cn: {
15443                             tag: 'button',
15444                             cls: 'btn btn-danger roo-touch-view-cancel',
15445                             html: 'Cancel'
15446                         }
15447                     },
15448                     {
15449                         tag: 'div',
15450                         cls: 'col-xs-6 text-right',
15451                         cn: {
15452                             tag: 'button',
15453                             cls: 'btn btn-success roo-touch-view-ok',
15454                             html: 'OK'
15455                         }
15456                     }
15457                 ]
15458             }
15459         ]
15460         
15461     }
15462 });
15463
15464 Roo.apply(Roo.bootstrap.ComboBox,  {
15465     
15466     touchViewTemplate : {
15467         tag: 'div',
15468         cls: 'modal fade roo-combobox-touch-view',
15469         cn: [
15470             {
15471                 tag: 'div',
15472                 cls: 'modal-dialog',
15473                 style : 'position:fixed', // we have to fix position....
15474                 cn: [
15475                     {
15476                         tag: 'div',
15477                         cls: 'modal-content',
15478                         cn: [
15479                             Roo.bootstrap.ComboBox.header,
15480                             Roo.bootstrap.ComboBox.body,
15481                             Roo.bootstrap.ComboBox.footer
15482                         ]
15483                     }
15484                 ]
15485             }
15486         ]
15487     }
15488 });/*
15489  * Based on:
15490  * Ext JS Library 1.1.1
15491  * Copyright(c) 2006-2007, Ext JS, LLC.
15492  *
15493  * Originally Released Under LGPL - original licence link has changed is not relivant.
15494  *
15495  * Fork - LGPL
15496  * <script type="text/javascript">
15497  */
15498
15499 /**
15500  * @class Roo.View
15501  * @extends Roo.util.Observable
15502  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15503  * This class also supports single and multi selection modes. <br>
15504  * Create a data model bound view:
15505  <pre><code>
15506  var store = new Roo.data.Store(...);
15507
15508  var view = new Roo.View({
15509     el : "my-element",
15510     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15511  
15512     singleSelect: true,
15513     selectedClass: "ydataview-selected",
15514     store: store
15515  });
15516
15517  // listen for node click?
15518  view.on("click", function(vw, index, node, e){
15519  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15520  });
15521
15522  // load XML data
15523  dataModel.load("foobar.xml");
15524  </code></pre>
15525  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15526  * <br><br>
15527  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15528  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15529  * 
15530  * Note: old style constructor is still suported (container, template, config)
15531  * 
15532  * @constructor
15533  * Create a new View
15534  * @param {Object} config The config object
15535  * 
15536  */
15537 Roo.View = function(config, depreciated_tpl, depreciated_config){
15538     
15539     this.parent = false;
15540     
15541     if (typeof(depreciated_tpl) == 'undefined') {
15542         // new way.. - universal constructor.
15543         Roo.apply(this, config);
15544         this.el  = Roo.get(this.el);
15545     } else {
15546         // old format..
15547         this.el  = Roo.get(config);
15548         this.tpl = depreciated_tpl;
15549         Roo.apply(this, depreciated_config);
15550     }
15551     this.wrapEl  = this.el.wrap().wrap();
15552     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15553     
15554     
15555     if(typeof(this.tpl) == "string"){
15556         this.tpl = new Roo.Template(this.tpl);
15557     } else {
15558         // support xtype ctors..
15559         this.tpl = new Roo.factory(this.tpl, Roo);
15560     }
15561     
15562     
15563     this.tpl.compile();
15564     
15565     /** @private */
15566     this.addEvents({
15567         /**
15568          * @event beforeclick
15569          * Fires before a click is processed. Returns false to cancel the default action.
15570          * @param {Roo.View} this
15571          * @param {Number} index The index of the target node
15572          * @param {HTMLElement} node The target node
15573          * @param {Roo.EventObject} e The raw event object
15574          */
15575             "beforeclick" : true,
15576         /**
15577          * @event click
15578          * Fires when a template node is clicked.
15579          * @param {Roo.View} this
15580          * @param {Number} index The index of the target node
15581          * @param {HTMLElement} node The target node
15582          * @param {Roo.EventObject} e The raw event object
15583          */
15584             "click" : true,
15585         /**
15586          * @event dblclick
15587          * Fires when a template node is double clicked.
15588          * @param {Roo.View} this
15589          * @param {Number} index The index of the target node
15590          * @param {HTMLElement} node The target node
15591          * @param {Roo.EventObject} e The raw event object
15592          */
15593             "dblclick" : true,
15594         /**
15595          * @event contextmenu
15596          * Fires when a template node is right clicked.
15597          * @param {Roo.View} this
15598          * @param {Number} index The index of the target node
15599          * @param {HTMLElement} node The target node
15600          * @param {Roo.EventObject} e The raw event object
15601          */
15602             "contextmenu" : true,
15603         /**
15604          * @event selectionchange
15605          * Fires when the selected nodes change.
15606          * @param {Roo.View} this
15607          * @param {Array} selections Array of the selected nodes
15608          */
15609             "selectionchange" : true,
15610     
15611         /**
15612          * @event beforeselect
15613          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15614          * @param {Roo.View} this
15615          * @param {HTMLElement} node The node to be selected
15616          * @param {Array} selections Array of currently selected nodes
15617          */
15618             "beforeselect" : true,
15619         /**
15620          * @event preparedata
15621          * Fires on every row to render, to allow you to change the data.
15622          * @param {Roo.View} this
15623          * @param {Object} data to be rendered (change this)
15624          */
15625           "preparedata" : true
15626           
15627           
15628         });
15629
15630
15631
15632     this.el.on({
15633         "click": this.onClick,
15634         "dblclick": this.onDblClick,
15635         "contextmenu": this.onContextMenu,
15636         scope:this
15637     });
15638
15639     this.selections = [];
15640     this.nodes = [];
15641     this.cmp = new Roo.CompositeElementLite([]);
15642     if(this.store){
15643         this.store = Roo.factory(this.store, Roo.data);
15644         this.setStore(this.store, true);
15645     }
15646     
15647     if ( this.footer && this.footer.xtype) {
15648            
15649          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15650         
15651         this.footer.dataSource = this.store;
15652         this.footer.container = fctr;
15653         this.footer = Roo.factory(this.footer, Roo);
15654         fctr.insertFirst(this.el);
15655         
15656         // this is a bit insane - as the paging toolbar seems to detach the el..
15657 //        dom.parentNode.parentNode.parentNode
15658          // they get detached?
15659     }
15660     
15661     
15662     Roo.View.superclass.constructor.call(this);
15663     
15664     
15665 };
15666
15667 Roo.extend(Roo.View, Roo.util.Observable, {
15668     
15669      /**
15670      * @cfg {Roo.data.Store} store Data store to load data from.
15671      */
15672     store : false,
15673     
15674     /**
15675      * @cfg {String|Roo.Element} el The container element.
15676      */
15677     el : '',
15678     
15679     /**
15680      * @cfg {String|Roo.Template} tpl The template used by this View 
15681      */
15682     tpl : false,
15683     /**
15684      * @cfg {String} dataName the named area of the template to use as the data area
15685      *                          Works with domtemplates roo-name="name"
15686      */
15687     dataName: false,
15688     /**
15689      * @cfg {String} selectedClass The css class to add to selected nodes
15690      */
15691     selectedClass : "x-view-selected",
15692      /**
15693      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15694      */
15695     emptyText : "",
15696     
15697     /**
15698      * @cfg {String} text to display on mask (default Loading)
15699      */
15700     mask : false,
15701     /**
15702      * @cfg {Boolean} multiSelect Allow multiple selection
15703      */
15704     multiSelect : false,
15705     /**
15706      * @cfg {Boolean} singleSelect Allow single selection
15707      */
15708     singleSelect:  false,
15709     
15710     /**
15711      * @cfg {Boolean} toggleSelect - selecting 
15712      */
15713     toggleSelect : false,
15714     
15715     /**
15716      * @cfg {Boolean} tickable - selecting 
15717      */
15718     tickable : false,
15719     
15720     /**
15721      * Returns the element this view is bound to.
15722      * @return {Roo.Element}
15723      */
15724     getEl : function(){
15725         return this.wrapEl;
15726     },
15727     
15728     
15729
15730     /**
15731      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15732      */
15733     refresh : function(){
15734         //Roo.log('refresh');
15735         var t = this.tpl;
15736         
15737         // if we are using something like 'domtemplate', then
15738         // the what gets used is:
15739         // t.applySubtemplate(NAME, data, wrapping data..)
15740         // the outer template then get' applied with
15741         //     the store 'extra data'
15742         // and the body get's added to the
15743         //      roo-name="data" node?
15744         //      <span class='roo-tpl-{name}'></span> ?????
15745         
15746         
15747         
15748         this.clearSelections();
15749         this.el.update("");
15750         var html = [];
15751         var records = this.store.getRange();
15752         if(records.length < 1) {
15753             
15754             // is this valid??  = should it render a template??
15755             
15756             this.el.update(this.emptyText);
15757             return;
15758         }
15759         var el = this.el;
15760         if (this.dataName) {
15761             this.el.update(t.apply(this.store.meta)); //????
15762             el = this.el.child('.roo-tpl-' + this.dataName);
15763         }
15764         
15765         for(var i = 0, len = records.length; i < len; i++){
15766             var data = this.prepareData(records[i].data, i, records[i]);
15767             this.fireEvent("preparedata", this, data, i, records[i]);
15768             
15769             var d = Roo.apply({}, data);
15770             
15771             if(this.tickable){
15772                 Roo.apply(d, {'roo-id' : Roo.id()});
15773                 
15774                 var _this = this;
15775             
15776                 Roo.each(this.parent.item, function(item){
15777                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15778                         return;
15779                     }
15780                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15781                 });
15782             }
15783             
15784             html[html.length] = Roo.util.Format.trim(
15785                 this.dataName ?
15786                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15787                     t.apply(d)
15788             );
15789         }
15790         
15791         
15792         
15793         el.update(html.join(""));
15794         this.nodes = el.dom.childNodes;
15795         this.updateIndexes(0);
15796     },
15797     
15798
15799     /**
15800      * Function to override to reformat the data that is sent to
15801      * the template for each node.
15802      * DEPRICATED - use the preparedata event handler.
15803      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15804      * a JSON object for an UpdateManager bound view).
15805      */
15806     prepareData : function(data, index, record)
15807     {
15808         this.fireEvent("preparedata", this, data, index, record);
15809         return data;
15810     },
15811
15812     onUpdate : function(ds, record){
15813         // Roo.log('on update');   
15814         this.clearSelections();
15815         var index = this.store.indexOf(record);
15816         var n = this.nodes[index];
15817         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15818         n.parentNode.removeChild(n);
15819         this.updateIndexes(index, index);
15820     },
15821
15822     
15823     
15824 // --------- FIXME     
15825     onAdd : function(ds, records, index)
15826     {
15827         //Roo.log(['on Add', ds, records, index] );        
15828         this.clearSelections();
15829         if(this.nodes.length == 0){
15830             this.refresh();
15831             return;
15832         }
15833         var n = this.nodes[index];
15834         for(var i = 0, len = records.length; i < len; i++){
15835             var d = this.prepareData(records[i].data, i, records[i]);
15836             if(n){
15837                 this.tpl.insertBefore(n, d);
15838             }else{
15839                 
15840                 this.tpl.append(this.el, d);
15841             }
15842         }
15843         this.updateIndexes(index);
15844     },
15845
15846     onRemove : function(ds, record, index){
15847        // Roo.log('onRemove');
15848         this.clearSelections();
15849         var el = this.dataName  ?
15850             this.el.child('.roo-tpl-' + this.dataName) :
15851             this.el; 
15852         
15853         el.dom.removeChild(this.nodes[index]);
15854         this.updateIndexes(index);
15855     },
15856
15857     /**
15858      * Refresh an individual node.
15859      * @param {Number} index
15860      */
15861     refreshNode : function(index){
15862         this.onUpdate(this.store, this.store.getAt(index));
15863     },
15864
15865     updateIndexes : function(startIndex, endIndex){
15866         var ns = this.nodes;
15867         startIndex = startIndex || 0;
15868         endIndex = endIndex || ns.length - 1;
15869         for(var i = startIndex; i <= endIndex; i++){
15870             ns[i].nodeIndex = i;
15871         }
15872     },
15873
15874     /**
15875      * Changes the data store this view uses and refresh the view.
15876      * @param {Store} store
15877      */
15878     setStore : function(store, initial){
15879         if(!initial && this.store){
15880             this.store.un("datachanged", this.refresh);
15881             this.store.un("add", this.onAdd);
15882             this.store.un("remove", this.onRemove);
15883             this.store.un("update", this.onUpdate);
15884             this.store.un("clear", this.refresh);
15885             this.store.un("beforeload", this.onBeforeLoad);
15886             this.store.un("load", this.onLoad);
15887             this.store.un("loadexception", this.onLoad);
15888         }
15889         if(store){
15890           
15891             store.on("datachanged", this.refresh, this);
15892             store.on("add", this.onAdd, this);
15893             store.on("remove", this.onRemove, this);
15894             store.on("update", this.onUpdate, this);
15895             store.on("clear", this.refresh, this);
15896             store.on("beforeload", this.onBeforeLoad, this);
15897             store.on("load", this.onLoad, this);
15898             store.on("loadexception", this.onLoad, this);
15899         }
15900         
15901         if(store){
15902             this.refresh();
15903         }
15904     },
15905     /**
15906      * onbeforeLoad - masks the loading area.
15907      *
15908      */
15909     onBeforeLoad : function(store,opts)
15910     {
15911          //Roo.log('onBeforeLoad');   
15912         if (!opts.add) {
15913             this.el.update("");
15914         }
15915         this.el.mask(this.mask ? this.mask : "Loading" ); 
15916     },
15917     onLoad : function ()
15918     {
15919         this.el.unmask();
15920     },
15921     
15922
15923     /**
15924      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15925      * @param {HTMLElement} node
15926      * @return {HTMLElement} The template node
15927      */
15928     findItemFromChild : function(node){
15929         var el = this.dataName  ?
15930             this.el.child('.roo-tpl-' + this.dataName,true) :
15931             this.el.dom; 
15932         
15933         if(!node || node.parentNode == el){
15934                     return node;
15935             }
15936             var p = node.parentNode;
15937             while(p && p != el){
15938             if(p.parentNode == el){
15939                 return p;
15940             }
15941             p = p.parentNode;
15942         }
15943             return null;
15944     },
15945
15946     /** @ignore */
15947     onClick : function(e){
15948         var item = this.findItemFromChild(e.getTarget());
15949         if(item){
15950             var index = this.indexOf(item);
15951             if(this.onItemClick(item, index, e) !== false){
15952                 this.fireEvent("click", this, index, item, e);
15953             }
15954         }else{
15955             this.clearSelections();
15956         }
15957     },
15958
15959     /** @ignore */
15960     onContextMenu : function(e){
15961         var item = this.findItemFromChild(e.getTarget());
15962         if(item){
15963             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15964         }
15965     },
15966
15967     /** @ignore */
15968     onDblClick : function(e){
15969         var item = this.findItemFromChild(e.getTarget());
15970         if(item){
15971             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15972         }
15973     },
15974
15975     onItemClick : function(item, index, e)
15976     {
15977         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15978             return false;
15979         }
15980         if (this.toggleSelect) {
15981             var m = this.isSelected(item) ? 'unselect' : 'select';
15982             //Roo.log(m);
15983             var _t = this;
15984             _t[m](item, true, false);
15985             return true;
15986         }
15987         if(this.multiSelect || this.singleSelect){
15988             if(this.multiSelect && e.shiftKey && this.lastSelection){
15989                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15990             }else{
15991                 this.select(item, this.multiSelect && e.ctrlKey);
15992                 this.lastSelection = item;
15993             }
15994             
15995             if(!this.tickable){
15996                 e.preventDefault();
15997             }
15998             
15999         }
16000         return true;
16001     },
16002
16003     /**
16004      * Get the number of selected nodes.
16005      * @return {Number}
16006      */
16007     getSelectionCount : function(){
16008         return this.selections.length;
16009     },
16010
16011     /**
16012      * Get the currently selected nodes.
16013      * @return {Array} An array of HTMLElements
16014      */
16015     getSelectedNodes : function(){
16016         return this.selections;
16017     },
16018
16019     /**
16020      * Get the indexes of the selected nodes.
16021      * @return {Array}
16022      */
16023     getSelectedIndexes : function(){
16024         var indexes = [], s = this.selections;
16025         for(var i = 0, len = s.length; i < len; i++){
16026             indexes.push(s[i].nodeIndex);
16027         }
16028         return indexes;
16029     },
16030
16031     /**
16032      * Clear all selections
16033      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16034      */
16035     clearSelections : function(suppressEvent){
16036         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16037             this.cmp.elements = this.selections;
16038             this.cmp.removeClass(this.selectedClass);
16039             this.selections = [];
16040             if(!suppressEvent){
16041                 this.fireEvent("selectionchange", this, this.selections);
16042             }
16043         }
16044     },
16045
16046     /**
16047      * Returns true if the passed node is selected
16048      * @param {HTMLElement/Number} node The node or node index
16049      * @return {Boolean}
16050      */
16051     isSelected : function(node){
16052         var s = this.selections;
16053         if(s.length < 1){
16054             return false;
16055         }
16056         node = this.getNode(node);
16057         return s.indexOf(node) !== -1;
16058     },
16059
16060     /**
16061      * Selects nodes.
16062      * @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
16063      * @param {Boolean} keepExisting (optional) true to keep existing selections
16064      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16065      */
16066     select : function(nodeInfo, keepExisting, suppressEvent){
16067         if(nodeInfo instanceof Array){
16068             if(!keepExisting){
16069                 this.clearSelections(true);
16070             }
16071             for(var i = 0, len = nodeInfo.length; i < len; i++){
16072                 this.select(nodeInfo[i], true, true);
16073             }
16074             return;
16075         } 
16076         var node = this.getNode(nodeInfo);
16077         if(!node || this.isSelected(node)){
16078             return; // already selected.
16079         }
16080         if(!keepExisting){
16081             this.clearSelections(true);
16082         }
16083         
16084         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16085             Roo.fly(node).addClass(this.selectedClass);
16086             this.selections.push(node);
16087             if(!suppressEvent){
16088                 this.fireEvent("selectionchange", this, this.selections);
16089             }
16090         }
16091         
16092         
16093     },
16094       /**
16095      * Unselects nodes.
16096      * @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
16097      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16099      */
16100     unselect : function(nodeInfo, keepExisting, suppressEvent)
16101     {
16102         if(nodeInfo instanceof Array){
16103             Roo.each(this.selections, function(s) {
16104                 this.unselect(s, nodeInfo);
16105             }, this);
16106             return;
16107         }
16108         var node = this.getNode(nodeInfo);
16109         if(!node || !this.isSelected(node)){
16110             //Roo.log("not selected");
16111             return; // not selected.
16112         }
16113         // fireevent???
16114         var ns = [];
16115         Roo.each(this.selections, function(s) {
16116             if (s == node ) {
16117                 Roo.fly(node).removeClass(this.selectedClass);
16118
16119                 return;
16120             }
16121             ns.push(s);
16122         },this);
16123         
16124         this.selections= ns;
16125         this.fireEvent("selectionchange", this, this.selections);
16126     },
16127
16128     /**
16129      * Gets a template node.
16130      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16131      * @return {HTMLElement} The node or null if it wasn't found
16132      */
16133     getNode : function(nodeInfo){
16134         if(typeof nodeInfo == "string"){
16135             return document.getElementById(nodeInfo);
16136         }else if(typeof nodeInfo == "number"){
16137             return this.nodes[nodeInfo];
16138         }
16139         return nodeInfo;
16140     },
16141
16142     /**
16143      * Gets a range template nodes.
16144      * @param {Number} startIndex
16145      * @param {Number} endIndex
16146      * @return {Array} An array of nodes
16147      */
16148     getNodes : function(start, end){
16149         var ns = this.nodes;
16150         start = start || 0;
16151         end = typeof end == "undefined" ? ns.length - 1 : end;
16152         var nodes = [];
16153         if(start <= end){
16154             for(var i = start; i <= end; i++){
16155                 nodes.push(ns[i]);
16156             }
16157         } else{
16158             for(var i = start; i >= end; i--){
16159                 nodes.push(ns[i]);
16160             }
16161         }
16162         return nodes;
16163     },
16164
16165     /**
16166      * Finds the index of the passed node
16167      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16168      * @return {Number} The index of the node or -1
16169      */
16170     indexOf : function(node){
16171         node = this.getNode(node);
16172         if(typeof node.nodeIndex == "number"){
16173             return node.nodeIndex;
16174         }
16175         var ns = this.nodes;
16176         for(var i = 0, len = ns.length; i < len; i++){
16177             if(ns[i] == node){
16178                 return i;
16179             }
16180         }
16181         return -1;
16182     }
16183 });
16184 /*
16185  * - LGPL
16186  *
16187  * based on jquery fullcalendar
16188  * 
16189  */
16190
16191 Roo.bootstrap = Roo.bootstrap || {};
16192 /**
16193  * @class Roo.bootstrap.Calendar
16194  * @extends Roo.bootstrap.Component
16195  * Bootstrap Calendar class
16196  * @cfg {Boolean} loadMask (true|false) default false
16197  * @cfg {Object} header generate the user specific header of the calendar, default false
16198
16199  * @constructor
16200  * Create a new Container
16201  * @param {Object} config The config object
16202  */
16203
16204
16205
16206 Roo.bootstrap.Calendar = function(config){
16207     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16208      this.addEvents({
16209         /**
16210              * @event select
16211              * Fires when a date is selected
16212              * @param {DatePicker} this
16213              * @param {Date} date The selected date
16214              */
16215         'select': true,
16216         /**
16217              * @event monthchange
16218              * Fires when the displayed month changes 
16219              * @param {DatePicker} this
16220              * @param {Date} date The selected month
16221              */
16222         'monthchange': true,
16223         /**
16224              * @event evententer
16225              * Fires when mouse over an event
16226              * @param {Calendar} this
16227              * @param {event} Event
16228              */
16229         'evententer': true,
16230         /**
16231              * @event eventleave
16232              * Fires when the mouse leaves an
16233              * @param {Calendar} this
16234              * @param {event}
16235              */
16236         'eventleave': true,
16237         /**
16238              * @event eventclick
16239              * Fires when the mouse click an
16240              * @param {Calendar} this
16241              * @param {event}
16242              */
16243         'eventclick': true
16244         
16245     });
16246
16247 };
16248
16249 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16250     
16251      /**
16252      * @cfg {Number} startDay
16253      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16254      */
16255     startDay : 0,
16256     
16257     loadMask : false,
16258     
16259     header : false,
16260       
16261     getAutoCreate : function(){
16262         
16263         
16264         var fc_button = function(name, corner, style, content ) {
16265             return Roo.apply({},{
16266                 tag : 'span',
16267                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16268                          (corner.length ?
16269                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16270                             ''
16271                         ),
16272                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16273                 unselectable: 'on'
16274             });
16275         };
16276         
16277         var header = {};
16278         
16279         if(!this.header){
16280             header = {
16281                 tag : 'table',
16282                 cls : 'fc-header',
16283                 style : 'width:100%',
16284                 cn : [
16285                     {
16286                         tag: 'tr',
16287                         cn : [
16288                             {
16289                                 tag : 'td',
16290                                 cls : 'fc-header-left',
16291                                 cn : [
16292                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16293                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16294                                     { tag: 'span', cls: 'fc-header-space' },
16295                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16296
16297
16298                                 ]
16299                             },
16300
16301                             {
16302                                 tag : 'td',
16303                                 cls : 'fc-header-center',
16304                                 cn : [
16305                                     {
16306                                         tag: 'span',
16307                                         cls: 'fc-header-title',
16308                                         cn : {
16309                                             tag: 'H2',
16310                                             html : 'month / year'
16311                                         }
16312                                     }
16313
16314                                 ]
16315                             },
16316                             {
16317                                 tag : 'td',
16318                                 cls : 'fc-header-right',
16319                                 cn : [
16320                               /*      fc_button('month', 'left', '', 'month' ),
16321                                     fc_button('week', '', '', 'week' ),
16322                                     fc_button('day', 'right', '', 'day' )
16323                                 */    
16324
16325                                 ]
16326                             }
16327
16328                         ]
16329                     }
16330                 ]
16331             };
16332         }
16333         
16334         header = this.header;
16335         
16336        
16337         var cal_heads = function() {
16338             var ret = [];
16339             // fixme - handle this.
16340             
16341             for (var i =0; i < Date.dayNames.length; i++) {
16342                 var d = Date.dayNames[i];
16343                 ret.push({
16344                     tag: 'th',
16345                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16346                     html : d.substring(0,3)
16347                 });
16348                 
16349             }
16350             ret[0].cls += ' fc-first';
16351             ret[6].cls += ' fc-last';
16352             return ret;
16353         };
16354         var cal_cell = function(n) {
16355             return  {
16356                 tag: 'td',
16357                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16358                 cn : [
16359                     {
16360                         cn : [
16361                             {
16362                                 cls: 'fc-day-number',
16363                                 html: 'D'
16364                             },
16365                             {
16366                                 cls: 'fc-day-content',
16367                              
16368                                 cn : [
16369                                      {
16370                                         style: 'position: relative;' // height: 17px;
16371                                     }
16372                                 ]
16373                             }
16374                             
16375                             
16376                         ]
16377                     }
16378                 ]
16379                 
16380             }
16381         };
16382         var cal_rows = function() {
16383             
16384             var ret = [];
16385             for (var r = 0; r < 6; r++) {
16386                 var row= {
16387                     tag : 'tr',
16388                     cls : 'fc-week',
16389                     cn : []
16390                 };
16391                 
16392                 for (var i =0; i < Date.dayNames.length; i++) {
16393                     var d = Date.dayNames[i];
16394                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16395
16396                 }
16397                 row.cn[0].cls+=' fc-first';
16398                 row.cn[0].cn[0].style = 'min-height:90px';
16399                 row.cn[6].cls+=' fc-last';
16400                 ret.push(row);
16401                 
16402             }
16403             ret[0].cls += ' fc-first';
16404             ret[4].cls += ' fc-prev-last';
16405             ret[5].cls += ' fc-last';
16406             return ret;
16407             
16408         };
16409         
16410         var cal_table = {
16411             tag: 'table',
16412             cls: 'fc-border-separate',
16413             style : 'width:100%',
16414             cellspacing  : 0,
16415             cn : [
16416                 { 
16417                     tag: 'thead',
16418                     cn : [
16419                         { 
16420                             tag: 'tr',
16421                             cls : 'fc-first fc-last',
16422                             cn : cal_heads()
16423                         }
16424                     ]
16425                 },
16426                 { 
16427                     tag: 'tbody',
16428                     cn : cal_rows()
16429                 }
16430                   
16431             ]
16432         };
16433          
16434          var cfg = {
16435             cls : 'fc fc-ltr',
16436             cn : [
16437                 header,
16438                 {
16439                     cls : 'fc-content',
16440                     style : "position: relative;",
16441                     cn : [
16442                         {
16443                             cls : 'fc-view fc-view-month fc-grid',
16444                             style : 'position: relative',
16445                             unselectable : 'on',
16446                             cn : [
16447                                 {
16448                                     cls : 'fc-event-container',
16449                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16450                                 },
16451                                 cal_table
16452                             ]
16453                         }
16454                     ]
16455     
16456                 }
16457            ] 
16458             
16459         };
16460         
16461          
16462         
16463         return cfg;
16464     },
16465     
16466     
16467     initEvents : function()
16468     {
16469         if(!this.store){
16470             throw "can not find store for calendar";
16471         }
16472         
16473         var mark = {
16474             tag: "div",
16475             cls:"x-dlg-mask",
16476             style: "text-align:center",
16477             cn: [
16478                 {
16479                     tag: "div",
16480                     style: "background-color:white;width:50%;margin:250 auto",
16481                     cn: [
16482                         {
16483                             tag: "img",
16484                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16485                         },
16486                         {
16487                             tag: "span",
16488                             html: "Loading"
16489                         }
16490                         
16491                     ]
16492                 }
16493             ]
16494         };
16495         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16496         
16497         var size = this.el.select('.fc-content', true).first().getSize();
16498         this.maskEl.setSize(size.width, size.height);
16499         this.maskEl.enableDisplayMode("block");
16500         if(!this.loadMask){
16501             this.maskEl.hide();
16502         }
16503         
16504         this.store = Roo.factory(this.store, Roo.data);
16505         this.store.on('load', this.onLoad, this);
16506         this.store.on('beforeload', this.onBeforeLoad, this);
16507         
16508         this.resize();
16509         
16510         this.cells = this.el.select('.fc-day',true);
16511         //Roo.log(this.cells);
16512         this.textNodes = this.el.query('.fc-day-number');
16513         this.cells.addClassOnOver('fc-state-hover');
16514         
16515         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16516         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16517         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16518         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16519         
16520         this.on('monthchange', this.onMonthChange, this);
16521         
16522         this.update(new Date().clearTime());
16523     },
16524     
16525     resize : function() {
16526         var sz  = this.el.getSize();
16527         
16528         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16529         this.el.select('.fc-day-content div',true).setHeight(34);
16530     },
16531     
16532     
16533     // private
16534     showPrevMonth : function(e){
16535         this.update(this.activeDate.add("mo", -1));
16536     },
16537     showToday : function(e){
16538         this.update(new Date().clearTime());
16539     },
16540     // private
16541     showNextMonth : function(e){
16542         this.update(this.activeDate.add("mo", 1));
16543     },
16544
16545     // private
16546     showPrevYear : function(){
16547         this.update(this.activeDate.add("y", -1));
16548     },
16549
16550     // private
16551     showNextYear : function(){
16552         this.update(this.activeDate.add("y", 1));
16553     },
16554
16555     
16556    // private
16557     update : function(date)
16558     {
16559         var vd = this.activeDate;
16560         this.activeDate = date;
16561 //        if(vd && this.el){
16562 //            var t = date.getTime();
16563 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16564 //                Roo.log('using add remove');
16565 //                
16566 //                this.fireEvent('monthchange', this, date);
16567 //                
16568 //                this.cells.removeClass("fc-state-highlight");
16569 //                this.cells.each(function(c){
16570 //                   if(c.dateValue == t){
16571 //                       c.addClass("fc-state-highlight");
16572 //                       setTimeout(function(){
16573 //                            try{c.dom.firstChild.focus();}catch(e){}
16574 //                       }, 50);
16575 //                       return false;
16576 //                   }
16577 //                   return true;
16578 //                });
16579 //                return;
16580 //            }
16581 //        }
16582         
16583         var days = date.getDaysInMonth();
16584         
16585         var firstOfMonth = date.getFirstDateOfMonth();
16586         var startingPos = firstOfMonth.getDay()-this.startDay;
16587         
16588         if(startingPos < this.startDay){
16589             startingPos += 7;
16590         }
16591         
16592         var pm = date.add(Date.MONTH, -1);
16593         var prevStart = pm.getDaysInMonth()-startingPos;
16594 //        
16595         this.cells = this.el.select('.fc-day',true);
16596         this.textNodes = this.el.query('.fc-day-number');
16597         this.cells.addClassOnOver('fc-state-hover');
16598         
16599         var cells = this.cells.elements;
16600         var textEls = this.textNodes;
16601         
16602         Roo.each(cells, function(cell){
16603             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16604         });
16605         
16606         days += startingPos;
16607
16608         // convert everything to numbers so it's fast
16609         var day = 86400000;
16610         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16611         //Roo.log(d);
16612         //Roo.log(pm);
16613         //Roo.log(prevStart);
16614         
16615         var today = new Date().clearTime().getTime();
16616         var sel = date.clearTime().getTime();
16617         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16618         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16619         var ddMatch = this.disabledDatesRE;
16620         var ddText = this.disabledDatesText;
16621         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16622         var ddaysText = this.disabledDaysText;
16623         var format = this.format;
16624         
16625         var setCellClass = function(cal, cell){
16626             cell.row = 0;
16627             cell.events = [];
16628             cell.more = [];
16629             //Roo.log('set Cell Class');
16630             cell.title = "";
16631             var t = d.getTime();
16632             
16633             //Roo.log(d);
16634             
16635             cell.dateValue = t;
16636             if(t == today){
16637                 cell.className += " fc-today";
16638                 cell.className += " fc-state-highlight";
16639                 cell.title = cal.todayText;
16640             }
16641             if(t == sel){
16642                 // disable highlight in other month..
16643                 //cell.className += " fc-state-highlight";
16644                 
16645             }
16646             // disabling
16647             if(t < min) {
16648                 cell.className = " fc-state-disabled";
16649                 cell.title = cal.minText;
16650                 return;
16651             }
16652             if(t > max) {
16653                 cell.className = " fc-state-disabled";
16654                 cell.title = cal.maxText;
16655                 return;
16656             }
16657             if(ddays){
16658                 if(ddays.indexOf(d.getDay()) != -1){
16659                     cell.title = ddaysText;
16660                     cell.className = " fc-state-disabled";
16661                 }
16662             }
16663             if(ddMatch && format){
16664                 var fvalue = d.dateFormat(format);
16665                 if(ddMatch.test(fvalue)){
16666                     cell.title = ddText.replace("%0", fvalue);
16667                     cell.className = " fc-state-disabled";
16668                 }
16669             }
16670             
16671             if (!cell.initialClassName) {
16672                 cell.initialClassName = cell.dom.className;
16673             }
16674             
16675             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16676         };
16677
16678         var i = 0;
16679         
16680         for(; i < startingPos; i++) {
16681             textEls[i].innerHTML = (++prevStart);
16682             d.setDate(d.getDate()+1);
16683             
16684             cells[i].className = "fc-past fc-other-month";
16685             setCellClass(this, cells[i]);
16686         }
16687         
16688         var intDay = 0;
16689         
16690         for(; i < days; i++){
16691             intDay = i - startingPos + 1;
16692             textEls[i].innerHTML = (intDay);
16693             d.setDate(d.getDate()+1);
16694             
16695             cells[i].className = ''; // "x-date-active";
16696             setCellClass(this, cells[i]);
16697         }
16698         var extraDays = 0;
16699         
16700         for(; i < 42; i++) {
16701             textEls[i].innerHTML = (++extraDays);
16702             d.setDate(d.getDate()+1);
16703             
16704             cells[i].className = "fc-future fc-other-month";
16705             setCellClass(this, cells[i]);
16706         }
16707         
16708         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16709         
16710         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16711         
16712         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16713         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16714         
16715         if(totalRows != 6){
16716             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16717             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16718         }
16719         
16720         this.fireEvent('monthchange', this, date);
16721         
16722         
16723         /*
16724         if(!this.internalRender){
16725             var main = this.el.dom.firstChild;
16726             var w = main.offsetWidth;
16727             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16728             Roo.fly(main).setWidth(w);
16729             this.internalRender = true;
16730             // opera does not respect the auto grow header center column
16731             // then, after it gets a width opera refuses to recalculate
16732             // without a second pass
16733             if(Roo.isOpera && !this.secondPass){
16734                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16735                 this.secondPass = true;
16736                 this.update.defer(10, this, [date]);
16737             }
16738         }
16739         */
16740         
16741     },
16742     
16743     findCell : function(dt) {
16744         dt = dt.clearTime().getTime();
16745         var ret = false;
16746         this.cells.each(function(c){
16747             //Roo.log("check " +c.dateValue + '?=' + dt);
16748             if(c.dateValue == dt){
16749                 ret = c;
16750                 return false;
16751             }
16752             return true;
16753         });
16754         
16755         return ret;
16756     },
16757     
16758     findCells : function(ev) {
16759         var s = ev.start.clone().clearTime().getTime();
16760        // Roo.log(s);
16761         var e= ev.end.clone().clearTime().getTime();
16762        // Roo.log(e);
16763         var ret = [];
16764         this.cells.each(function(c){
16765              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16766             
16767             if(c.dateValue > e){
16768                 return ;
16769             }
16770             if(c.dateValue < s){
16771                 return ;
16772             }
16773             ret.push(c);
16774         });
16775         
16776         return ret;    
16777     },
16778     
16779 //    findBestRow: function(cells)
16780 //    {
16781 //        var ret = 0;
16782 //        
16783 //        for (var i =0 ; i < cells.length;i++) {
16784 //            ret  = Math.max(cells[i].rows || 0,ret);
16785 //        }
16786 //        return ret;
16787 //        
16788 //    },
16789     
16790     
16791     addItem : function(ev)
16792     {
16793         // look for vertical location slot in
16794         var cells = this.findCells(ev);
16795         
16796 //        ev.row = this.findBestRow(cells);
16797         
16798         // work out the location.
16799         
16800         var crow = false;
16801         var rows = [];
16802         for(var i =0; i < cells.length; i++) {
16803             
16804             cells[i].row = cells[0].row;
16805             
16806             if(i == 0){
16807                 cells[i].row = cells[i].row + 1;
16808             }
16809             
16810             if (!crow) {
16811                 crow = {
16812                     start : cells[i],
16813                     end :  cells[i]
16814                 };
16815                 continue;
16816             }
16817             if (crow.start.getY() == cells[i].getY()) {
16818                 // on same row.
16819                 crow.end = cells[i];
16820                 continue;
16821             }
16822             // different row.
16823             rows.push(crow);
16824             crow = {
16825                 start: cells[i],
16826                 end : cells[i]
16827             };
16828             
16829         }
16830         
16831         rows.push(crow);
16832         ev.els = [];
16833         ev.rows = rows;
16834         ev.cells = cells;
16835         
16836         cells[0].events.push(ev);
16837         
16838         this.calevents.push(ev);
16839     },
16840     
16841     clearEvents: function() {
16842         
16843         if(!this.calevents){
16844             return;
16845         }
16846         
16847         Roo.each(this.cells.elements, function(c){
16848             c.row = 0;
16849             c.events = [];
16850             c.more = [];
16851         });
16852         
16853         Roo.each(this.calevents, function(e) {
16854             Roo.each(e.els, function(el) {
16855                 el.un('mouseenter' ,this.onEventEnter, this);
16856                 el.un('mouseleave' ,this.onEventLeave, this);
16857                 el.remove();
16858             },this);
16859         },this);
16860         
16861         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16862             e.remove();
16863         });
16864         
16865     },
16866     
16867     renderEvents: function()
16868     {   
16869         var _this = this;
16870         
16871         this.cells.each(function(c) {
16872             
16873             if(c.row < 5){
16874                 return;
16875             }
16876             
16877             var ev = c.events;
16878             
16879             var r = 4;
16880             if(c.row != c.events.length){
16881                 r = 4 - (4 - (c.row - c.events.length));
16882             }
16883             
16884             c.events = ev.slice(0, r);
16885             c.more = ev.slice(r);
16886             
16887             if(c.more.length && c.more.length == 1){
16888                 c.events.push(c.more.pop());
16889             }
16890             
16891             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16892             
16893         });
16894             
16895         this.cells.each(function(c) {
16896             
16897             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16898             
16899             
16900             for (var e = 0; e < c.events.length; e++){
16901                 var ev = c.events[e];
16902                 var rows = ev.rows;
16903                 
16904                 for(var i = 0; i < rows.length; i++) {
16905                 
16906                     // how many rows should it span..
16907
16908                     var  cfg = {
16909                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16910                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16911
16912                         unselectable : "on",
16913                         cn : [
16914                             {
16915                                 cls: 'fc-event-inner',
16916                                 cn : [
16917     //                                {
16918     //                                  tag:'span',
16919     //                                  cls: 'fc-event-time',
16920     //                                  html : cells.length > 1 ? '' : ev.time
16921     //                                },
16922                                     {
16923                                       tag:'span',
16924                                       cls: 'fc-event-title',
16925                                       html : String.format('{0}', ev.title)
16926                                     }
16927
16928
16929                                 ]
16930                             },
16931                             {
16932                                 cls: 'ui-resizable-handle ui-resizable-e',
16933                                 html : '&nbsp;&nbsp;&nbsp'
16934                             }
16935
16936                         ]
16937                     };
16938
16939                     if (i == 0) {
16940                         cfg.cls += ' fc-event-start';
16941                     }
16942                     if ((i+1) == rows.length) {
16943                         cfg.cls += ' fc-event-end';
16944                     }
16945
16946                     var ctr = _this.el.select('.fc-event-container',true).first();
16947                     var cg = ctr.createChild(cfg);
16948
16949                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16950                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16951
16952                     var r = (c.more.length) ? 1 : 0;
16953                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16954                     cg.setWidth(ebox.right - sbox.x -2);
16955
16956                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16957                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16958                     cg.on('click', _this.onEventClick, _this, ev);
16959
16960                     ev.els.push(cg);
16961                     
16962                 }
16963                 
16964             }
16965             
16966             
16967             if(c.more.length){
16968                 var  cfg = {
16969                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16970                     style : 'position: absolute',
16971                     unselectable : "on",
16972                     cn : [
16973                         {
16974                             cls: 'fc-event-inner',
16975                             cn : [
16976                                 {
16977                                   tag:'span',
16978                                   cls: 'fc-event-title',
16979                                   html : 'More'
16980                                 }
16981
16982
16983                             ]
16984                         },
16985                         {
16986                             cls: 'ui-resizable-handle ui-resizable-e',
16987                             html : '&nbsp;&nbsp;&nbsp'
16988                         }
16989
16990                     ]
16991                 };
16992
16993                 var ctr = _this.el.select('.fc-event-container',true).first();
16994                 var cg = ctr.createChild(cfg);
16995
16996                 var sbox = c.select('.fc-day-content',true).first().getBox();
16997                 var ebox = c.select('.fc-day-content',true).first().getBox();
16998                 //Roo.log(cg);
16999                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17000                 cg.setWidth(ebox.right - sbox.x -2);
17001
17002                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17003                 
17004             }
17005             
17006         });
17007         
17008         
17009         
17010     },
17011     
17012     onEventEnter: function (e, el,event,d) {
17013         this.fireEvent('evententer', this, el, event);
17014     },
17015     
17016     onEventLeave: function (e, el,event,d) {
17017         this.fireEvent('eventleave', this, el, event);
17018     },
17019     
17020     onEventClick: function (e, el,event,d) {
17021         this.fireEvent('eventclick', this, el, event);
17022     },
17023     
17024     onMonthChange: function () {
17025         this.store.load();
17026     },
17027     
17028     onMoreEventClick: function(e, el, more)
17029     {
17030         var _this = this;
17031         
17032         this.calpopover.placement = 'right';
17033         this.calpopover.setTitle('More');
17034         
17035         this.calpopover.setContent('');
17036         
17037         var ctr = this.calpopover.el.select('.popover-content', true).first();
17038         
17039         Roo.each(more, function(m){
17040             var cfg = {
17041                 cls : 'fc-event-hori fc-event-draggable',
17042                 html : m.title
17043             };
17044             var cg = ctr.createChild(cfg);
17045             
17046             cg.on('click', _this.onEventClick, _this, m);
17047         });
17048         
17049         this.calpopover.show(el);
17050         
17051         
17052     },
17053     
17054     onLoad: function () 
17055     {   
17056         this.calevents = [];
17057         var cal = this;
17058         
17059         if(this.store.getCount() > 0){
17060             this.store.data.each(function(d){
17061                cal.addItem({
17062                     id : d.data.id,
17063                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17064                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17065                     time : d.data.start_time,
17066                     title : d.data.title,
17067                     description : d.data.description,
17068                     venue : d.data.venue
17069                 });
17070             });
17071         }
17072         
17073         this.renderEvents();
17074         
17075         if(this.calevents.length && this.loadMask){
17076             this.maskEl.hide();
17077         }
17078     },
17079     
17080     onBeforeLoad: function()
17081     {
17082         this.clearEvents();
17083         if(this.loadMask){
17084             this.maskEl.show();
17085         }
17086     }
17087 });
17088
17089  
17090  /*
17091  * - LGPL
17092  *
17093  * element
17094  * 
17095  */
17096
17097 /**
17098  * @class Roo.bootstrap.Popover
17099  * @extends Roo.bootstrap.Component
17100  * Bootstrap Popover class
17101  * @cfg {String} html contents of the popover   (or false to use children..)
17102  * @cfg {String} title of popover (or false to hide)
17103  * @cfg {String} placement how it is placed
17104  * @cfg {String} trigger click || hover (or false to trigger manually)
17105  * @cfg {String} over what (parent or false to trigger manually.)
17106  * @cfg {Number} delay - delay before showing
17107  
17108  * @constructor
17109  * Create a new Popover
17110  * @param {Object} config The config object
17111  */
17112
17113 Roo.bootstrap.Popover = function(config){
17114     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17115     
17116     this.addEvents({
17117         // raw events
17118          /**
17119          * @event show
17120          * After the popover show
17121          * 
17122          * @param {Roo.bootstrap.Popover} this
17123          */
17124         "show" : true,
17125         /**
17126          * @event hide
17127          * After the popover hide
17128          * 
17129          * @param {Roo.bootstrap.Popover} this
17130          */
17131         "hide" : true
17132     });
17133 };
17134
17135 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17136     
17137     title: 'Fill in a title',
17138     html: false,
17139     
17140     placement : 'right',
17141     trigger : 'hover', // hover
17142     
17143     delay : 0,
17144     
17145     over: 'parent',
17146     
17147     can_build_overlaid : false,
17148     
17149     getChildContainer : function()
17150     {
17151         return this.el.select('.popover-content',true).first();
17152     },
17153     
17154     getAutoCreate : function(){
17155          
17156         var cfg = {
17157            cls : 'popover roo-dynamic',
17158            style: 'display:block',
17159            cn : [
17160                 {
17161                     cls : 'arrow'
17162                 },
17163                 {
17164                     cls : 'popover-inner',
17165                     cn : [
17166                         {
17167                             tag: 'h3',
17168                             cls: 'popover-title',
17169                             html : this.title
17170                         },
17171                         {
17172                             cls : 'popover-content',
17173                             html : this.html
17174                         }
17175                     ]
17176                     
17177                 }
17178            ]
17179         };
17180         
17181         return cfg;
17182     },
17183     setTitle: function(str)
17184     {
17185         this.title = str;
17186         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17187     },
17188     setContent: function(str)
17189     {
17190         this.html = str;
17191         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17192     },
17193     // as it get's added to the bottom of the page.
17194     onRender : function(ct, position)
17195     {
17196         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17197         if(!this.el){
17198             var cfg = Roo.apply({},  this.getAutoCreate());
17199             cfg.id = Roo.id();
17200             
17201             if (this.cls) {
17202                 cfg.cls += ' ' + this.cls;
17203             }
17204             if (this.style) {
17205                 cfg.style = this.style;
17206             }
17207             //Roo.log("adding to ");
17208             this.el = Roo.get(document.body).createChild(cfg, position);
17209 //            Roo.log(this.el);
17210         }
17211         this.initEvents();
17212     },
17213     
17214     initEvents : function()
17215     {
17216         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17217         this.el.enableDisplayMode('block');
17218         this.el.hide();
17219         if (this.over === false) {
17220             return; 
17221         }
17222         if (this.triggers === false) {
17223             return;
17224         }
17225         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17226         var triggers = this.trigger ? this.trigger.split(' ') : [];
17227         Roo.each(triggers, function(trigger) {
17228         
17229             if (trigger == 'click') {
17230                 on_el.on('click', this.toggle, this);
17231             } else if (trigger != 'manual') {
17232                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17233                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17234       
17235                 on_el.on(eventIn  ,this.enter, this);
17236                 on_el.on(eventOut, this.leave, this);
17237             }
17238         }, this);
17239         
17240     },
17241     
17242     
17243     // private
17244     timeout : null,
17245     hoverState : null,
17246     
17247     toggle : function () {
17248         this.hoverState == 'in' ? this.leave() : this.enter();
17249     },
17250     
17251     enter : function () {
17252         
17253         clearTimeout(this.timeout);
17254     
17255         this.hoverState = 'in';
17256     
17257         if (!this.delay || !this.delay.show) {
17258             this.show();
17259             return;
17260         }
17261         var _t = this;
17262         this.timeout = setTimeout(function () {
17263             if (_t.hoverState == 'in') {
17264                 _t.show();
17265             }
17266         }, this.delay.show)
17267     },
17268     
17269     leave : function() {
17270         clearTimeout(this.timeout);
17271     
17272         this.hoverState = 'out';
17273     
17274         if (!this.delay || !this.delay.hide) {
17275             this.hide();
17276             return;
17277         }
17278         var _t = this;
17279         this.timeout = setTimeout(function () {
17280             if (_t.hoverState == 'out') {
17281                 _t.hide();
17282             }
17283         }, this.delay.hide)
17284     },
17285     
17286     show : function (on_el)
17287     {
17288         if (!on_el) {
17289             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17290         }
17291         
17292         // set content.
17293         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17294         if (this.html !== false) {
17295             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17296         }
17297         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17298         if (!this.title.length) {
17299             this.el.select('.popover-title',true).hide();
17300         }
17301         
17302         var placement = typeof this.placement == 'function' ?
17303             this.placement.call(this, this.el, on_el) :
17304             this.placement;
17305             
17306         var autoToken = /\s?auto?\s?/i;
17307         var autoPlace = autoToken.test(placement);
17308         if (autoPlace) {
17309             placement = placement.replace(autoToken, '') || 'top';
17310         }
17311         
17312         //this.el.detach()
17313         //this.el.setXY([0,0]);
17314         this.el.show();
17315         this.el.dom.style.display='block';
17316         this.el.addClass(placement);
17317         
17318         //this.el.appendTo(on_el);
17319         
17320         var p = this.getPosition();
17321         var box = this.el.getBox();
17322         
17323         if (autoPlace) {
17324             // fixme..
17325         }
17326         var align = Roo.bootstrap.Popover.alignment[placement];
17327         
17328 //        Roo.log(align);
17329         this.el.alignTo(on_el, align[0],align[1]);
17330         //var arrow = this.el.select('.arrow',true).first();
17331         //arrow.set(align[2], 
17332         
17333         this.el.addClass('in');
17334         
17335         
17336         if (this.el.hasClass('fade')) {
17337             // fade it?
17338         }
17339         
17340         this.hoverState = 'in';
17341         
17342         this.fireEvent('show', this);
17343         
17344     },
17345     hide : function()
17346     {
17347         this.el.setXY([0,0]);
17348         this.el.removeClass('in');
17349         this.el.hide();
17350         this.hoverState = null;
17351         
17352         this.fireEvent('hide', this);
17353     }
17354     
17355 });
17356
17357 Roo.bootstrap.Popover.alignment = {
17358     'left' : ['r-l', [-10,0], 'right'],
17359     'right' : ['l-r', [10,0], 'left'],
17360     'bottom' : ['t-b', [0,10], 'top'],
17361     'top' : [ 'b-t', [0,-10], 'bottom']
17362 };
17363
17364  /*
17365  * - LGPL
17366  *
17367  * Progress
17368  * 
17369  */
17370
17371 /**
17372  * @class Roo.bootstrap.Progress
17373  * @extends Roo.bootstrap.Component
17374  * Bootstrap Progress class
17375  * @cfg {Boolean} striped striped of the progress bar
17376  * @cfg {Boolean} active animated of the progress bar
17377  * 
17378  * 
17379  * @constructor
17380  * Create a new Progress
17381  * @param {Object} config The config object
17382  */
17383
17384 Roo.bootstrap.Progress = function(config){
17385     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17386 };
17387
17388 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17389     
17390     striped : false,
17391     active: false,
17392     
17393     getAutoCreate : function(){
17394         var cfg = {
17395             tag: 'div',
17396             cls: 'progress'
17397         };
17398         
17399         
17400         if(this.striped){
17401             cfg.cls += ' progress-striped';
17402         }
17403       
17404         if(this.active){
17405             cfg.cls += ' active';
17406         }
17407         
17408         
17409         return cfg;
17410     }
17411    
17412 });
17413
17414  
17415
17416  /*
17417  * - LGPL
17418  *
17419  * ProgressBar
17420  * 
17421  */
17422
17423 /**
17424  * @class Roo.bootstrap.ProgressBar
17425  * @extends Roo.bootstrap.Component
17426  * Bootstrap ProgressBar class
17427  * @cfg {Number} aria_valuenow aria-value now
17428  * @cfg {Number} aria_valuemin aria-value min
17429  * @cfg {Number} aria_valuemax aria-value max
17430  * @cfg {String} label label for the progress bar
17431  * @cfg {String} panel (success | info | warning | danger )
17432  * @cfg {String} role role of the progress bar
17433  * @cfg {String} sr_only text
17434  * 
17435  * 
17436  * @constructor
17437  * Create a new ProgressBar
17438  * @param {Object} config The config object
17439  */
17440
17441 Roo.bootstrap.ProgressBar = function(config){
17442     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17443 };
17444
17445 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17446     
17447     aria_valuenow : 0,
17448     aria_valuemin : 0,
17449     aria_valuemax : 100,
17450     label : false,
17451     panel : false,
17452     role : false,
17453     sr_only: false,
17454     
17455     getAutoCreate : function()
17456     {
17457         
17458         var cfg = {
17459             tag: 'div',
17460             cls: 'progress-bar',
17461             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17462         };
17463         
17464         if(this.sr_only){
17465             cfg.cn = {
17466                 tag: 'span',
17467                 cls: 'sr-only',
17468                 html: this.sr_only
17469             }
17470         }
17471         
17472         if(this.role){
17473             cfg.role = this.role;
17474         }
17475         
17476         if(this.aria_valuenow){
17477             cfg['aria-valuenow'] = this.aria_valuenow;
17478         }
17479         
17480         if(this.aria_valuemin){
17481             cfg['aria-valuemin'] = this.aria_valuemin;
17482         }
17483         
17484         if(this.aria_valuemax){
17485             cfg['aria-valuemax'] = this.aria_valuemax;
17486         }
17487         
17488         if(this.label && !this.sr_only){
17489             cfg.html = this.label;
17490         }
17491         
17492         if(this.panel){
17493             cfg.cls += ' progress-bar-' + this.panel;
17494         }
17495         
17496         return cfg;
17497     },
17498     
17499     update : function(aria_valuenow)
17500     {
17501         this.aria_valuenow = aria_valuenow;
17502         
17503         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17504     }
17505    
17506 });
17507
17508  
17509
17510  /*
17511  * - LGPL
17512  *
17513  * column
17514  * 
17515  */
17516
17517 /**
17518  * @class Roo.bootstrap.TabGroup
17519  * @extends Roo.bootstrap.Column
17520  * Bootstrap Column class
17521  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17522  * @cfg {Boolean} carousel true to make the group behave like a carousel
17523  * @cfg {Boolean} bullets show bullets for the panels
17524  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17525  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17526  * @cfg {Boolean} showarrow (true|false) show arrow default true
17527  * 
17528  * @constructor
17529  * Create a new TabGroup
17530  * @param {Object} config The config object
17531  */
17532
17533 Roo.bootstrap.TabGroup = function(config){
17534     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17535     if (!this.navId) {
17536         this.navId = Roo.id();
17537     }
17538     this.tabs = [];
17539     Roo.bootstrap.TabGroup.register(this);
17540     
17541 };
17542
17543 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17544     
17545     carousel : false,
17546     transition : false,
17547     bullets : 0,
17548     timer : 0,
17549     autoslide : false,
17550     slideFn : false,
17551     slideOnTouch : false,
17552     showarrow : true,
17553     
17554     getAutoCreate : function()
17555     {
17556         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17557         
17558         cfg.cls += ' tab-content';
17559         
17560         if (this.carousel) {
17561             cfg.cls += ' carousel slide';
17562             
17563             cfg.cn = [{
17564                cls : 'carousel-inner',
17565                cn : []
17566             }];
17567         
17568             if(this.bullets  && !Roo.isTouch){
17569                 
17570                 var bullets = {
17571                     cls : 'carousel-bullets',
17572                     cn : []
17573                 };
17574                
17575                 if(this.bullets_cls){
17576                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17577                 }
17578                 
17579                 bullets.cn.push({
17580                     cls : 'clear'
17581                 });
17582                 
17583                 cfg.cn[0].cn.push(bullets);
17584             }
17585             
17586             if(this.showarrow){
17587                 cfg.cn[0].cn.push({
17588                     tag : 'div',
17589                     class : 'carousel-arrow',
17590                     cn : [
17591                         {
17592                             tag : 'div',
17593                             class : 'carousel-prev',
17594                             cn : [
17595                                 {
17596                                     tag : 'i',
17597                                     class : 'fa fa-chevron-left'
17598                                 }
17599                             ]
17600                         },
17601                         {
17602                             tag : 'div',
17603                             class : 'carousel-next',
17604                             cn : [
17605                                 {
17606                                     tag : 'i',
17607                                     class : 'fa fa-chevron-right'
17608                                 }
17609                             ]
17610                         }
17611                     ]
17612                 });
17613             }
17614             
17615         }
17616         
17617         return cfg;
17618     },
17619     
17620     initEvents:  function()
17621     {
17622 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17623 //            this.el.on("touchstart", this.onTouchStart, this);
17624 //        }
17625         
17626         if(this.autoslide){
17627             var _this = this;
17628             
17629             this.slideFn = window.setInterval(function() {
17630                 _this.showPanelNext();
17631             }, this.timer);
17632         }
17633         
17634         if(this.showarrow){
17635             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17636             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17637         }
17638         
17639         
17640     },
17641     
17642 //    onTouchStart : function(e, el, o)
17643 //    {
17644 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17645 //            return;
17646 //        }
17647 //        
17648 //        this.showPanelNext();
17649 //    },
17650     
17651     
17652     getChildContainer : function()
17653     {
17654         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17655     },
17656     
17657     /**
17658     * register a Navigation item
17659     * @param {Roo.bootstrap.NavItem} the navitem to add
17660     */
17661     register : function(item)
17662     {
17663         this.tabs.push( item);
17664         item.navId = this.navId; // not really needed..
17665         this.addBullet();
17666     
17667     },
17668     
17669     getActivePanel : function()
17670     {
17671         var r = false;
17672         Roo.each(this.tabs, function(t) {
17673             if (t.active) {
17674                 r = t;
17675                 return false;
17676             }
17677             return null;
17678         });
17679         return r;
17680         
17681     },
17682     getPanelByName : function(n)
17683     {
17684         var r = false;
17685         Roo.each(this.tabs, function(t) {
17686             if (t.tabId == n) {
17687                 r = t;
17688                 return false;
17689             }
17690             return null;
17691         });
17692         return r;
17693     },
17694     indexOfPanel : function(p)
17695     {
17696         var r = false;
17697         Roo.each(this.tabs, function(t,i) {
17698             if (t.tabId == p.tabId) {
17699                 r = i;
17700                 return false;
17701             }
17702             return null;
17703         });
17704         return r;
17705     },
17706     /**
17707      * show a specific panel
17708      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17709      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17710      */
17711     showPanel : function (pan)
17712     {
17713         if(this.transition || typeof(pan) == 'undefined'){
17714             Roo.log("waiting for the transitionend");
17715             return;
17716         }
17717         
17718         if (typeof(pan) == 'number') {
17719             pan = this.tabs[pan];
17720         }
17721         
17722         if (typeof(pan) == 'string') {
17723             pan = this.getPanelByName(pan);
17724         }
17725         
17726         var cur = this.getActivePanel();
17727         
17728         if(!pan || !cur){
17729             Roo.log('pan or acitve pan is undefined');
17730             return false;
17731         }
17732         
17733         if (pan.tabId == this.getActivePanel().tabId) {
17734             return true;
17735         }
17736         
17737         if (false === cur.fireEvent('beforedeactivate')) {
17738             return false;
17739         }
17740         
17741         if(this.bullets > 0 && !Roo.isTouch){
17742             this.setActiveBullet(this.indexOfPanel(pan));
17743         }
17744         
17745         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17746             
17747             this.transition = true;
17748             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17749             var lr = dir == 'next' ? 'left' : 'right';
17750             pan.el.addClass(dir); // or prev
17751             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17752             cur.el.addClass(lr); // or right
17753             pan.el.addClass(lr);
17754             
17755             var _this = this;
17756             cur.el.on('transitionend', function() {
17757                 Roo.log("trans end?");
17758                 
17759                 pan.el.removeClass([lr,dir]);
17760                 pan.setActive(true);
17761                 
17762                 cur.el.removeClass([lr]);
17763                 cur.setActive(false);
17764                 
17765                 _this.transition = false;
17766                 
17767             }, this, { single:  true } );
17768             
17769             return true;
17770         }
17771         
17772         cur.setActive(false);
17773         pan.setActive(true);
17774         
17775         return true;
17776         
17777     },
17778     showPanelNext : function()
17779     {
17780         var i = this.indexOfPanel(this.getActivePanel());
17781         
17782         if (i >= this.tabs.length - 1 && !this.autoslide) {
17783             return;
17784         }
17785         
17786         if (i >= this.tabs.length - 1 && this.autoslide) {
17787             i = -1;
17788         }
17789         
17790         this.showPanel(this.tabs[i+1]);
17791     },
17792     
17793     showPanelPrev : function()
17794     {
17795         var i = this.indexOfPanel(this.getActivePanel());
17796         
17797         if (i  < 1 && !this.autoslide) {
17798             return;
17799         }
17800         
17801         if (i < 1 && this.autoslide) {
17802             i = this.tabs.length;
17803         }
17804         
17805         this.showPanel(this.tabs[i-1]);
17806     },
17807     
17808     
17809     addBullet: function()
17810     {
17811         if(!this.bullets || Roo.isTouch){
17812             return;
17813         }
17814         var ctr = this.el.select('.carousel-bullets',true).first();
17815         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17816         var bullet = ctr.createChild({
17817             cls : 'bullet bullet-' + i
17818         },ctr.dom.lastChild);
17819         
17820         
17821         var _this = this;
17822         
17823         bullet.on('click', (function(e, el, o, ii, t){
17824
17825             e.preventDefault();
17826
17827             this.showPanel(ii);
17828
17829             if(this.autoslide && this.slideFn){
17830                 clearInterval(this.slideFn);
17831                 this.slideFn = window.setInterval(function() {
17832                     _this.showPanelNext();
17833                 }, this.timer);
17834             }
17835
17836         }).createDelegate(this, [i, bullet], true));
17837                 
17838         
17839     },
17840      
17841     setActiveBullet : function(i)
17842     {
17843         if(Roo.isTouch){
17844             return;
17845         }
17846         
17847         Roo.each(this.el.select('.bullet', true).elements, function(el){
17848             el.removeClass('selected');
17849         });
17850
17851         var bullet = this.el.select('.bullet-' + i, true).first();
17852         
17853         if(!bullet){
17854             return;
17855         }
17856         
17857         bullet.addClass('selected');
17858     }
17859     
17860     
17861   
17862 });
17863
17864  
17865
17866  
17867  
17868 Roo.apply(Roo.bootstrap.TabGroup, {
17869     
17870     groups: {},
17871      /**
17872     * register a Navigation Group
17873     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17874     */
17875     register : function(navgrp)
17876     {
17877         this.groups[navgrp.navId] = navgrp;
17878         
17879     },
17880     /**
17881     * fetch a Navigation Group based on the navigation ID
17882     * if one does not exist , it will get created.
17883     * @param {string} the navgroup to add
17884     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17885     */
17886     get: function(navId) {
17887         if (typeof(this.groups[navId]) == 'undefined') {
17888             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17889         }
17890         return this.groups[navId] ;
17891     }
17892     
17893     
17894     
17895 });
17896
17897  /*
17898  * - LGPL
17899  *
17900  * TabPanel
17901  * 
17902  */
17903
17904 /**
17905  * @class Roo.bootstrap.TabPanel
17906  * @extends Roo.bootstrap.Component
17907  * Bootstrap TabPanel class
17908  * @cfg {Boolean} active panel active
17909  * @cfg {String} html panel content
17910  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17911  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17912  * @cfg {String} href click to link..
17913  * 
17914  * 
17915  * @constructor
17916  * Create a new TabPanel
17917  * @param {Object} config The config object
17918  */
17919
17920 Roo.bootstrap.TabPanel = function(config){
17921     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17922     this.addEvents({
17923         /**
17924              * @event changed
17925              * Fires when the active status changes
17926              * @param {Roo.bootstrap.TabPanel} this
17927              * @param {Boolean} state the new state
17928             
17929          */
17930         'changed': true,
17931         /**
17932              * @event beforedeactivate
17933              * Fires before a tab is de-activated - can be used to do validation on a form.
17934              * @param {Roo.bootstrap.TabPanel} this
17935              * @return {Boolean} false if there is an error
17936             
17937          */
17938         'beforedeactivate': true
17939      });
17940     
17941     this.tabId = this.tabId || Roo.id();
17942   
17943 };
17944
17945 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17946     
17947     active: false,
17948     html: false,
17949     tabId: false,
17950     navId : false,
17951     href : '',
17952     
17953     getAutoCreate : function(){
17954         var cfg = {
17955             tag: 'div',
17956             // item is needed for carousel - not sure if it has any effect otherwise
17957             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17958             html: this.html || ''
17959         };
17960         
17961         if(this.active){
17962             cfg.cls += ' active';
17963         }
17964         
17965         if(this.tabId){
17966             cfg.tabId = this.tabId;
17967         }
17968         
17969         
17970         return cfg;
17971     },
17972     
17973     initEvents:  function()
17974     {
17975         var p = this.parent();
17976         
17977         this.navId = this.navId || p.navId;
17978         
17979         if (typeof(this.navId) != 'undefined') {
17980             // not really needed.. but just in case.. parent should be a NavGroup.
17981             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17982             
17983             tg.register(this);
17984             
17985             var i = tg.tabs.length - 1;
17986             
17987             if(this.active && tg.bullets > 0 && i < tg.bullets){
17988                 tg.setActiveBullet(i);
17989             }
17990         }
17991         
17992         this.el.on('click', this.onClick, this);
17993         
17994         if(Roo.isTouch){
17995             this.el.on("touchstart", this.onTouchStart, this);
17996             this.el.on("touchmove", this.onTouchMove, this);
17997             this.el.on("touchend", this.onTouchEnd, this);
17998         }
17999         
18000     },
18001     
18002     onRender : function(ct, position)
18003     {
18004         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18005     },
18006     
18007     setActive : function(state)
18008     {
18009         Roo.log("panel - set active " + this.tabId + "=" + state);
18010         
18011         this.active = state;
18012         if (!state) {
18013             this.el.removeClass('active');
18014             
18015         } else  if (!this.el.hasClass('active')) {
18016             this.el.addClass('active');
18017         }
18018         
18019         this.fireEvent('changed', this, state);
18020     },
18021     
18022     onClick : function(e)
18023     {
18024         e.preventDefault();
18025         
18026         if(!this.href.length){
18027             return;
18028         }
18029         
18030         window.location.href = this.href;
18031     },
18032     
18033     startX : 0,
18034     startY : 0,
18035     endX : 0,
18036     endY : 0,
18037     swiping : false,
18038     
18039     onTouchStart : function(e)
18040     {
18041         this.swiping = false;
18042         
18043         this.startX = e.browserEvent.touches[0].clientX;
18044         this.startY = e.browserEvent.touches[0].clientY;
18045     },
18046     
18047     onTouchMove : function(e)
18048     {
18049         this.swiping = true;
18050         
18051         this.endX = e.browserEvent.touches[0].clientX;
18052         this.endY = e.browserEvent.touches[0].clientY;
18053     },
18054     
18055     onTouchEnd : function(e)
18056     {
18057         if(!this.swiping){
18058             this.onClick(e);
18059             return;
18060         }
18061         
18062         var tabGroup = this.parent();
18063         
18064         if(this.endX > this.startX){ // swiping right
18065             tabGroup.showPanelPrev();
18066             return;
18067         }
18068         
18069         if(this.startX > this.endX){ // swiping left
18070             tabGroup.showPanelNext();
18071             return;
18072         }
18073     }
18074     
18075     
18076 });
18077  
18078
18079  
18080
18081  /*
18082  * - LGPL
18083  *
18084  * DateField
18085  * 
18086  */
18087
18088 /**
18089  * @class Roo.bootstrap.DateField
18090  * @extends Roo.bootstrap.Input
18091  * Bootstrap DateField class
18092  * @cfg {Number} weekStart default 0
18093  * @cfg {String} viewMode default empty, (months|years)
18094  * @cfg {String} minViewMode default empty, (months|years)
18095  * @cfg {Number} startDate default -Infinity
18096  * @cfg {Number} endDate default Infinity
18097  * @cfg {Boolean} todayHighlight default false
18098  * @cfg {Boolean} todayBtn default false
18099  * @cfg {Boolean} calendarWeeks default false
18100  * @cfg {Object} daysOfWeekDisabled default empty
18101  * @cfg {Boolean} singleMode default false (true | false)
18102  * 
18103  * @cfg {Boolean} keyboardNavigation default true
18104  * @cfg {String} language default en
18105  * 
18106  * @constructor
18107  * Create a new DateField
18108  * @param {Object} config The config object
18109  */
18110
18111 Roo.bootstrap.DateField = function(config){
18112     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18113      this.addEvents({
18114             /**
18115              * @event show
18116              * Fires when this field show.
18117              * @param {Roo.bootstrap.DateField} this
18118              * @param {Mixed} date The date value
18119              */
18120             show : true,
18121             /**
18122              * @event show
18123              * Fires when this field hide.
18124              * @param {Roo.bootstrap.DateField} this
18125              * @param {Mixed} date The date value
18126              */
18127             hide : true,
18128             /**
18129              * @event select
18130              * Fires when select a date.
18131              * @param {Roo.bootstrap.DateField} this
18132              * @param {Mixed} date The date value
18133              */
18134             select : true,
18135             /**
18136              * @event beforeselect
18137              * Fires when before select a date.
18138              * @param {Roo.bootstrap.DateField} this
18139              * @param {Mixed} date The date value
18140              */
18141             beforeselect : true
18142         });
18143 };
18144
18145 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18146     
18147     /**
18148      * @cfg {String} format
18149      * The default date format string which can be overriden for localization support.  The format must be
18150      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18151      */
18152     format : "m/d/y",
18153     /**
18154      * @cfg {String} altFormats
18155      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18156      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18157      */
18158     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18159     
18160     weekStart : 0,
18161     
18162     viewMode : '',
18163     
18164     minViewMode : '',
18165     
18166     todayHighlight : false,
18167     
18168     todayBtn: false,
18169     
18170     language: 'en',
18171     
18172     keyboardNavigation: true,
18173     
18174     calendarWeeks: false,
18175     
18176     startDate: -Infinity,
18177     
18178     endDate: Infinity,
18179     
18180     daysOfWeekDisabled: [],
18181     
18182     _events: [],
18183     
18184     singleMode : false,
18185     
18186     UTCDate: function()
18187     {
18188         return new Date(Date.UTC.apply(Date, arguments));
18189     },
18190     
18191     UTCToday: function()
18192     {
18193         var today = new Date();
18194         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18195     },
18196     
18197     getDate: function() {
18198             var d = this.getUTCDate();
18199             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18200     },
18201     
18202     getUTCDate: function() {
18203             return this.date;
18204     },
18205     
18206     setDate: function(d) {
18207             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18208     },
18209     
18210     setUTCDate: function(d) {
18211             this.date = d;
18212             this.setValue(this.formatDate(this.date));
18213     },
18214         
18215     onRender: function(ct, position)
18216     {
18217         
18218         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18219         
18220         this.language = this.language || 'en';
18221         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18222         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18223         
18224         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18225         this.format = this.format || 'm/d/y';
18226         this.isInline = false;
18227         this.isInput = true;
18228         this.component = this.el.select('.add-on', true).first() || false;
18229         this.component = (this.component && this.component.length === 0) ? false : this.component;
18230         this.hasInput = this.component && this.inputEl().length;
18231         
18232         if (typeof(this.minViewMode === 'string')) {
18233             switch (this.minViewMode) {
18234                 case 'months':
18235                     this.minViewMode = 1;
18236                     break;
18237                 case 'years':
18238                     this.minViewMode = 2;
18239                     break;
18240                 default:
18241                     this.minViewMode = 0;
18242                     break;
18243             }
18244         }
18245         
18246         if (typeof(this.viewMode === 'string')) {
18247             switch (this.viewMode) {
18248                 case 'months':
18249                     this.viewMode = 1;
18250                     break;
18251                 case 'years':
18252                     this.viewMode = 2;
18253                     break;
18254                 default:
18255                     this.viewMode = 0;
18256                     break;
18257             }
18258         }
18259                 
18260         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18261         
18262 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18263         
18264         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18265         
18266         this.picker().on('mousedown', this.onMousedown, this);
18267         this.picker().on('click', this.onClick, this);
18268         
18269         this.picker().addClass('datepicker-dropdown');
18270         
18271         this.startViewMode = this.viewMode;
18272         
18273         if(this.singleMode){
18274             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18275                 v.setVisibilityMode(Roo.Element.DISPLAY);
18276                 v.hide();
18277             });
18278             
18279             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18280                 v.setStyle('width', '189px');
18281             });
18282         }
18283         
18284         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18285             if(!this.calendarWeeks){
18286                 v.remove();
18287                 return;
18288             }
18289             
18290             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18291             v.attr('colspan', function(i, val){
18292                 return parseInt(val) + 1;
18293             });
18294         });
18295                         
18296         
18297         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18298         
18299         this.setStartDate(this.startDate);
18300         this.setEndDate(this.endDate);
18301         
18302         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18303         
18304         this.fillDow();
18305         this.fillMonths();
18306         this.update();
18307         this.showMode();
18308         
18309         if(this.isInline) {
18310             this.show();
18311         }
18312     },
18313     
18314     picker : function()
18315     {
18316         return this.pickerEl;
18317 //        return this.el.select('.datepicker', true).first();
18318     },
18319     
18320     fillDow: function()
18321     {
18322         var dowCnt = this.weekStart;
18323         
18324         var dow = {
18325             tag: 'tr',
18326             cn: [
18327                 
18328             ]
18329         };
18330         
18331         if(this.calendarWeeks){
18332             dow.cn.push({
18333                 tag: 'th',
18334                 cls: 'cw',
18335                 html: '&nbsp;'
18336             })
18337         }
18338         
18339         while (dowCnt < this.weekStart + 7) {
18340             dow.cn.push({
18341                 tag: 'th',
18342                 cls: 'dow',
18343                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18344             });
18345         }
18346         
18347         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18348     },
18349     
18350     fillMonths: function()
18351     {    
18352         var i = 0;
18353         var months = this.picker().select('>.datepicker-months td', true).first();
18354         
18355         months.dom.innerHTML = '';
18356         
18357         while (i < 12) {
18358             var month = {
18359                 tag: 'span',
18360                 cls: 'month',
18361                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18362             };
18363             
18364             months.createChild(month);
18365         }
18366         
18367     },
18368     
18369     update: function()
18370     {
18371         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;
18372         
18373         if (this.date < this.startDate) {
18374             this.viewDate = new Date(this.startDate);
18375         } else if (this.date > this.endDate) {
18376             this.viewDate = new Date(this.endDate);
18377         } else {
18378             this.viewDate = new Date(this.date);
18379         }
18380         
18381         this.fill();
18382     },
18383     
18384     fill: function() 
18385     {
18386         var d = new Date(this.viewDate),
18387                 year = d.getUTCFullYear(),
18388                 month = d.getUTCMonth(),
18389                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18390                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18391                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18392                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18393                 currentDate = this.date && this.date.valueOf(),
18394                 today = this.UTCToday();
18395         
18396         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18397         
18398 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18399         
18400 //        this.picker.select('>tfoot th.today').
18401 //                                              .text(dates[this.language].today)
18402 //                                              .toggle(this.todayBtn !== false);
18403     
18404         this.updateNavArrows();
18405         this.fillMonths();
18406                                                 
18407         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18408         
18409         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18410          
18411         prevMonth.setUTCDate(day);
18412         
18413         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18414         
18415         var nextMonth = new Date(prevMonth);
18416         
18417         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18418         
18419         nextMonth = nextMonth.valueOf();
18420         
18421         var fillMonths = false;
18422         
18423         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18424         
18425         while(prevMonth.valueOf() < nextMonth) {
18426             var clsName = '';
18427             
18428             if (prevMonth.getUTCDay() === this.weekStart) {
18429                 if(fillMonths){
18430                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18431                 }
18432                     
18433                 fillMonths = {
18434                     tag: 'tr',
18435                     cn: []
18436                 };
18437                 
18438                 if(this.calendarWeeks){
18439                     // ISO 8601: First week contains first thursday.
18440                     // ISO also states week starts on Monday, but we can be more abstract here.
18441                     var
18442                     // Start of current week: based on weekstart/current date
18443                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18444                     // Thursday of this week
18445                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18446                     // First Thursday of year, year from thursday
18447                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18448                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18449                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18450                     
18451                     fillMonths.cn.push({
18452                         tag: 'td',
18453                         cls: 'cw',
18454                         html: calWeek
18455                     });
18456                 }
18457             }
18458             
18459             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18460                 clsName += ' old';
18461             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18462                 clsName += ' new';
18463             }
18464             if (this.todayHighlight &&
18465                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18466                 prevMonth.getUTCMonth() == today.getMonth() &&
18467                 prevMonth.getUTCDate() == today.getDate()) {
18468                 clsName += ' today';
18469             }
18470             
18471             if (currentDate && prevMonth.valueOf() === currentDate) {
18472                 clsName += ' active';
18473             }
18474             
18475             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18476                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18477                     clsName += ' disabled';
18478             }
18479             
18480             fillMonths.cn.push({
18481                 tag: 'td',
18482                 cls: 'day ' + clsName,
18483                 html: prevMonth.getDate()
18484             });
18485             
18486             prevMonth.setDate(prevMonth.getDate()+1);
18487         }
18488           
18489         var currentYear = this.date && this.date.getUTCFullYear();
18490         var currentMonth = this.date && this.date.getUTCMonth();
18491         
18492         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18493         
18494         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18495             v.removeClass('active');
18496             
18497             if(currentYear === year && k === currentMonth){
18498                 v.addClass('active');
18499             }
18500             
18501             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18502                 v.addClass('disabled');
18503             }
18504             
18505         });
18506         
18507         
18508         year = parseInt(year/10, 10) * 10;
18509         
18510         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18511         
18512         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18513         
18514         year -= 1;
18515         for (var i = -1; i < 11; i++) {
18516             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18517                 tag: 'span',
18518                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18519                 html: year
18520             });
18521             
18522             year += 1;
18523         }
18524     },
18525     
18526     showMode: function(dir) 
18527     {
18528         if (dir) {
18529             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18530         }
18531         
18532         Roo.each(this.picker().select('>div',true).elements, function(v){
18533             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18534             v.hide();
18535         });
18536         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18537     },
18538     
18539     place: function()
18540     {
18541         if(this.isInline) {
18542             return;
18543         }
18544         
18545         this.picker().removeClass(['bottom', 'top']);
18546         
18547         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18548             /*
18549              * place to the top of element!
18550              *
18551              */
18552             
18553             this.picker().addClass('top');
18554             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18555             
18556             return;
18557         }
18558         
18559         this.picker().addClass('bottom');
18560         
18561         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18562     },
18563     
18564     parseDate : function(value)
18565     {
18566         if(!value || value instanceof Date){
18567             return value;
18568         }
18569         var v = Date.parseDate(value, this.format);
18570         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18571             v = Date.parseDate(value, 'Y-m-d');
18572         }
18573         if(!v && this.altFormats){
18574             if(!this.altFormatsArray){
18575                 this.altFormatsArray = this.altFormats.split("|");
18576             }
18577             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18578                 v = Date.parseDate(value, this.altFormatsArray[i]);
18579             }
18580         }
18581         return v;
18582     },
18583     
18584     formatDate : function(date, fmt)
18585     {   
18586         return (!date || !(date instanceof Date)) ?
18587         date : date.dateFormat(fmt || this.format);
18588     },
18589     
18590     onFocus : function()
18591     {
18592         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18593         this.show();
18594     },
18595     
18596     onBlur : function()
18597     {
18598         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18599         
18600         var d = this.inputEl().getValue();
18601         
18602         this.setValue(d);
18603                 
18604         this.hide();
18605     },
18606     
18607     show : function()
18608     {
18609         this.picker().show();
18610         this.update();
18611         this.place();
18612         
18613         this.fireEvent('show', this, this.date);
18614     },
18615     
18616     hide : function()
18617     {
18618         if(this.isInline) {
18619             return;
18620         }
18621         this.picker().hide();
18622         this.viewMode = this.startViewMode;
18623         this.showMode();
18624         
18625         this.fireEvent('hide', this, this.date);
18626         
18627     },
18628     
18629     onMousedown: function(e)
18630     {
18631         e.stopPropagation();
18632         e.preventDefault();
18633     },
18634     
18635     keyup: function(e)
18636     {
18637         Roo.bootstrap.DateField.superclass.keyup.call(this);
18638         this.update();
18639     },
18640
18641     setValue: function(v)
18642     {
18643         if(this.fireEvent('beforeselect', this, v) !== false){
18644             var d = new Date(this.parseDate(v) ).clearTime();
18645         
18646             if(isNaN(d.getTime())){
18647                 this.date = this.viewDate = '';
18648                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18649                 return;
18650             }
18651
18652             v = this.formatDate(d);
18653
18654             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18655
18656             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18657
18658             this.update();
18659
18660             this.fireEvent('select', this, this.date);
18661         }
18662     },
18663     
18664     getValue: function()
18665     {
18666         return this.formatDate(this.date);
18667     },
18668     
18669     fireKey: function(e)
18670     {
18671         if (!this.picker().isVisible()){
18672             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18673                 this.show();
18674             }
18675             return;
18676         }
18677         
18678         var dateChanged = false,
18679         dir, day, month,
18680         newDate, newViewDate;
18681         
18682         switch(e.keyCode){
18683             case 27: // escape
18684                 this.hide();
18685                 e.preventDefault();
18686                 break;
18687             case 37: // left
18688             case 39: // right
18689                 if (!this.keyboardNavigation) {
18690                     break;
18691                 }
18692                 dir = e.keyCode == 37 ? -1 : 1;
18693                 
18694                 if (e.ctrlKey){
18695                     newDate = this.moveYear(this.date, dir);
18696                     newViewDate = this.moveYear(this.viewDate, dir);
18697                 } else if (e.shiftKey){
18698                     newDate = this.moveMonth(this.date, dir);
18699                     newViewDate = this.moveMonth(this.viewDate, dir);
18700                 } else {
18701                     newDate = new Date(this.date);
18702                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18703                     newViewDate = new Date(this.viewDate);
18704                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18705                 }
18706                 if (this.dateWithinRange(newDate)){
18707                     this.date = newDate;
18708                     this.viewDate = newViewDate;
18709                     this.setValue(this.formatDate(this.date));
18710 //                    this.update();
18711                     e.preventDefault();
18712                     dateChanged = true;
18713                 }
18714                 break;
18715             case 38: // up
18716             case 40: // down
18717                 if (!this.keyboardNavigation) {
18718                     break;
18719                 }
18720                 dir = e.keyCode == 38 ? -1 : 1;
18721                 if (e.ctrlKey){
18722                     newDate = this.moveYear(this.date, dir);
18723                     newViewDate = this.moveYear(this.viewDate, dir);
18724                 } else if (e.shiftKey){
18725                     newDate = this.moveMonth(this.date, dir);
18726                     newViewDate = this.moveMonth(this.viewDate, dir);
18727                 } else {
18728                     newDate = new Date(this.date);
18729                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18730                     newViewDate = new Date(this.viewDate);
18731                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18732                 }
18733                 if (this.dateWithinRange(newDate)){
18734                     this.date = newDate;
18735                     this.viewDate = newViewDate;
18736                     this.setValue(this.formatDate(this.date));
18737 //                    this.update();
18738                     e.preventDefault();
18739                     dateChanged = true;
18740                 }
18741                 break;
18742             case 13: // enter
18743                 this.setValue(this.formatDate(this.date));
18744                 this.hide();
18745                 e.preventDefault();
18746                 break;
18747             case 9: // tab
18748                 this.setValue(this.formatDate(this.date));
18749                 this.hide();
18750                 break;
18751             case 16: // shift
18752             case 17: // ctrl
18753             case 18: // alt
18754                 break;
18755             default :
18756                 this.hide();
18757                 
18758         }
18759     },
18760     
18761     
18762     onClick: function(e) 
18763     {
18764         e.stopPropagation();
18765         e.preventDefault();
18766         
18767         var target = e.getTarget();
18768         
18769         if(target.nodeName.toLowerCase() === 'i'){
18770             target = Roo.get(target).dom.parentNode;
18771         }
18772         
18773         var nodeName = target.nodeName;
18774         var className = target.className;
18775         var html = target.innerHTML;
18776         //Roo.log(nodeName);
18777         
18778         switch(nodeName.toLowerCase()) {
18779             case 'th':
18780                 switch(className) {
18781                     case 'switch':
18782                         this.showMode(1);
18783                         break;
18784                     case 'prev':
18785                     case 'next':
18786                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18787                         switch(this.viewMode){
18788                                 case 0:
18789                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18790                                         break;
18791                                 case 1:
18792                                 case 2:
18793                                         this.viewDate = this.moveYear(this.viewDate, dir);
18794                                         break;
18795                         }
18796                         this.fill();
18797                         break;
18798                     case 'today':
18799                         var date = new Date();
18800                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18801 //                        this.fill()
18802                         this.setValue(this.formatDate(this.date));
18803                         
18804                         this.hide();
18805                         break;
18806                 }
18807                 break;
18808             case 'span':
18809                 if (className.indexOf('disabled') < 0) {
18810                     this.viewDate.setUTCDate(1);
18811                     if (className.indexOf('month') > -1) {
18812                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18813                     } else {
18814                         var year = parseInt(html, 10) || 0;
18815                         this.viewDate.setUTCFullYear(year);
18816                         
18817                     }
18818                     
18819                     if(this.singleMode){
18820                         this.setValue(this.formatDate(this.viewDate));
18821                         this.hide();
18822                         return;
18823                     }
18824                     
18825                     this.showMode(-1);
18826                     this.fill();
18827                 }
18828                 break;
18829                 
18830             case 'td':
18831                 //Roo.log(className);
18832                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18833                     var day = parseInt(html, 10) || 1;
18834                     var year = this.viewDate.getUTCFullYear(),
18835                         month = this.viewDate.getUTCMonth();
18836
18837                     if (className.indexOf('old') > -1) {
18838                         if(month === 0 ){
18839                             month = 11;
18840                             year -= 1;
18841                         }else{
18842                             month -= 1;
18843                         }
18844                     } else if (className.indexOf('new') > -1) {
18845                         if (month == 11) {
18846                             month = 0;
18847                             year += 1;
18848                         } else {
18849                             month += 1;
18850                         }
18851                     }
18852                     //Roo.log([year,month,day]);
18853                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18854                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18855 //                    this.fill();
18856                     //Roo.log(this.formatDate(this.date));
18857                     this.setValue(this.formatDate(this.date));
18858                     this.hide();
18859                 }
18860                 break;
18861         }
18862     },
18863     
18864     setStartDate: function(startDate)
18865     {
18866         this.startDate = startDate || -Infinity;
18867         if (this.startDate !== -Infinity) {
18868             this.startDate = this.parseDate(this.startDate);
18869         }
18870         this.update();
18871         this.updateNavArrows();
18872     },
18873
18874     setEndDate: function(endDate)
18875     {
18876         this.endDate = endDate || Infinity;
18877         if (this.endDate !== Infinity) {
18878             this.endDate = this.parseDate(this.endDate);
18879         }
18880         this.update();
18881         this.updateNavArrows();
18882     },
18883     
18884     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18885     {
18886         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18887         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18888             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18889         }
18890         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18891             return parseInt(d, 10);
18892         });
18893         this.update();
18894         this.updateNavArrows();
18895     },
18896     
18897     updateNavArrows: function() 
18898     {
18899         if(this.singleMode){
18900             return;
18901         }
18902         
18903         var d = new Date(this.viewDate),
18904         year = d.getUTCFullYear(),
18905         month = d.getUTCMonth();
18906         
18907         Roo.each(this.picker().select('.prev', true).elements, function(v){
18908             v.show();
18909             switch (this.viewMode) {
18910                 case 0:
18911
18912                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18913                         v.hide();
18914                     }
18915                     break;
18916                 case 1:
18917                 case 2:
18918                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18919                         v.hide();
18920                     }
18921                     break;
18922             }
18923         });
18924         
18925         Roo.each(this.picker().select('.next', true).elements, function(v){
18926             v.show();
18927             switch (this.viewMode) {
18928                 case 0:
18929
18930                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18931                         v.hide();
18932                     }
18933                     break;
18934                 case 1:
18935                 case 2:
18936                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18937                         v.hide();
18938                     }
18939                     break;
18940             }
18941         })
18942     },
18943     
18944     moveMonth: function(date, dir)
18945     {
18946         if (!dir) {
18947             return date;
18948         }
18949         var new_date = new Date(date.valueOf()),
18950         day = new_date.getUTCDate(),
18951         month = new_date.getUTCMonth(),
18952         mag = Math.abs(dir),
18953         new_month, test;
18954         dir = dir > 0 ? 1 : -1;
18955         if (mag == 1){
18956             test = dir == -1
18957             // If going back one month, make sure month is not current month
18958             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18959             ? function(){
18960                 return new_date.getUTCMonth() == month;
18961             }
18962             // If going forward one month, make sure month is as expected
18963             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18964             : function(){
18965                 return new_date.getUTCMonth() != new_month;
18966             };
18967             new_month = month + dir;
18968             new_date.setUTCMonth(new_month);
18969             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18970             if (new_month < 0 || new_month > 11) {
18971                 new_month = (new_month + 12) % 12;
18972             }
18973         } else {
18974             // For magnitudes >1, move one month at a time...
18975             for (var i=0; i<mag; i++) {
18976                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18977                 new_date = this.moveMonth(new_date, dir);
18978             }
18979             // ...then reset the day, keeping it in the new month
18980             new_month = new_date.getUTCMonth();
18981             new_date.setUTCDate(day);
18982             test = function(){
18983                 return new_month != new_date.getUTCMonth();
18984             };
18985         }
18986         // Common date-resetting loop -- if date is beyond end of month, make it
18987         // end of month
18988         while (test()){
18989             new_date.setUTCDate(--day);
18990             new_date.setUTCMonth(new_month);
18991         }
18992         return new_date;
18993     },
18994
18995     moveYear: function(date, dir)
18996     {
18997         return this.moveMonth(date, dir*12);
18998     },
18999
19000     dateWithinRange: function(date)
19001     {
19002         return date >= this.startDate && date <= this.endDate;
19003     },
19004
19005     
19006     remove: function() 
19007     {
19008         this.picker().remove();
19009     },
19010     
19011     validateValue : function(value)
19012     {
19013         if(value.length < 1)  {
19014             if(this.allowBlank){
19015                 return true;
19016             }
19017             return false;
19018         }
19019         
19020         if(value.length < this.minLength){
19021             return false;
19022         }
19023         if(value.length > this.maxLength){
19024             return false;
19025         }
19026         if(this.vtype){
19027             var vt = Roo.form.VTypes;
19028             if(!vt[this.vtype](value, this)){
19029                 return false;
19030             }
19031         }
19032         if(typeof this.validator == "function"){
19033             var msg = this.validator(value);
19034             if(msg !== true){
19035                 return false;
19036             }
19037         }
19038         
19039         if(this.regex && !this.regex.test(value)){
19040             return false;
19041         }
19042         
19043         if(typeof(this.parseDate(value)) == 'undefined'){
19044             return false;
19045         }
19046         
19047         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19048             return false;
19049         }      
19050         
19051         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19052             return false;
19053         } 
19054         
19055         
19056         return true;
19057     }
19058    
19059 });
19060
19061 Roo.apply(Roo.bootstrap.DateField,  {
19062     
19063     head : {
19064         tag: 'thead',
19065         cn: [
19066         {
19067             tag: 'tr',
19068             cn: [
19069             {
19070                 tag: 'th',
19071                 cls: 'prev',
19072                 html: '<i class="fa fa-arrow-left"/>'
19073             },
19074             {
19075                 tag: 'th',
19076                 cls: 'switch',
19077                 colspan: '5'
19078             },
19079             {
19080                 tag: 'th',
19081                 cls: 'next',
19082                 html: '<i class="fa fa-arrow-right"/>'
19083             }
19084
19085             ]
19086         }
19087         ]
19088     },
19089     
19090     content : {
19091         tag: 'tbody',
19092         cn: [
19093         {
19094             tag: 'tr',
19095             cn: [
19096             {
19097                 tag: 'td',
19098                 colspan: '7'
19099             }
19100             ]
19101         }
19102         ]
19103     },
19104     
19105     footer : {
19106         tag: 'tfoot',
19107         cn: [
19108         {
19109             tag: 'tr',
19110             cn: [
19111             {
19112                 tag: 'th',
19113                 colspan: '7',
19114                 cls: 'today'
19115             }
19116                     
19117             ]
19118         }
19119         ]
19120     },
19121     
19122     dates:{
19123         en: {
19124             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19125             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19126             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19127             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19128             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19129             today: "Today"
19130         }
19131     },
19132     
19133     modes: [
19134     {
19135         clsName: 'days',
19136         navFnc: 'Month',
19137         navStep: 1
19138     },
19139     {
19140         clsName: 'months',
19141         navFnc: 'FullYear',
19142         navStep: 1
19143     },
19144     {
19145         clsName: 'years',
19146         navFnc: 'FullYear',
19147         navStep: 10
19148     }]
19149 });
19150
19151 Roo.apply(Roo.bootstrap.DateField,  {
19152   
19153     template : {
19154         tag: 'div',
19155         cls: 'datepicker dropdown-menu roo-dynamic',
19156         cn: [
19157         {
19158             tag: 'div',
19159             cls: 'datepicker-days',
19160             cn: [
19161             {
19162                 tag: 'table',
19163                 cls: 'table-condensed',
19164                 cn:[
19165                 Roo.bootstrap.DateField.head,
19166                 {
19167                     tag: 'tbody'
19168                 },
19169                 Roo.bootstrap.DateField.footer
19170                 ]
19171             }
19172             ]
19173         },
19174         {
19175             tag: 'div',
19176             cls: 'datepicker-months',
19177             cn: [
19178             {
19179                 tag: 'table',
19180                 cls: 'table-condensed',
19181                 cn:[
19182                 Roo.bootstrap.DateField.head,
19183                 Roo.bootstrap.DateField.content,
19184                 Roo.bootstrap.DateField.footer
19185                 ]
19186             }
19187             ]
19188         },
19189         {
19190             tag: 'div',
19191             cls: 'datepicker-years',
19192             cn: [
19193             {
19194                 tag: 'table',
19195                 cls: 'table-condensed',
19196                 cn:[
19197                 Roo.bootstrap.DateField.head,
19198                 Roo.bootstrap.DateField.content,
19199                 Roo.bootstrap.DateField.footer
19200                 ]
19201             }
19202             ]
19203         }
19204         ]
19205     }
19206 });
19207
19208  
19209
19210  /*
19211  * - LGPL
19212  *
19213  * TimeField
19214  * 
19215  */
19216
19217 /**
19218  * @class Roo.bootstrap.TimeField
19219  * @extends Roo.bootstrap.Input
19220  * Bootstrap DateField class
19221  * 
19222  * 
19223  * @constructor
19224  * Create a new TimeField
19225  * @param {Object} config The config object
19226  */
19227
19228 Roo.bootstrap.TimeField = function(config){
19229     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19230     this.addEvents({
19231             /**
19232              * @event show
19233              * Fires when this field show.
19234              * @param {Roo.bootstrap.DateField} thisthis
19235              * @param {Mixed} date The date value
19236              */
19237             show : true,
19238             /**
19239              * @event show
19240              * Fires when this field hide.
19241              * @param {Roo.bootstrap.DateField} this
19242              * @param {Mixed} date The date value
19243              */
19244             hide : true,
19245             /**
19246              * @event select
19247              * Fires when select a date.
19248              * @param {Roo.bootstrap.DateField} this
19249              * @param {Mixed} date The date value
19250              */
19251             select : true
19252         });
19253 };
19254
19255 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19256     
19257     /**
19258      * @cfg {String} format
19259      * The default time format string which can be overriden for localization support.  The format must be
19260      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19261      */
19262     format : "H:i",
19263        
19264     onRender: function(ct, position)
19265     {
19266         
19267         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19268                 
19269         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19270         
19271         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19272         
19273         this.pop = this.picker().select('>.datepicker-time',true).first();
19274         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19275         
19276         this.picker().on('mousedown', this.onMousedown, this);
19277         this.picker().on('click', this.onClick, this);
19278         
19279         this.picker().addClass('datepicker-dropdown');
19280     
19281         this.fillTime();
19282         this.update();
19283             
19284         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19285         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19286         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19287         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19288         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19289         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19290
19291     },
19292     
19293     fireKey: function(e){
19294         if (!this.picker().isVisible()){
19295             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19296                 this.show();
19297             }
19298             return;
19299         }
19300
19301         e.preventDefault();
19302         
19303         switch(e.keyCode){
19304             case 27: // escape
19305                 this.hide();
19306                 break;
19307             case 37: // left
19308             case 39: // right
19309                 this.onTogglePeriod();
19310                 break;
19311             case 38: // up
19312                 this.onIncrementMinutes();
19313                 break;
19314             case 40: // down
19315                 this.onDecrementMinutes();
19316                 break;
19317             case 13: // enter
19318             case 9: // tab
19319                 this.setTime();
19320                 break;
19321         }
19322     },
19323     
19324     onClick: function(e) {
19325         e.stopPropagation();
19326         e.preventDefault();
19327     },
19328     
19329     picker : function()
19330     {
19331         return this.el.select('.datepicker', true).first();
19332     },
19333     
19334     fillTime: function()
19335     {    
19336         var time = this.pop.select('tbody', true).first();
19337         
19338         time.dom.innerHTML = '';
19339         
19340         time.createChild({
19341             tag: 'tr',
19342             cn: [
19343                 {
19344                     tag: 'td',
19345                     cn: [
19346                         {
19347                             tag: 'a',
19348                             href: '#',
19349                             cls: 'btn',
19350                             cn: [
19351                                 {
19352                                     tag: 'span',
19353                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19354                                 }
19355                             ]
19356                         } 
19357                     ]
19358                 },
19359                 {
19360                     tag: 'td',
19361                     cls: 'separator'
19362                 },
19363                 {
19364                     tag: 'td',
19365                     cn: [
19366                         {
19367                             tag: 'a',
19368                             href: '#',
19369                             cls: 'btn',
19370                             cn: [
19371                                 {
19372                                     tag: 'span',
19373                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19374                                 }
19375                             ]
19376                         }
19377                     ]
19378                 },
19379                 {
19380                     tag: 'td',
19381                     cls: 'separator'
19382                 }
19383             ]
19384         });
19385         
19386         time.createChild({
19387             tag: 'tr',
19388             cn: [
19389                 {
19390                     tag: 'td',
19391                     cn: [
19392                         {
19393                             tag: 'span',
19394                             cls: 'timepicker-hour',
19395                             html: '00'
19396                         }  
19397                     ]
19398                 },
19399                 {
19400                     tag: 'td',
19401                     cls: 'separator',
19402                     html: ':'
19403                 },
19404                 {
19405                     tag: 'td',
19406                     cn: [
19407                         {
19408                             tag: 'span',
19409                             cls: 'timepicker-minute',
19410                             html: '00'
19411                         }  
19412                     ]
19413                 },
19414                 {
19415                     tag: 'td',
19416                     cls: 'separator'
19417                 },
19418                 {
19419                     tag: 'td',
19420                     cn: [
19421                         {
19422                             tag: 'button',
19423                             type: 'button',
19424                             cls: 'btn btn-primary period',
19425                             html: 'AM'
19426                             
19427                         }
19428                     ]
19429                 }
19430             ]
19431         });
19432         
19433         time.createChild({
19434             tag: 'tr',
19435             cn: [
19436                 {
19437                     tag: 'td',
19438                     cn: [
19439                         {
19440                             tag: 'a',
19441                             href: '#',
19442                             cls: 'btn',
19443                             cn: [
19444                                 {
19445                                     tag: 'span',
19446                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19447                                 }
19448                             ]
19449                         }
19450                     ]
19451                 },
19452                 {
19453                     tag: 'td',
19454                     cls: 'separator'
19455                 },
19456                 {
19457                     tag: 'td',
19458                     cn: [
19459                         {
19460                             tag: 'a',
19461                             href: '#',
19462                             cls: 'btn',
19463                             cn: [
19464                                 {
19465                                     tag: 'span',
19466                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19467                                 }
19468                             ]
19469                         }
19470                     ]
19471                 },
19472                 {
19473                     tag: 'td',
19474                     cls: 'separator'
19475                 }
19476             ]
19477         });
19478         
19479     },
19480     
19481     update: function()
19482     {
19483         
19484         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19485         
19486         this.fill();
19487     },
19488     
19489     fill: function() 
19490     {
19491         var hours = this.time.getHours();
19492         var minutes = this.time.getMinutes();
19493         var period = 'AM';
19494         
19495         if(hours > 11){
19496             period = 'PM';
19497         }
19498         
19499         if(hours == 0){
19500             hours = 12;
19501         }
19502         
19503         
19504         if(hours > 12){
19505             hours = hours - 12;
19506         }
19507         
19508         if(hours < 10){
19509             hours = '0' + hours;
19510         }
19511         
19512         if(minutes < 10){
19513             minutes = '0' + minutes;
19514         }
19515         
19516         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19517         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19518         this.pop.select('button', true).first().dom.innerHTML = period;
19519         
19520     },
19521     
19522     place: function()
19523     {   
19524         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19525         
19526         var cls = ['bottom'];
19527         
19528         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19529             cls.pop();
19530             cls.push('top');
19531         }
19532         
19533         cls.push('right');
19534         
19535         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19536             cls.pop();
19537             cls.push('left');
19538         }
19539         
19540         this.picker().addClass(cls.join('-'));
19541         
19542         var _this = this;
19543         
19544         Roo.each(cls, function(c){
19545             if(c == 'bottom'){
19546                 _this.picker().setTop(_this.inputEl().getHeight());
19547                 return;
19548             }
19549             if(c == 'top'){
19550                 _this.picker().setTop(0 - _this.picker().getHeight());
19551                 return;
19552             }
19553             
19554             if(c == 'left'){
19555                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19556                 return;
19557             }
19558             if(c == 'right'){
19559                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19560                 return;
19561             }
19562         });
19563         
19564     },
19565   
19566     onFocus : function()
19567     {
19568         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19569         this.show();
19570     },
19571     
19572     onBlur : function()
19573     {
19574         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19575         this.hide();
19576     },
19577     
19578     show : function()
19579     {
19580         this.picker().show();
19581         this.pop.show();
19582         this.update();
19583         this.place();
19584         
19585         this.fireEvent('show', this, this.date);
19586     },
19587     
19588     hide : function()
19589     {
19590         this.picker().hide();
19591         this.pop.hide();
19592         
19593         this.fireEvent('hide', this, this.date);
19594     },
19595     
19596     setTime : function()
19597     {
19598         this.hide();
19599         this.setValue(this.time.format(this.format));
19600         
19601         this.fireEvent('select', this, this.date);
19602         
19603         
19604     },
19605     
19606     onMousedown: function(e){
19607         e.stopPropagation();
19608         e.preventDefault();
19609     },
19610     
19611     onIncrementHours: function()
19612     {
19613         Roo.log('onIncrementHours');
19614         this.time = this.time.add(Date.HOUR, 1);
19615         this.update();
19616         
19617     },
19618     
19619     onDecrementHours: function()
19620     {
19621         Roo.log('onDecrementHours');
19622         this.time = this.time.add(Date.HOUR, -1);
19623         this.update();
19624     },
19625     
19626     onIncrementMinutes: function()
19627     {
19628         Roo.log('onIncrementMinutes');
19629         this.time = this.time.add(Date.MINUTE, 1);
19630         this.update();
19631     },
19632     
19633     onDecrementMinutes: function()
19634     {
19635         Roo.log('onDecrementMinutes');
19636         this.time = this.time.add(Date.MINUTE, -1);
19637         this.update();
19638     },
19639     
19640     onTogglePeriod: function()
19641     {
19642         Roo.log('onTogglePeriod');
19643         this.time = this.time.add(Date.HOUR, 12);
19644         this.update();
19645     }
19646     
19647    
19648 });
19649
19650 Roo.apply(Roo.bootstrap.TimeField,  {
19651     
19652     content : {
19653         tag: 'tbody',
19654         cn: [
19655             {
19656                 tag: 'tr',
19657                 cn: [
19658                 {
19659                     tag: 'td',
19660                     colspan: '7'
19661                 }
19662                 ]
19663             }
19664         ]
19665     },
19666     
19667     footer : {
19668         tag: 'tfoot',
19669         cn: [
19670             {
19671                 tag: 'tr',
19672                 cn: [
19673                 {
19674                     tag: 'th',
19675                     colspan: '7',
19676                     cls: '',
19677                     cn: [
19678                         {
19679                             tag: 'button',
19680                             cls: 'btn btn-info ok',
19681                             html: 'OK'
19682                         }
19683                     ]
19684                 }
19685
19686                 ]
19687             }
19688         ]
19689     }
19690 });
19691
19692 Roo.apply(Roo.bootstrap.TimeField,  {
19693   
19694     template : {
19695         tag: 'div',
19696         cls: 'datepicker dropdown-menu',
19697         cn: [
19698             {
19699                 tag: 'div',
19700                 cls: 'datepicker-time',
19701                 cn: [
19702                 {
19703                     tag: 'table',
19704                     cls: 'table-condensed',
19705                     cn:[
19706                     Roo.bootstrap.TimeField.content,
19707                     Roo.bootstrap.TimeField.footer
19708                     ]
19709                 }
19710                 ]
19711             }
19712         ]
19713     }
19714 });
19715
19716  
19717
19718  /*
19719  * - LGPL
19720  *
19721  * MonthField
19722  * 
19723  */
19724
19725 /**
19726  * @class Roo.bootstrap.MonthField
19727  * @extends Roo.bootstrap.Input
19728  * Bootstrap MonthField class
19729  * 
19730  * @cfg {String} language default en
19731  * 
19732  * @constructor
19733  * Create a new MonthField
19734  * @param {Object} config The config object
19735  */
19736
19737 Roo.bootstrap.MonthField = function(config){
19738     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19739     
19740     this.addEvents({
19741         /**
19742          * @event show
19743          * Fires when this field show.
19744          * @param {Roo.bootstrap.MonthField} this
19745          * @param {Mixed} date The date value
19746          */
19747         show : true,
19748         /**
19749          * @event show
19750          * Fires when this field hide.
19751          * @param {Roo.bootstrap.MonthField} this
19752          * @param {Mixed} date The date value
19753          */
19754         hide : true,
19755         /**
19756          * @event select
19757          * Fires when select a date.
19758          * @param {Roo.bootstrap.MonthField} this
19759          * @param {String} oldvalue The old value
19760          * @param {String} newvalue The new value
19761          */
19762         select : true
19763     });
19764 };
19765
19766 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19767     
19768     onRender: function(ct, position)
19769     {
19770         
19771         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19772         
19773         this.language = this.language || 'en';
19774         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19775         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19776         
19777         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19778         this.isInline = false;
19779         this.isInput = true;
19780         this.component = this.el.select('.add-on', true).first() || false;
19781         this.component = (this.component && this.component.length === 0) ? false : this.component;
19782         this.hasInput = this.component && this.inputEL().length;
19783         
19784         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19785         
19786         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19787         
19788         this.picker().on('mousedown', this.onMousedown, this);
19789         this.picker().on('click', this.onClick, this);
19790         
19791         this.picker().addClass('datepicker-dropdown');
19792         
19793         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19794             v.setStyle('width', '189px');
19795         });
19796         
19797         this.fillMonths();
19798         
19799         this.update();
19800         
19801         if(this.isInline) {
19802             this.show();
19803         }
19804         
19805     },
19806     
19807     setValue: function(v, suppressEvent)
19808     {   
19809         var o = this.getValue();
19810         
19811         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19812         
19813         this.update();
19814
19815         if(suppressEvent !== true){
19816             this.fireEvent('select', this, o, v);
19817         }
19818         
19819     },
19820     
19821     getValue: function()
19822     {
19823         return this.value;
19824     },
19825     
19826     onClick: function(e) 
19827     {
19828         e.stopPropagation();
19829         e.preventDefault();
19830         
19831         var target = e.getTarget();
19832         
19833         if(target.nodeName.toLowerCase() === 'i'){
19834             target = Roo.get(target).dom.parentNode;
19835         }
19836         
19837         var nodeName = target.nodeName;
19838         var className = target.className;
19839         var html = target.innerHTML;
19840         
19841         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19842             return;
19843         }
19844         
19845         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19846         
19847         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19848         
19849         this.hide();
19850                         
19851     },
19852     
19853     picker : function()
19854     {
19855         return this.pickerEl;
19856     },
19857     
19858     fillMonths: function()
19859     {    
19860         var i = 0;
19861         var months = this.picker().select('>.datepicker-months td', true).first();
19862         
19863         months.dom.innerHTML = '';
19864         
19865         while (i < 12) {
19866             var month = {
19867                 tag: 'span',
19868                 cls: 'month',
19869                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19870             };
19871             
19872             months.createChild(month);
19873         }
19874         
19875     },
19876     
19877     update: function()
19878     {
19879         var _this = this;
19880         
19881         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19882             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19883         }
19884         
19885         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19886             e.removeClass('active');
19887             
19888             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19889                 e.addClass('active');
19890             }
19891         })
19892     },
19893     
19894     place: function()
19895     {
19896         if(this.isInline) {
19897             return;
19898         }
19899         
19900         this.picker().removeClass(['bottom', 'top']);
19901         
19902         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19903             /*
19904              * place to the top of element!
19905              *
19906              */
19907             
19908             this.picker().addClass('top');
19909             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19910             
19911             return;
19912         }
19913         
19914         this.picker().addClass('bottom');
19915         
19916         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19917     },
19918     
19919     onFocus : function()
19920     {
19921         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19922         this.show();
19923     },
19924     
19925     onBlur : function()
19926     {
19927         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19928         
19929         var d = this.inputEl().getValue();
19930         
19931         this.setValue(d);
19932                 
19933         this.hide();
19934     },
19935     
19936     show : function()
19937     {
19938         this.picker().show();
19939         this.picker().select('>.datepicker-months', true).first().show();
19940         this.update();
19941         this.place();
19942         
19943         this.fireEvent('show', this, this.date);
19944     },
19945     
19946     hide : function()
19947     {
19948         if(this.isInline) {
19949             return;
19950         }
19951         this.picker().hide();
19952         this.fireEvent('hide', this, this.date);
19953         
19954     },
19955     
19956     onMousedown: function(e)
19957     {
19958         e.stopPropagation();
19959         e.preventDefault();
19960     },
19961     
19962     keyup: function(e)
19963     {
19964         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19965         this.update();
19966     },
19967
19968     fireKey: function(e)
19969     {
19970         if (!this.picker().isVisible()){
19971             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19972                 this.show();
19973             }
19974             return;
19975         }
19976         
19977         var dir;
19978         
19979         switch(e.keyCode){
19980             case 27: // escape
19981                 this.hide();
19982                 e.preventDefault();
19983                 break;
19984             case 37: // left
19985             case 39: // right
19986                 dir = e.keyCode == 37 ? -1 : 1;
19987                 
19988                 this.vIndex = this.vIndex + dir;
19989                 
19990                 if(this.vIndex < 0){
19991                     this.vIndex = 0;
19992                 }
19993                 
19994                 if(this.vIndex > 11){
19995                     this.vIndex = 11;
19996                 }
19997                 
19998                 if(isNaN(this.vIndex)){
19999                     this.vIndex = 0;
20000                 }
20001                 
20002                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20003                 
20004                 break;
20005             case 38: // up
20006             case 40: // down
20007                 
20008                 dir = e.keyCode == 38 ? -1 : 1;
20009                 
20010                 this.vIndex = this.vIndex + dir * 4;
20011                 
20012                 if(this.vIndex < 0){
20013                     this.vIndex = 0;
20014                 }
20015                 
20016                 if(this.vIndex > 11){
20017                     this.vIndex = 11;
20018                 }
20019                 
20020                 if(isNaN(this.vIndex)){
20021                     this.vIndex = 0;
20022                 }
20023                 
20024                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20025                 break;
20026                 
20027             case 13: // enter
20028                 
20029                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20030                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20031                 }
20032                 
20033                 this.hide();
20034                 e.preventDefault();
20035                 break;
20036             case 9: // tab
20037                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20038                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20039                 }
20040                 this.hide();
20041                 break;
20042             case 16: // shift
20043             case 17: // ctrl
20044             case 18: // alt
20045                 break;
20046             default :
20047                 this.hide();
20048                 
20049         }
20050     },
20051     
20052     remove: function() 
20053     {
20054         this.picker().remove();
20055     }
20056    
20057 });
20058
20059 Roo.apply(Roo.bootstrap.MonthField,  {
20060     
20061     content : {
20062         tag: 'tbody',
20063         cn: [
20064         {
20065             tag: 'tr',
20066             cn: [
20067             {
20068                 tag: 'td',
20069                 colspan: '7'
20070             }
20071             ]
20072         }
20073         ]
20074     },
20075     
20076     dates:{
20077         en: {
20078             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20079             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20080         }
20081     }
20082 });
20083
20084 Roo.apply(Roo.bootstrap.MonthField,  {
20085   
20086     template : {
20087         tag: 'div',
20088         cls: 'datepicker dropdown-menu roo-dynamic',
20089         cn: [
20090             {
20091                 tag: 'div',
20092                 cls: 'datepicker-months',
20093                 cn: [
20094                 {
20095                     tag: 'table',
20096                     cls: 'table-condensed',
20097                     cn:[
20098                         Roo.bootstrap.DateField.content
20099                     ]
20100                 }
20101                 ]
20102             }
20103         ]
20104     }
20105 });
20106
20107  
20108
20109  
20110  /*
20111  * - LGPL
20112  *
20113  * CheckBox
20114  * 
20115  */
20116
20117 /**
20118  * @class Roo.bootstrap.CheckBox
20119  * @extends Roo.bootstrap.Input
20120  * Bootstrap CheckBox class
20121  * 
20122  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20123  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20124  * @cfg {String} boxLabel The text that appears beside the checkbox
20125  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20126  * @cfg {Boolean} checked initnal the element
20127  * @cfg {Boolean} inline inline the element (default false)
20128  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20129  * @cfg {String} tooltip label tooltip
20130  * 
20131  * @constructor
20132  * Create a new CheckBox
20133  * @param {Object} config The config object
20134  */
20135
20136 Roo.bootstrap.CheckBox = function(config){
20137     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20138    
20139     this.addEvents({
20140         /**
20141         * @event check
20142         * Fires when the element is checked or unchecked.
20143         * @param {Roo.bootstrap.CheckBox} this This input
20144         * @param {Boolean} checked The new checked value
20145         */
20146        check : true,
20147        /**
20148         * @event click
20149         * Fires when the element is click.
20150         * @param {Roo.bootstrap.CheckBox} this This input
20151         */
20152        click : true
20153     });
20154     
20155 };
20156
20157 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20158   
20159     inputType: 'checkbox',
20160     inputValue: 1,
20161     valueOff: 0,
20162     boxLabel: false,
20163     checked: false,
20164     weight : false,
20165     inline: false,
20166     tooltip : '',
20167     
20168     getAutoCreate : function()
20169     {
20170         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20171         
20172         var id = Roo.id();
20173         
20174         var cfg = {};
20175         
20176         cfg.cls = 'form-group ' + this.inputType; //input-group
20177         
20178         if(this.inline){
20179             cfg.cls += ' ' + this.inputType + '-inline';
20180         }
20181         
20182         var input =  {
20183             tag: 'input',
20184             id : id,
20185             type : this.inputType,
20186             value : this.inputValue,
20187             cls : 'roo-' + this.inputType, //'form-box',
20188             placeholder : this.placeholder || ''
20189             
20190         };
20191         
20192         if(this.inputType != 'radio'){
20193             var hidden =  {
20194                 tag: 'input',
20195                 type : 'hidden',
20196                 cls : 'roo-hidden-value',
20197                 value : this.checked ? this.inputValue : this.valueOff
20198             };
20199         }
20200         
20201             
20202         if (this.weight) { // Validity check?
20203             cfg.cls += " " + this.inputType + "-" + this.weight;
20204         }
20205         
20206         if (this.disabled) {
20207             input.disabled=true;
20208         }
20209         
20210         if(this.checked){
20211             input.checked = this.checked;
20212         }
20213         
20214         if (this.name) {
20215             
20216             input.name = this.name;
20217             
20218             if(this.inputType != 'radio'){
20219                 hidden.name = this.name;
20220                 input.name = '_hidden_' + this.name;
20221             }
20222         }
20223         
20224         if (this.size) {
20225             input.cls += ' input-' + this.size;
20226         }
20227         
20228         var settings=this;
20229         
20230         ['xs','sm','md','lg'].map(function(size){
20231             if (settings[size]) {
20232                 cfg.cls += ' col-' + size + '-' + settings[size];
20233             }
20234         });
20235         
20236         var inputblock = input;
20237          
20238         if (this.before || this.after) {
20239             
20240             inputblock = {
20241                 cls : 'input-group',
20242                 cn :  [] 
20243             };
20244             
20245             if (this.before) {
20246                 inputblock.cn.push({
20247                     tag :'span',
20248                     cls : 'input-group-addon',
20249                     html : this.before
20250                 });
20251             }
20252             
20253             inputblock.cn.push(input);
20254             
20255             if(this.inputType != 'radio'){
20256                 inputblock.cn.push(hidden);
20257             }
20258             
20259             if (this.after) {
20260                 inputblock.cn.push({
20261                     tag :'span',
20262                     cls : 'input-group-addon',
20263                     html : this.after
20264                 });
20265             }
20266             
20267         }
20268         
20269         if (align ==='left' && this.fieldLabel.length) {
20270 //                Roo.log("left and has label");
20271             cfg.cn = [
20272                 {
20273                     tag: 'span',
20274                     'for' :  id,
20275                     cls : 'control-label',
20276                     html : this.fieldLabel
20277                 },
20278                 {
20279                     cls : "", 
20280                     cn: [
20281                         inputblock
20282                     ]
20283                 }
20284             ];
20285             
20286             if(this.labelWidth > 12){
20287                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20288             }
20289             
20290             if(this.labelWidth < 13 && this.labelmd == 0){
20291                 this.labelmd = this.labelWidth;
20292             }
20293             
20294             if(this.labellg > 0){
20295                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20296                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20297             }
20298             
20299             if(this.labelmd > 0){
20300                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20301                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20302             }
20303             
20304             if(this.labelsm > 0){
20305                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20306                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20307             }
20308             
20309             if(this.labelxs > 0){
20310                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20311                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20312             }
20313             
20314         } else if ( this.fieldLabel.length) {
20315 //                Roo.log(" label");
20316                 cfg.cn = [
20317                    
20318                     {
20319                         tag: this.boxLabel ? 'span' : 'label',
20320                         'for': id,
20321                         cls: 'control-label box-input-label',
20322                         //cls : 'input-group-addon',
20323                         html : this.fieldLabel
20324                     },
20325                     
20326                     inputblock
20327                     
20328                 ];
20329
20330         } else {
20331             
20332 //                Roo.log(" no label && no align");
20333                 cfg.cn = [  inputblock ] ;
20334                 
20335                 
20336         }
20337         
20338         if(this.boxLabel){
20339              var boxLabelCfg = {
20340                 tag: 'label',
20341                 //'for': id, // box label is handled by onclick - so no for...
20342                 cls: 'box-label',
20343                 html: this.boxLabel
20344             };
20345             
20346             if(this.tooltip){
20347                 boxLabelCfg.tooltip = this.tooltip;
20348             }
20349              
20350             cfg.cn.push(boxLabelCfg);
20351         }
20352         
20353         if(this.inputType != 'radio'){
20354             cfg.cn.push(hidden);
20355         }
20356         
20357         return cfg;
20358         
20359     },
20360     
20361     /**
20362      * return the real input element.
20363      */
20364     inputEl: function ()
20365     {
20366         return this.el.select('input.roo-' + this.inputType,true).first();
20367     },
20368     hiddenEl: function ()
20369     {
20370         return this.el.select('input.roo-hidden-value',true).first();
20371     },
20372     
20373     labelEl: function()
20374     {
20375         return this.el.select('label.control-label',true).first();
20376     },
20377     /* depricated... */
20378     
20379     label: function()
20380     {
20381         return this.labelEl();
20382     },
20383     
20384     boxLabelEl: function()
20385     {
20386         return this.el.select('label.box-label',true).first();
20387     },
20388     
20389     initEvents : function()
20390     {
20391 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20392         
20393         this.inputEl().on('click', this.onClick,  this);
20394         
20395         if (this.boxLabel) { 
20396             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20397         }
20398         
20399         this.startValue = this.getValue();
20400         
20401         if(this.groupId){
20402             Roo.bootstrap.CheckBox.register(this);
20403         }
20404     },
20405     
20406     onClick : function(e)
20407     {   
20408         if(this.fireEvent('click', this, e) !== false){
20409             this.setChecked(!this.checked);
20410         }
20411         
20412     },
20413     
20414     setChecked : function(state,suppressEvent)
20415     {
20416         this.startValue = this.getValue();
20417
20418         if(this.inputType == 'radio'){
20419             
20420             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20421                 e.dom.checked = false;
20422             });
20423             
20424             this.inputEl().dom.checked = true;
20425             
20426             this.inputEl().dom.value = this.inputValue;
20427             
20428             if(suppressEvent !== true){
20429                 this.fireEvent('check', this, true);
20430             }
20431             
20432             this.validate();
20433             
20434             return;
20435         }
20436         
20437         this.checked = state;
20438         
20439         this.inputEl().dom.checked = state;
20440         
20441         
20442         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20443         
20444         if(suppressEvent !== true){
20445             this.fireEvent('check', this, state);
20446         }
20447         
20448         this.validate();
20449     },
20450     
20451     getValue : function()
20452     {
20453         if(this.inputType == 'radio'){
20454             return this.getGroupValue();
20455         }
20456         
20457         return this.hiddenEl().dom.value;
20458         
20459     },
20460     
20461     getGroupValue : function()
20462     {
20463         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20464             return '';
20465         }
20466         
20467         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20468     },
20469     
20470     setValue : function(v,suppressEvent)
20471     {
20472         if(this.inputType == 'radio'){
20473             this.setGroupValue(v, suppressEvent);
20474             return;
20475         }
20476         
20477         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20478         
20479         this.validate();
20480     },
20481     
20482     setGroupValue : function(v, suppressEvent)
20483     {
20484         this.startValue = this.getValue();
20485         
20486         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20487             e.dom.checked = false;
20488             
20489             if(e.dom.value == v){
20490                 e.dom.checked = true;
20491             }
20492         });
20493         
20494         if(suppressEvent !== true){
20495             this.fireEvent('check', this, true);
20496         }
20497
20498         this.validate();
20499         
20500         return;
20501     },
20502     
20503     validate : function()
20504     {
20505         if(
20506                 this.disabled || 
20507                 (this.inputType == 'radio' && this.validateRadio()) ||
20508                 (this.inputType == 'checkbox' && this.validateCheckbox())
20509         ){
20510             this.markValid();
20511             return true;
20512         }
20513         
20514         this.markInvalid();
20515         return false;
20516     },
20517     
20518     validateRadio : function()
20519     {
20520         if(this.allowBlank){
20521             return true;
20522         }
20523         
20524         var valid = false;
20525         
20526         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20527             if(!e.dom.checked){
20528                 return;
20529             }
20530             
20531             valid = true;
20532             
20533             return false;
20534         });
20535         
20536         return valid;
20537     },
20538     
20539     validateCheckbox : function()
20540     {
20541         if(!this.groupId){
20542             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20543             //return (this.getValue() == this.inputValue) ? true : false;
20544         }
20545         
20546         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20547         
20548         if(!group){
20549             return false;
20550         }
20551         
20552         var r = false;
20553         
20554         for(var i in group){
20555             if(r){
20556                 break;
20557             }
20558             
20559             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20560         }
20561         
20562         return r;
20563     },
20564     
20565     /**
20566      * Mark this field as valid
20567      */
20568     markValid : function()
20569     {
20570         var _this = this;
20571         
20572         this.fireEvent('valid', this);
20573         
20574         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20575         
20576         if(this.groupId){
20577             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20578         }
20579         
20580         if(label){
20581             label.markValid();
20582         }
20583
20584         if(this.inputType == 'radio'){
20585             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20586                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20587                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20588             });
20589             
20590             return;
20591         }
20592
20593         if(!this.groupId){
20594             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20595             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20596             return;
20597         }
20598         
20599         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20600         
20601         if(!group){
20602             return;
20603         }
20604         
20605         for(var i in group){
20606             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20607             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20608         }
20609     },
20610     
20611      /**
20612      * Mark this field as invalid
20613      * @param {String} msg The validation message
20614      */
20615     markInvalid : function(msg)
20616     {
20617         if(this.allowBlank){
20618             return;
20619         }
20620         
20621         var _this = this;
20622         
20623         this.fireEvent('invalid', this, msg);
20624         
20625         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20626         
20627         if(this.groupId){
20628             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20629         }
20630         
20631         if(label){
20632             label.markInvalid();
20633         }
20634             
20635         if(this.inputType == 'radio'){
20636             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20637                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20638                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20639             });
20640             
20641             return;
20642         }
20643         
20644         if(!this.groupId){
20645             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20646             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20647             return;
20648         }
20649         
20650         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20651         
20652         if(!group){
20653             return;
20654         }
20655         
20656         for(var i in group){
20657             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20658             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20659         }
20660         
20661     },
20662     
20663     clearInvalid : function()
20664     {
20665         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20666         
20667         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20668         
20669         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20670         
20671         if (label) {
20672             label.iconEl.removeClass(label.validClass);
20673             label.iconEl.removeClass(label.invalidClass);
20674         }
20675     },
20676     
20677     disable : function()
20678     {
20679         if(this.inputType != 'radio'){
20680             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20681             return;
20682         }
20683         
20684         var _this = this;
20685         
20686         if(this.rendered){
20687             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20688                 _this.getActionEl().addClass(this.disabledClass);
20689                 e.dom.disabled = true;
20690             });
20691         }
20692         
20693         this.disabled = true;
20694         this.fireEvent("disable", this);
20695         return this;
20696     },
20697
20698     enable : function()
20699     {
20700         if(this.inputType != 'radio'){
20701             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20702             return;
20703         }
20704         
20705         var _this = this;
20706         
20707         if(this.rendered){
20708             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20709                 _this.getActionEl().removeClass(this.disabledClass);
20710                 e.dom.disabled = false;
20711             });
20712         }
20713         
20714         this.disabled = false;
20715         this.fireEvent("enable", this);
20716         return this;
20717     }
20718
20719 });
20720
20721 Roo.apply(Roo.bootstrap.CheckBox, {
20722     
20723     groups: {},
20724     
20725      /**
20726     * register a CheckBox Group
20727     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20728     */
20729     register : function(checkbox)
20730     {
20731         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20732             this.groups[checkbox.groupId] = {};
20733         }
20734         
20735         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20736             return;
20737         }
20738         
20739         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20740         
20741     },
20742     /**
20743     * fetch a CheckBox Group based on the group ID
20744     * @param {string} the group ID
20745     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20746     */
20747     get: function(groupId) {
20748         if (typeof(this.groups[groupId]) == 'undefined') {
20749             return false;
20750         }
20751         
20752         return this.groups[groupId] ;
20753     }
20754     
20755     
20756 });
20757 /*
20758  * - LGPL
20759  *
20760  * RadioItem
20761  * 
20762  */
20763
20764 /**
20765  * @class Roo.bootstrap.Radio
20766  * @extends Roo.bootstrap.Component
20767  * Bootstrap Radio class
20768  * @cfg {String} boxLabel - the label associated
20769  * @cfg {String} value - the value of radio
20770  * 
20771  * @constructor
20772  * Create a new Radio
20773  * @param {Object} config The config object
20774  */
20775 Roo.bootstrap.Radio = function(config){
20776     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20777     
20778 };
20779
20780 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20781     
20782     boxLabel : '',
20783     
20784     value : '',
20785     
20786     getAutoCreate : function()
20787     {
20788         var cfg = {
20789             tag : 'div',
20790             cls : 'form-group radio',
20791             cn : [
20792                 {
20793                     tag : 'label',
20794                     cls : 'box-label',
20795                     html : this.boxLabel
20796                 }
20797             ]
20798         };
20799         
20800         return cfg;
20801     },
20802     
20803     initEvents : function() 
20804     {
20805         this.parent().register(this);
20806         
20807         this.el.on('click', this.onClick, this);
20808         
20809     },
20810     
20811     onClick : function()
20812     {
20813         this.setChecked(true);
20814     },
20815     
20816     setChecked : function(state, suppressEvent)
20817     {
20818         this.parent().setValue(this.value, suppressEvent);
20819         
20820     },
20821     
20822     setBoxLabel : function(v)
20823     {
20824         this.boxLabel = v;
20825         
20826         if(this.rendered){
20827             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20828         }
20829     }
20830     
20831 });
20832  
20833
20834  /*
20835  * - LGPL
20836  *
20837  * Input
20838  * 
20839  */
20840
20841 /**
20842  * @class Roo.bootstrap.SecurePass
20843  * @extends Roo.bootstrap.Input
20844  * Bootstrap SecurePass class
20845  *
20846  * 
20847  * @constructor
20848  * Create a new SecurePass
20849  * @param {Object} config The config object
20850  */
20851  
20852 Roo.bootstrap.SecurePass = function (config) {
20853     // these go here, so the translation tool can replace them..
20854     this.errors = {
20855         PwdEmpty: "Please type a password, and then retype it to confirm.",
20856         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20857         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20858         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20859         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20860         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20861         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20862         TooWeak: "Your password is Too Weak."
20863     },
20864     this.meterLabel = "Password strength:";
20865     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20866     this.meterClass = [
20867         "roo-password-meter-tooweak", 
20868         "roo-password-meter-weak", 
20869         "roo-password-meter-medium", 
20870         "roo-password-meter-strong", 
20871         "roo-password-meter-grey"
20872     ];
20873     
20874     this.errors = {};
20875     
20876     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20877 }
20878
20879 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20880     /**
20881      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20882      * {
20883      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20884      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20885      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20886      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20887      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20888      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20889      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20890      * })
20891      */
20892     // private
20893     
20894     meterWidth: 300,
20895     errorMsg :'',    
20896     errors: false,
20897     imageRoot: '/',
20898     /**
20899      * @cfg {String/Object} Label for the strength meter (defaults to
20900      * 'Password strength:')
20901      */
20902     // private
20903     meterLabel: '',
20904     /**
20905      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20906      * ['Weak', 'Medium', 'Strong'])
20907      */
20908     // private    
20909     pwdStrengths: false,    
20910     // private
20911     strength: 0,
20912     // private
20913     _lastPwd: null,
20914     // private
20915     kCapitalLetter: 0,
20916     kSmallLetter: 1,
20917     kDigit: 2,
20918     kPunctuation: 3,
20919     
20920     insecure: false,
20921     // private
20922     initEvents: function ()
20923     {
20924         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20925
20926         if (this.el.is('input[type=password]') && Roo.isSafari) {
20927             this.el.on('keydown', this.SafariOnKeyDown, this);
20928         }
20929
20930         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20931     },
20932     // private
20933     onRender: function (ct, position)
20934     {
20935         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20936         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20937         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20938
20939         this.trigger.createChild({
20940                    cn: [
20941                     {
20942                     //id: 'PwdMeter',
20943                     tag: 'div',
20944                     cls: 'roo-password-meter-grey col-xs-12',
20945                     style: {
20946                         //width: 0,
20947                         //width: this.meterWidth + 'px'                                                
20948                         }
20949                     },
20950                     {                            
20951                          cls: 'roo-password-meter-text'                          
20952                     }
20953                 ]            
20954         });
20955
20956          
20957         if (this.hideTrigger) {
20958             this.trigger.setDisplayed(false);
20959         }
20960         this.setSize(this.width || '', this.height || '');
20961     },
20962     // private
20963     onDestroy: function ()
20964     {
20965         if (this.trigger) {
20966             this.trigger.removeAllListeners();
20967             this.trigger.remove();
20968         }
20969         if (this.wrap) {
20970             this.wrap.remove();
20971         }
20972         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20973     },
20974     // private
20975     checkStrength: function ()
20976     {
20977         var pwd = this.inputEl().getValue();
20978         if (pwd == this._lastPwd) {
20979             return;
20980         }
20981
20982         var strength;
20983         if (this.ClientSideStrongPassword(pwd)) {
20984             strength = 3;
20985         } else if (this.ClientSideMediumPassword(pwd)) {
20986             strength = 2;
20987         } else if (this.ClientSideWeakPassword(pwd)) {
20988             strength = 1;
20989         } else {
20990             strength = 0;
20991         }
20992         
20993         Roo.log('strength1: ' + strength);
20994         
20995         //var pm = this.trigger.child('div/div/div').dom;
20996         var pm = this.trigger.child('div/div');
20997         pm.removeClass(this.meterClass);
20998         pm.addClass(this.meterClass[strength]);
20999                 
21000         
21001         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21002                 
21003         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21004         
21005         this._lastPwd = pwd;
21006     },
21007     reset: function ()
21008     {
21009         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21010         
21011         this._lastPwd = '';
21012         
21013         var pm = this.trigger.child('div/div');
21014         pm.removeClass(this.meterClass);
21015         pm.addClass('roo-password-meter-grey');        
21016         
21017         
21018         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21019         
21020         pt.innerHTML = '';
21021         this.inputEl().dom.type='password';
21022     },
21023     // private
21024     validateValue: function (value)
21025     {
21026         
21027         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21028             return false;
21029         }
21030         if (value.length == 0) {
21031             if (this.allowBlank) {
21032                 this.clearInvalid();
21033                 return true;
21034             }
21035
21036             this.markInvalid(this.errors.PwdEmpty);
21037             this.errorMsg = this.errors.PwdEmpty;
21038             return false;
21039         }
21040         
21041         if(this.insecure){
21042             return true;
21043         }
21044         
21045         if ('[\x21-\x7e]*'.match(value)) {
21046             this.markInvalid(this.errors.PwdBadChar);
21047             this.errorMsg = this.errors.PwdBadChar;
21048             return false;
21049         }
21050         if (value.length < 6) {
21051             this.markInvalid(this.errors.PwdShort);
21052             this.errorMsg = this.errors.PwdShort;
21053             return false;
21054         }
21055         if (value.length > 16) {
21056             this.markInvalid(this.errors.PwdLong);
21057             this.errorMsg = this.errors.PwdLong;
21058             return false;
21059         }
21060         var strength;
21061         if (this.ClientSideStrongPassword(value)) {
21062             strength = 3;
21063         } else if (this.ClientSideMediumPassword(value)) {
21064             strength = 2;
21065         } else if (this.ClientSideWeakPassword(value)) {
21066             strength = 1;
21067         } else {
21068             strength = 0;
21069         }
21070
21071         
21072         if (strength < 2) {
21073             //this.markInvalid(this.errors.TooWeak);
21074             this.errorMsg = this.errors.TooWeak;
21075             //return false;
21076         }
21077         
21078         
21079         console.log('strength2: ' + strength);
21080         
21081         //var pm = this.trigger.child('div/div/div').dom;
21082         
21083         var pm = this.trigger.child('div/div');
21084         pm.removeClass(this.meterClass);
21085         pm.addClass(this.meterClass[strength]);
21086                 
21087         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21088                 
21089         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21090         
21091         this.errorMsg = ''; 
21092         return true;
21093     },
21094     // private
21095     CharacterSetChecks: function (type)
21096     {
21097         this.type = type;
21098         this.fResult = false;
21099     },
21100     // private
21101     isctype: function (character, type)
21102     {
21103         switch (type) {  
21104             case this.kCapitalLetter:
21105                 if (character >= 'A' && character <= 'Z') {
21106                     return true;
21107                 }
21108                 break;
21109             
21110             case this.kSmallLetter:
21111                 if (character >= 'a' && character <= 'z') {
21112                     return true;
21113                 }
21114                 break;
21115             
21116             case this.kDigit:
21117                 if (character >= '0' && character <= '9') {
21118                     return true;
21119                 }
21120                 break;
21121             
21122             case this.kPunctuation:
21123                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21124                     return true;
21125                 }
21126                 break;
21127             
21128             default:
21129                 return false;
21130         }
21131
21132     },
21133     // private
21134     IsLongEnough: function (pwd, size)
21135     {
21136         return !(pwd == null || isNaN(size) || pwd.length < size);
21137     },
21138     // private
21139     SpansEnoughCharacterSets: function (word, nb)
21140     {
21141         if (!this.IsLongEnough(word, nb))
21142         {
21143             return false;
21144         }
21145
21146         var characterSetChecks = new Array(
21147             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21148             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21149         );
21150         
21151         for (var index = 0; index < word.length; ++index) {
21152             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21153                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21154                     characterSetChecks[nCharSet].fResult = true;
21155                     break;
21156                 }
21157             }
21158         }
21159
21160         var nCharSets = 0;
21161         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21162             if (characterSetChecks[nCharSet].fResult) {
21163                 ++nCharSets;
21164             }
21165         }
21166
21167         if (nCharSets < nb) {
21168             return false;
21169         }
21170         return true;
21171     },
21172     // private
21173     ClientSideStrongPassword: function (pwd)
21174     {
21175         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21176     },
21177     // private
21178     ClientSideMediumPassword: function (pwd)
21179     {
21180         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21181     },
21182     // private
21183     ClientSideWeakPassword: function (pwd)
21184     {
21185         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21186     }
21187           
21188 })//<script type="text/javascript">
21189
21190 /*
21191  * Based  Ext JS Library 1.1.1
21192  * Copyright(c) 2006-2007, Ext JS, LLC.
21193  * LGPL
21194  *
21195  */
21196  
21197 /**
21198  * @class Roo.HtmlEditorCore
21199  * @extends Roo.Component
21200  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21201  *
21202  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21203  */
21204
21205 Roo.HtmlEditorCore = function(config){
21206     
21207     
21208     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21209     
21210     
21211     this.addEvents({
21212         /**
21213          * @event initialize
21214          * Fires when the editor is fully initialized (including the iframe)
21215          * @param {Roo.HtmlEditorCore} this
21216          */
21217         initialize: true,
21218         /**
21219          * @event activate
21220          * Fires when the editor is first receives the focus. Any insertion must wait
21221          * until after this event.
21222          * @param {Roo.HtmlEditorCore} this
21223          */
21224         activate: true,
21225          /**
21226          * @event beforesync
21227          * Fires before the textarea is updated with content from the editor iframe. Return false
21228          * to cancel the sync.
21229          * @param {Roo.HtmlEditorCore} this
21230          * @param {String} html
21231          */
21232         beforesync: true,
21233          /**
21234          * @event beforepush
21235          * Fires before the iframe editor is updated with content from the textarea. Return false
21236          * to cancel the push.
21237          * @param {Roo.HtmlEditorCore} this
21238          * @param {String} html
21239          */
21240         beforepush: true,
21241          /**
21242          * @event sync
21243          * Fires when the textarea is updated with content from the editor iframe.
21244          * @param {Roo.HtmlEditorCore} this
21245          * @param {String} html
21246          */
21247         sync: true,
21248          /**
21249          * @event push
21250          * Fires when the iframe editor is updated with content from the textarea.
21251          * @param {Roo.HtmlEditorCore} this
21252          * @param {String} html
21253          */
21254         push: true,
21255         
21256         /**
21257          * @event editorevent
21258          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21259          * @param {Roo.HtmlEditorCore} this
21260          */
21261         editorevent: true
21262         
21263     });
21264     
21265     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21266     
21267     // defaults : white / black...
21268     this.applyBlacklists();
21269     
21270     
21271     
21272 };
21273
21274
21275 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21276
21277
21278      /**
21279      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21280      */
21281     
21282     owner : false,
21283     
21284      /**
21285      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21286      *                        Roo.resizable.
21287      */
21288     resizable : false,
21289      /**
21290      * @cfg {Number} height (in pixels)
21291      */   
21292     height: 300,
21293    /**
21294      * @cfg {Number} width (in pixels)
21295      */   
21296     width: 500,
21297     
21298     /**
21299      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21300      * 
21301      */
21302     stylesheets: false,
21303     
21304     // id of frame..
21305     frameId: false,
21306     
21307     // private properties
21308     validationEvent : false,
21309     deferHeight: true,
21310     initialized : false,
21311     activated : false,
21312     sourceEditMode : false,
21313     onFocus : Roo.emptyFn,
21314     iframePad:3,
21315     hideMode:'offsets',
21316     
21317     clearUp: true,
21318     
21319     // blacklist + whitelisted elements..
21320     black: false,
21321     white: false,
21322      
21323     bodyCls : '',
21324
21325     /**
21326      * Protected method that will not generally be called directly. It
21327      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21328      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21329      */
21330     getDocMarkup : function(){
21331         // body styles..
21332         var st = '';
21333         
21334         // inherit styels from page...?? 
21335         if (this.stylesheets === false) {
21336             
21337             Roo.get(document.head).select('style').each(function(node) {
21338                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21339             });
21340             
21341             Roo.get(document.head).select('link').each(function(node) { 
21342                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21343             });
21344             
21345         } else if (!this.stylesheets.length) {
21346                 // simple..
21347                 st = '<style type="text/css">' +
21348                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21349                    '</style>';
21350         } else { 
21351             st = '<style type="text/css">' +
21352                     this.stylesheets +
21353                 '</style>';
21354         }
21355         
21356         st +=  '<style type="text/css">' +
21357             'IMG { cursor: pointer } ' +
21358         '</style>';
21359
21360         var cls = 'roo-htmleditor-body';
21361         
21362         if(this.bodyCls.length){
21363             cls += ' ' + this.bodyCls;
21364         }
21365         
21366         return '<html><head>' + st  +
21367             //<style type="text/css">' +
21368             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21369             //'</style>' +
21370             ' </head><body class="' +  cls + '"></body></html>';
21371     },
21372
21373     // private
21374     onRender : function(ct, position)
21375     {
21376         var _t = this;
21377         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21378         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21379         
21380         
21381         this.el.dom.style.border = '0 none';
21382         this.el.dom.setAttribute('tabIndex', -1);
21383         this.el.addClass('x-hidden hide');
21384         
21385         
21386         
21387         if(Roo.isIE){ // fix IE 1px bogus margin
21388             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21389         }
21390        
21391         
21392         this.frameId = Roo.id();
21393         
21394          
21395         
21396         var iframe = this.owner.wrap.createChild({
21397             tag: 'iframe',
21398             cls: 'form-control', // bootstrap..
21399             id: this.frameId,
21400             name: this.frameId,
21401             frameBorder : 'no',
21402             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21403         }, this.el
21404         );
21405         
21406         
21407         this.iframe = iframe.dom;
21408
21409          this.assignDocWin();
21410         
21411         this.doc.designMode = 'on';
21412        
21413         this.doc.open();
21414         this.doc.write(this.getDocMarkup());
21415         this.doc.close();
21416
21417         
21418         var task = { // must defer to wait for browser to be ready
21419             run : function(){
21420                 //console.log("run task?" + this.doc.readyState);
21421                 this.assignDocWin();
21422                 if(this.doc.body || this.doc.readyState == 'complete'){
21423                     try {
21424                         this.doc.designMode="on";
21425                     } catch (e) {
21426                         return;
21427                     }
21428                     Roo.TaskMgr.stop(task);
21429                     this.initEditor.defer(10, this);
21430                 }
21431             },
21432             interval : 10,
21433             duration: 10000,
21434             scope: this
21435         };
21436         Roo.TaskMgr.start(task);
21437
21438     },
21439
21440     // private
21441     onResize : function(w, h)
21442     {
21443          Roo.log('resize: ' +w + ',' + h );
21444         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21445         if(!this.iframe){
21446             return;
21447         }
21448         if(typeof w == 'number'){
21449             
21450             this.iframe.style.width = w + 'px';
21451         }
21452         if(typeof h == 'number'){
21453             
21454             this.iframe.style.height = h + 'px';
21455             if(this.doc){
21456                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21457             }
21458         }
21459         
21460     },
21461
21462     /**
21463      * Toggles the editor between standard and source edit mode.
21464      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21465      */
21466     toggleSourceEdit : function(sourceEditMode){
21467         
21468         this.sourceEditMode = sourceEditMode === true;
21469         
21470         if(this.sourceEditMode){
21471  
21472             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21473             
21474         }else{
21475             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21476             //this.iframe.className = '';
21477             this.deferFocus();
21478         }
21479         //this.setSize(this.owner.wrap.getSize());
21480         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21481     },
21482
21483     
21484   
21485
21486     /**
21487      * Protected method that will not generally be called directly. If you need/want
21488      * custom HTML cleanup, this is the method you should override.
21489      * @param {String} html The HTML to be cleaned
21490      * return {String} The cleaned HTML
21491      */
21492     cleanHtml : function(html){
21493         html = String(html);
21494         if(html.length > 5){
21495             if(Roo.isSafari){ // strip safari nonsense
21496                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21497             }
21498         }
21499         if(html == '&nbsp;'){
21500             html = '';
21501         }
21502         return html;
21503     },
21504
21505     /**
21506      * HTML Editor -> Textarea
21507      * Protected method that will not generally be called directly. Syncs the contents
21508      * of the editor iframe with the textarea.
21509      */
21510     syncValue : function(){
21511         if(this.initialized){
21512             var bd = (this.doc.body || this.doc.documentElement);
21513             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21514             var html = bd.innerHTML;
21515             if(Roo.isSafari){
21516                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21517                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21518                 if(m && m[1]){
21519                     html = '<div style="'+m[0]+'">' + html + '</div>';
21520                 }
21521             }
21522             html = this.cleanHtml(html);
21523             // fix up the special chars.. normaly like back quotes in word...
21524             // however we do not want to do this with chinese..
21525             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21526                 var cc = b.charCodeAt();
21527                 if (
21528                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21529                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21530                     (cc >= 0xf900 && cc < 0xfb00 )
21531                 ) {
21532                         return b;
21533                 }
21534                 return "&#"+cc+";" 
21535             });
21536             if(this.owner.fireEvent('beforesync', this, html) !== false){
21537                 this.el.dom.value = html;
21538                 this.owner.fireEvent('sync', this, html);
21539             }
21540         }
21541     },
21542
21543     /**
21544      * Protected method that will not generally be called directly. Pushes the value of the textarea
21545      * into the iframe editor.
21546      */
21547     pushValue : function(){
21548         if(this.initialized){
21549             var v = this.el.dom.value.trim();
21550             
21551 //            if(v.length < 1){
21552 //                v = '&#160;';
21553 //            }
21554             
21555             if(this.owner.fireEvent('beforepush', this, v) !== false){
21556                 var d = (this.doc.body || this.doc.documentElement);
21557                 d.innerHTML = v;
21558                 this.cleanUpPaste();
21559                 this.el.dom.value = d.innerHTML;
21560                 this.owner.fireEvent('push', this, v);
21561             }
21562         }
21563     },
21564
21565     // private
21566     deferFocus : function(){
21567         this.focus.defer(10, this);
21568     },
21569
21570     // doc'ed in Field
21571     focus : function(){
21572         if(this.win && !this.sourceEditMode){
21573             this.win.focus();
21574         }else{
21575             this.el.focus();
21576         }
21577     },
21578     
21579     assignDocWin: function()
21580     {
21581         var iframe = this.iframe;
21582         
21583          if(Roo.isIE){
21584             this.doc = iframe.contentWindow.document;
21585             this.win = iframe.contentWindow;
21586         } else {
21587 //            if (!Roo.get(this.frameId)) {
21588 //                return;
21589 //            }
21590 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21591 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21592             
21593             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21594                 return;
21595             }
21596             
21597             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21598             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21599         }
21600     },
21601     
21602     // private
21603     initEditor : function(){
21604         //console.log("INIT EDITOR");
21605         this.assignDocWin();
21606         
21607         
21608         
21609         this.doc.designMode="on";
21610         this.doc.open();
21611         this.doc.write(this.getDocMarkup());
21612         this.doc.close();
21613         
21614         var dbody = (this.doc.body || this.doc.documentElement);
21615         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21616         // this copies styles from the containing element into thsi one..
21617         // not sure why we need all of this..
21618         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21619         
21620         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21621         //ss['background-attachment'] = 'fixed'; // w3c
21622         dbody.bgProperties = 'fixed'; // ie
21623         //Roo.DomHelper.applyStyles(dbody, ss);
21624         Roo.EventManager.on(this.doc, {
21625             //'mousedown': this.onEditorEvent,
21626             'mouseup': this.onEditorEvent,
21627             'dblclick': this.onEditorEvent,
21628             'click': this.onEditorEvent,
21629             'keyup': this.onEditorEvent,
21630             buffer:100,
21631             scope: this
21632         });
21633         if(Roo.isGecko){
21634             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21635         }
21636         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21637             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21638         }
21639         this.initialized = true;
21640
21641         this.owner.fireEvent('initialize', this);
21642         this.pushValue();
21643     },
21644
21645     // private
21646     onDestroy : function(){
21647         
21648         
21649         
21650         if(this.rendered){
21651             
21652             //for (var i =0; i < this.toolbars.length;i++) {
21653             //    // fixme - ask toolbars for heights?
21654             //    this.toolbars[i].onDestroy();
21655            // }
21656             
21657             //this.wrap.dom.innerHTML = '';
21658             //this.wrap.remove();
21659         }
21660     },
21661
21662     // private
21663     onFirstFocus : function(){
21664         
21665         this.assignDocWin();
21666         
21667         
21668         this.activated = true;
21669          
21670     
21671         if(Roo.isGecko){ // prevent silly gecko errors
21672             this.win.focus();
21673             var s = this.win.getSelection();
21674             if(!s.focusNode || s.focusNode.nodeType != 3){
21675                 var r = s.getRangeAt(0);
21676                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21677                 r.collapse(true);
21678                 this.deferFocus();
21679             }
21680             try{
21681                 this.execCmd('useCSS', true);
21682                 this.execCmd('styleWithCSS', false);
21683             }catch(e){}
21684         }
21685         this.owner.fireEvent('activate', this);
21686     },
21687
21688     // private
21689     adjustFont: function(btn){
21690         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21691         //if(Roo.isSafari){ // safari
21692         //    adjust *= 2;
21693        // }
21694         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21695         if(Roo.isSafari){ // safari
21696             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21697             v =  (v < 10) ? 10 : v;
21698             v =  (v > 48) ? 48 : v;
21699             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21700             
21701         }
21702         
21703         
21704         v = Math.max(1, v+adjust);
21705         
21706         this.execCmd('FontSize', v  );
21707     },
21708
21709     onEditorEvent : function(e)
21710     {
21711         this.owner.fireEvent('editorevent', this, e);
21712       //  this.updateToolbar();
21713         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21714     },
21715
21716     insertTag : function(tg)
21717     {
21718         // could be a bit smarter... -> wrap the current selected tRoo..
21719         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21720             
21721             range = this.createRange(this.getSelection());
21722             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21723             wrappingNode.appendChild(range.extractContents());
21724             range.insertNode(wrappingNode);
21725
21726             return;
21727             
21728             
21729             
21730         }
21731         this.execCmd("formatblock",   tg);
21732         
21733     },
21734     
21735     insertText : function(txt)
21736     {
21737         
21738         
21739         var range = this.createRange();
21740         range.deleteContents();
21741                //alert(Sender.getAttribute('label'));
21742                
21743         range.insertNode(this.doc.createTextNode(txt));
21744     } ,
21745     
21746      
21747
21748     /**
21749      * Executes a Midas editor command on the editor document and performs necessary focus and
21750      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21751      * @param {String} cmd The Midas command
21752      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21753      */
21754     relayCmd : function(cmd, value){
21755         this.win.focus();
21756         this.execCmd(cmd, value);
21757         this.owner.fireEvent('editorevent', this);
21758         //this.updateToolbar();
21759         this.owner.deferFocus();
21760     },
21761
21762     /**
21763      * Executes a Midas editor command directly on the editor document.
21764      * For visual commands, you should use {@link #relayCmd} instead.
21765      * <b>This should only be called after the editor is initialized.</b>
21766      * @param {String} cmd The Midas command
21767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21768      */
21769     execCmd : function(cmd, value){
21770         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21771         this.syncValue();
21772     },
21773  
21774  
21775    
21776     /**
21777      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21778      * to insert tRoo.
21779      * @param {String} text | dom node.. 
21780      */
21781     insertAtCursor : function(text)
21782     {
21783         
21784         if(!this.activated){
21785             return;
21786         }
21787         /*
21788         if(Roo.isIE){
21789             this.win.focus();
21790             var r = this.doc.selection.createRange();
21791             if(r){
21792                 r.collapse(true);
21793                 r.pasteHTML(text);
21794                 this.syncValue();
21795                 this.deferFocus();
21796             
21797             }
21798             return;
21799         }
21800         */
21801         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21802             this.win.focus();
21803             
21804             
21805             // from jquery ui (MIT licenced)
21806             var range, node;
21807             var win = this.win;
21808             
21809             if (win.getSelection && win.getSelection().getRangeAt) {
21810                 range = win.getSelection().getRangeAt(0);
21811                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21812                 range.insertNode(node);
21813             } else if (win.document.selection && win.document.selection.createRange) {
21814                 // no firefox support
21815                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21816                 win.document.selection.createRange().pasteHTML(txt);
21817             } else {
21818                 // no firefox support
21819                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21820                 this.execCmd('InsertHTML', txt);
21821             } 
21822             
21823             this.syncValue();
21824             
21825             this.deferFocus();
21826         }
21827     },
21828  // private
21829     mozKeyPress : function(e){
21830         if(e.ctrlKey){
21831             var c = e.getCharCode(), cmd;
21832           
21833             if(c > 0){
21834                 c = String.fromCharCode(c).toLowerCase();
21835                 switch(c){
21836                     case 'b':
21837                         cmd = 'bold';
21838                         break;
21839                     case 'i':
21840                         cmd = 'italic';
21841                         break;
21842                     
21843                     case 'u':
21844                         cmd = 'underline';
21845                         break;
21846                     
21847                     case 'v':
21848                         this.cleanUpPaste.defer(100, this);
21849                         return;
21850                         
21851                 }
21852                 if(cmd){
21853                     this.win.focus();
21854                     this.execCmd(cmd);
21855                     this.deferFocus();
21856                     e.preventDefault();
21857                 }
21858                 
21859             }
21860         }
21861     },
21862
21863     // private
21864     fixKeys : function(){ // load time branching for fastest keydown performance
21865         if(Roo.isIE){
21866             return function(e){
21867                 var k = e.getKey(), r;
21868                 if(k == e.TAB){
21869                     e.stopEvent();
21870                     r = this.doc.selection.createRange();
21871                     if(r){
21872                         r.collapse(true);
21873                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21874                         this.deferFocus();
21875                     }
21876                     return;
21877                 }
21878                 
21879                 if(k == e.ENTER){
21880                     r = this.doc.selection.createRange();
21881                     if(r){
21882                         var target = r.parentElement();
21883                         if(!target || target.tagName.toLowerCase() != 'li'){
21884                             e.stopEvent();
21885                             r.pasteHTML('<br />');
21886                             r.collapse(false);
21887                             r.select();
21888                         }
21889                     }
21890                 }
21891                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21892                     this.cleanUpPaste.defer(100, this);
21893                     return;
21894                 }
21895                 
21896                 
21897             };
21898         }else if(Roo.isOpera){
21899             return function(e){
21900                 var k = e.getKey();
21901                 if(k == e.TAB){
21902                     e.stopEvent();
21903                     this.win.focus();
21904                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21905                     this.deferFocus();
21906                 }
21907                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21908                     this.cleanUpPaste.defer(100, this);
21909                     return;
21910                 }
21911                 
21912             };
21913         }else if(Roo.isSafari){
21914             return function(e){
21915                 var k = e.getKey();
21916                 
21917                 if(k == e.TAB){
21918                     e.stopEvent();
21919                     this.execCmd('InsertText','\t');
21920                     this.deferFocus();
21921                     return;
21922                 }
21923                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21924                     this.cleanUpPaste.defer(100, this);
21925                     return;
21926                 }
21927                 
21928              };
21929         }
21930     }(),
21931     
21932     getAllAncestors: function()
21933     {
21934         var p = this.getSelectedNode();
21935         var a = [];
21936         if (!p) {
21937             a.push(p); // push blank onto stack..
21938             p = this.getParentElement();
21939         }
21940         
21941         
21942         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21943             a.push(p);
21944             p = p.parentNode;
21945         }
21946         a.push(this.doc.body);
21947         return a;
21948     },
21949     lastSel : false,
21950     lastSelNode : false,
21951     
21952     
21953     getSelection : function() 
21954     {
21955         this.assignDocWin();
21956         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21957     },
21958     
21959     getSelectedNode: function() 
21960     {
21961         // this may only work on Gecko!!!
21962         
21963         // should we cache this!!!!
21964         
21965         
21966         
21967          
21968         var range = this.createRange(this.getSelection()).cloneRange();
21969         
21970         if (Roo.isIE) {
21971             var parent = range.parentElement();
21972             while (true) {
21973                 var testRange = range.duplicate();
21974                 testRange.moveToElementText(parent);
21975                 if (testRange.inRange(range)) {
21976                     break;
21977                 }
21978                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21979                     break;
21980                 }
21981                 parent = parent.parentElement;
21982             }
21983             return parent;
21984         }
21985         
21986         // is ancestor a text element.
21987         var ac =  range.commonAncestorContainer;
21988         if (ac.nodeType == 3) {
21989             ac = ac.parentNode;
21990         }
21991         
21992         var ar = ac.childNodes;
21993          
21994         var nodes = [];
21995         var other_nodes = [];
21996         var has_other_nodes = false;
21997         for (var i=0;i<ar.length;i++) {
21998             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21999                 continue;
22000             }
22001             // fullly contained node.
22002             
22003             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22004                 nodes.push(ar[i]);
22005                 continue;
22006             }
22007             
22008             // probably selected..
22009             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22010                 other_nodes.push(ar[i]);
22011                 continue;
22012             }
22013             // outer..
22014             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22015                 continue;
22016             }
22017             
22018             
22019             has_other_nodes = true;
22020         }
22021         if (!nodes.length && other_nodes.length) {
22022             nodes= other_nodes;
22023         }
22024         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22025             return false;
22026         }
22027         
22028         return nodes[0];
22029     },
22030     createRange: function(sel)
22031     {
22032         // this has strange effects when using with 
22033         // top toolbar - not sure if it's a great idea.
22034         //this.editor.contentWindow.focus();
22035         if (typeof sel != "undefined") {
22036             try {
22037                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22038             } catch(e) {
22039                 return this.doc.createRange();
22040             }
22041         } else {
22042             return this.doc.createRange();
22043         }
22044     },
22045     getParentElement: function()
22046     {
22047         
22048         this.assignDocWin();
22049         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22050         
22051         var range = this.createRange(sel);
22052          
22053         try {
22054             var p = range.commonAncestorContainer;
22055             while (p.nodeType == 3) { // text node
22056                 p = p.parentNode;
22057             }
22058             return p;
22059         } catch (e) {
22060             return null;
22061         }
22062     
22063     },
22064     /***
22065      *
22066      * Range intersection.. the hard stuff...
22067      *  '-1' = before
22068      *  '0' = hits..
22069      *  '1' = after.
22070      *         [ -- selected range --- ]
22071      *   [fail]                        [fail]
22072      *
22073      *    basically..
22074      *      if end is before start or  hits it. fail.
22075      *      if start is after end or hits it fail.
22076      *
22077      *   if either hits (but other is outside. - then it's not 
22078      *   
22079      *    
22080      **/
22081     
22082     
22083     // @see http://www.thismuchiknow.co.uk/?p=64.
22084     rangeIntersectsNode : function(range, node)
22085     {
22086         var nodeRange = node.ownerDocument.createRange();
22087         try {
22088             nodeRange.selectNode(node);
22089         } catch (e) {
22090             nodeRange.selectNodeContents(node);
22091         }
22092     
22093         var rangeStartRange = range.cloneRange();
22094         rangeStartRange.collapse(true);
22095     
22096         var rangeEndRange = range.cloneRange();
22097         rangeEndRange.collapse(false);
22098     
22099         var nodeStartRange = nodeRange.cloneRange();
22100         nodeStartRange.collapse(true);
22101     
22102         var nodeEndRange = nodeRange.cloneRange();
22103         nodeEndRange.collapse(false);
22104     
22105         return rangeStartRange.compareBoundaryPoints(
22106                  Range.START_TO_START, nodeEndRange) == -1 &&
22107                rangeEndRange.compareBoundaryPoints(
22108                  Range.START_TO_START, nodeStartRange) == 1;
22109         
22110          
22111     },
22112     rangeCompareNode : function(range, node)
22113     {
22114         var nodeRange = node.ownerDocument.createRange();
22115         try {
22116             nodeRange.selectNode(node);
22117         } catch (e) {
22118             nodeRange.selectNodeContents(node);
22119         }
22120         
22121         
22122         range.collapse(true);
22123     
22124         nodeRange.collapse(true);
22125      
22126         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22127         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22128          
22129         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22130         
22131         var nodeIsBefore   =  ss == 1;
22132         var nodeIsAfter    = ee == -1;
22133         
22134         if (nodeIsBefore && nodeIsAfter) {
22135             return 0; // outer
22136         }
22137         if (!nodeIsBefore && nodeIsAfter) {
22138             return 1; //right trailed.
22139         }
22140         
22141         if (nodeIsBefore && !nodeIsAfter) {
22142             return 2;  // left trailed.
22143         }
22144         // fully contined.
22145         return 3;
22146     },
22147
22148     // private? - in a new class?
22149     cleanUpPaste :  function()
22150     {
22151         // cleans up the whole document..
22152         Roo.log('cleanuppaste');
22153         
22154         this.cleanUpChildren(this.doc.body);
22155         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22156         if (clean != this.doc.body.innerHTML) {
22157             this.doc.body.innerHTML = clean;
22158         }
22159         
22160     },
22161     
22162     cleanWordChars : function(input) {// change the chars to hex code
22163         var he = Roo.HtmlEditorCore;
22164         
22165         var output = input;
22166         Roo.each(he.swapCodes, function(sw) { 
22167             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22168             
22169             output = output.replace(swapper, sw[1]);
22170         });
22171         
22172         return output;
22173     },
22174     
22175     
22176     cleanUpChildren : function (n)
22177     {
22178         if (!n.childNodes.length) {
22179             return;
22180         }
22181         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22182            this.cleanUpChild(n.childNodes[i]);
22183         }
22184     },
22185     
22186     
22187         
22188     
22189     cleanUpChild : function (node)
22190     {
22191         var ed = this;
22192         //console.log(node);
22193         if (node.nodeName == "#text") {
22194             // clean up silly Windows -- stuff?
22195             return; 
22196         }
22197         if (node.nodeName == "#comment") {
22198             node.parentNode.removeChild(node);
22199             // clean up silly Windows -- stuff?
22200             return; 
22201         }
22202         var lcname = node.tagName.toLowerCase();
22203         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22204         // whitelist of tags..
22205         
22206         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22207             // remove node.
22208             node.parentNode.removeChild(node);
22209             return;
22210             
22211         }
22212         
22213         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22214         
22215         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22216         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22217         
22218         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22219         //    remove_keep_children = true;
22220         //}
22221         
22222         if (remove_keep_children) {
22223             this.cleanUpChildren(node);
22224             // inserts everything just before this node...
22225             while (node.childNodes.length) {
22226                 var cn = node.childNodes[0];
22227                 node.removeChild(cn);
22228                 node.parentNode.insertBefore(cn, node);
22229             }
22230             node.parentNode.removeChild(node);
22231             return;
22232         }
22233         
22234         if (!node.attributes || !node.attributes.length) {
22235             this.cleanUpChildren(node);
22236             return;
22237         }
22238         
22239         function cleanAttr(n,v)
22240         {
22241             
22242             if (v.match(/^\./) || v.match(/^\//)) {
22243                 return;
22244             }
22245             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22246                 return;
22247             }
22248             if (v.match(/^#/)) {
22249                 return;
22250             }
22251 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22252             node.removeAttribute(n);
22253             
22254         }
22255         
22256         var cwhite = this.cwhite;
22257         var cblack = this.cblack;
22258             
22259         function cleanStyle(n,v)
22260         {
22261             if (v.match(/expression/)) { //XSS?? should we even bother..
22262                 node.removeAttribute(n);
22263                 return;
22264             }
22265             
22266             var parts = v.split(/;/);
22267             var clean = [];
22268             
22269             Roo.each(parts, function(p) {
22270                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22271                 if (!p.length) {
22272                     return true;
22273                 }
22274                 var l = p.split(':').shift().replace(/\s+/g,'');
22275                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22276                 
22277                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22278 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22279                     //node.removeAttribute(n);
22280                     return true;
22281                 }
22282                 //Roo.log()
22283                 // only allow 'c whitelisted system attributes'
22284                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22285 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22286                     //node.removeAttribute(n);
22287                     return true;
22288                 }
22289                 
22290                 
22291                  
22292                 
22293                 clean.push(p);
22294                 return true;
22295             });
22296             if (clean.length) { 
22297                 node.setAttribute(n, clean.join(';'));
22298             } else {
22299                 node.removeAttribute(n);
22300             }
22301             
22302         }
22303         
22304         
22305         for (var i = node.attributes.length-1; i > -1 ; i--) {
22306             var a = node.attributes[i];
22307             //console.log(a);
22308             
22309             if (a.name.toLowerCase().substr(0,2)=='on')  {
22310                 node.removeAttribute(a.name);
22311                 continue;
22312             }
22313             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22314                 node.removeAttribute(a.name);
22315                 continue;
22316             }
22317             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22318                 cleanAttr(a.name,a.value); // fixme..
22319                 continue;
22320             }
22321             if (a.name == 'style') {
22322                 cleanStyle(a.name,a.value);
22323                 continue;
22324             }
22325             /// clean up MS crap..
22326             // tecnically this should be a list of valid class'es..
22327             
22328             
22329             if (a.name == 'class') {
22330                 if (a.value.match(/^Mso/)) {
22331                     node.className = '';
22332                 }
22333                 
22334                 if (a.value.match(/^body$/)) {
22335                     node.className = '';
22336                 }
22337                 continue;
22338             }
22339             
22340             // style cleanup!?
22341             // class cleanup?
22342             
22343         }
22344         
22345         
22346         this.cleanUpChildren(node);
22347         
22348         
22349     },
22350     
22351     /**
22352      * Clean up MS wordisms...
22353      */
22354     cleanWord : function(node)
22355     {
22356         
22357         
22358         if (!node) {
22359             this.cleanWord(this.doc.body);
22360             return;
22361         }
22362         if (node.nodeName == "#text") {
22363             // clean up silly Windows -- stuff?
22364             return; 
22365         }
22366         if (node.nodeName == "#comment") {
22367             node.parentNode.removeChild(node);
22368             // clean up silly Windows -- stuff?
22369             return; 
22370         }
22371         
22372         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22373             node.parentNode.removeChild(node);
22374             return;
22375         }
22376         
22377         // remove - but keep children..
22378         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22379             while (node.childNodes.length) {
22380                 var cn = node.childNodes[0];
22381                 node.removeChild(cn);
22382                 node.parentNode.insertBefore(cn, node);
22383             }
22384             node.parentNode.removeChild(node);
22385             this.iterateChildren(node, this.cleanWord);
22386             return;
22387         }
22388         // clean styles
22389         if (node.className.length) {
22390             
22391             var cn = node.className.split(/\W+/);
22392             var cna = [];
22393             Roo.each(cn, function(cls) {
22394                 if (cls.match(/Mso[a-zA-Z]+/)) {
22395                     return;
22396                 }
22397                 cna.push(cls);
22398             });
22399             node.className = cna.length ? cna.join(' ') : '';
22400             if (!cna.length) {
22401                 node.removeAttribute("class");
22402             }
22403         }
22404         
22405         if (node.hasAttribute("lang")) {
22406             node.removeAttribute("lang");
22407         }
22408         
22409         if (node.hasAttribute("style")) {
22410             
22411             var styles = node.getAttribute("style").split(";");
22412             var nstyle = [];
22413             Roo.each(styles, function(s) {
22414                 if (!s.match(/:/)) {
22415                     return;
22416                 }
22417                 var kv = s.split(":");
22418                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22419                     return;
22420                 }
22421                 // what ever is left... we allow.
22422                 nstyle.push(s);
22423             });
22424             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22425             if (!nstyle.length) {
22426                 node.removeAttribute('style');
22427             }
22428         }
22429         this.iterateChildren(node, this.cleanWord);
22430         
22431         
22432         
22433     },
22434     /**
22435      * iterateChildren of a Node, calling fn each time, using this as the scole..
22436      * @param {DomNode} node node to iterate children of.
22437      * @param {Function} fn method of this class to call on each item.
22438      */
22439     iterateChildren : function(node, fn)
22440     {
22441         if (!node.childNodes.length) {
22442                 return;
22443         }
22444         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22445            fn.call(this, node.childNodes[i])
22446         }
22447     },
22448     
22449     
22450     /**
22451      * cleanTableWidths.
22452      *
22453      * Quite often pasting from word etc.. results in tables with column and widths.
22454      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22455      *
22456      */
22457     cleanTableWidths : function(node)
22458     {
22459          
22460          
22461         if (!node) {
22462             this.cleanTableWidths(this.doc.body);
22463             return;
22464         }
22465         
22466         // ignore list...
22467         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22468             return; 
22469         }
22470         Roo.log(node.tagName);
22471         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22472             this.iterateChildren(node, this.cleanTableWidths);
22473             return;
22474         }
22475         if (node.hasAttribute('width')) {
22476             node.removeAttribute('width');
22477         }
22478         
22479          
22480         if (node.hasAttribute("style")) {
22481             // pretty basic...
22482             
22483             var styles = node.getAttribute("style").split(";");
22484             var nstyle = [];
22485             Roo.each(styles, function(s) {
22486                 if (!s.match(/:/)) {
22487                     return;
22488                 }
22489                 var kv = s.split(":");
22490                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22491                     return;
22492                 }
22493                 // what ever is left... we allow.
22494                 nstyle.push(s);
22495             });
22496             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22497             if (!nstyle.length) {
22498                 node.removeAttribute('style');
22499             }
22500         }
22501         
22502         this.iterateChildren(node, this.cleanTableWidths);
22503         
22504         
22505     },
22506     
22507     
22508     
22509     
22510     domToHTML : function(currentElement, depth, nopadtext) {
22511         
22512         depth = depth || 0;
22513         nopadtext = nopadtext || false;
22514     
22515         if (!currentElement) {
22516             return this.domToHTML(this.doc.body);
22517         }
22518         
22519         //Roo.log(currentElement);
22520         var j;
22521         var allText = false;
22522         var nodeName = currentElement.nodeName;
22523         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22524         
22525         if  (nodeName == '#text') {
22526             
22527             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22528         }
22529         
22530         
22531         var ret = '';
22532         if (nodeName != 'BODY') {
22533              
22534             var i = 0;
22535             // Prints the node tagName, such as <A>, <IMG>, etc
22536             if (tagName) {
22537                 var attr = [];
22538                 for(i = 0; i < currentElement.attributes.length;i++) {
22539                     // quoting?
22540                     var aname = currentElement.attributes.item(i).name;
22541                     if (!currentElement.attributes.item(i).value.length) {
22542                         continue;
22543                     }
22544                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22545                 }
22546                 
22547                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22548             } 
22549             else {
22550                 
22551                 // eack
22552             }
22553         } else {
22554             tagName = false;
22555         }
22556         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22557             return ret;
22558         }
22559         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22560             nopadtext = true;
22561         }
22562         
22563         
22564         // Traverse the tree
22565         i = 0;
22566         var currentElementChild = currentElement.childNodes.item(i);
22567         var allText = true;
22568         var innerHTML  = '';
22569         lastnode = '';
22570         while (currentElementChild) {
22571             // Formatting code (indent the tree so it looks nice on the screen)
22572             var nopad = nopadtext;
22573             if (lastnode == 'SPAN') {
22574                 nopad  = true;
22575             }
22576             // text
22577             if  (currentElementChild.nodeName == '#text') {
22578                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22579                 toadd = nopadtext ? toadd : toadd.trim();
22580                 if (!nopad && toadd.length > 80) {
22581                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22582                 }
22583                 innerHTML  += toadd;
22584                 
22585                 i++;
22586                 currentElementChild = currentElement.childNodes.item(i);
22587                 lastNode = '';
22588                 continue;
22589             }
22590             allText = false;
22591             
22592             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22593                 
22594             // Recursively traverse the tree structure of the child node
22595             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22596             lastnode = currentElementChild.nodeName;
22597             i++;
22598             currentElementChild=currentElement.childNodes.item(i);
22599         }
22600         
22601         ret += innerHTML;
22602         
22603         if (!allText) {
22604                 // The remaining code is mostly for formatting the tree
22605             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22606         }
22607         
22608         
22609         if (tagName) {
22610             ret+= "</"+tagName+">";
22611         }
22612         return ret;
22613         
22614     },
22615         
22616     applyBlacklists : function()
22617     {
22618         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22619         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22620         
22621         this.white = [];
22622         this.black = [];
22623         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22624             if (b.indexOf(tag) > -1) {
22625                 return;
22626             }
22627             this.white.push(tag);
22628             
22629         }, this);
22630         
22631         Roo.each(w, function(tag) {
22632             if (b.indexOf(tag) > -1) {
22633                 return;
22634             }
22635             if (this.white.indexOf(tag) > -1) {
22636                 return;
22637             }
22638             this.white.push(tag);
22639             
22640         }, this);
22641         
22642         
22643         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22644             if (w.indexOf(tag) > -1) {
22645                 return;
22646             }
22647             this.black.push(tag);
22648             
22649         }, this);
22650         
22651         Roo.each(b, function(tag) {
22652             if (w.indexOf(tag) > -1) {
22653                 return;
22654             }
22655             if (this.black.indexOf(tag) > -1) {
22656                 return;
22657             }
22658             this.black.push(tag);
22659             
22660         }, this);
22661         
22662         
22663         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22664         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22665         
22666         this.cwhite = [];
22667         this.cblack = [];
22668         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22669             if (b.indexOf(tag) > -1) {
22670                 return;
22671             }
22672             this.cwhite.push(tag);
22673             
22674         }, this);
22675         
22676         Roo.each(w, function(tag) {
22677             if (b.indexOf(tag) > -1) {
22678                 return;
22679             }
22680             if (this.cwhite.indexOf(tag) > -1) {
22681                 return;
22682             }
22683             this.cwhite.push(tag);
22684             
22685         }, this);
22686         
22687         
22688         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22689             if (w.indexOf(tag) > -1) {
22690                 return;
22691             }
22692             this.cblack.push(tag);
22693             
22694         }, this);
22695         
22696         Roo.each(b, function(tag) {
22697             if (w.indexOf(tag) > -1) {
22698                 return;
22699             }
22700             if (this.cblack.indexOf(tag) > -1) {
22701                 return;
22702             }
22703             this.cblack.push(tag);
22704             
22705         }, this);
22706     },
22707     
22708     setStylesheets : function(stylesheets)
22709     {
22710         if(typeof(stylesheets) == 'string'){
22711             Roo.get(this.iframe.contentDocument.head).createChild({
22712                 tag : 'link',
22713                 rel : 'stylesheet',
22714                 type : 'text/css',
22715                 href : stylesheets
22716             });
22717             
22718             return;
22719         }
22720         var _this = this;
22721      
22722         Roo.each(stylesheets, function(s) {
22723             if(!s.length){
22724                 return;
22725             }
22726             
22727             Roo.get(_this.iframe.contentDocument.head).createChild({
22728                 tag : 'link',
22729                 rel : 'stylesheet',
22730                 type : 'text/css',
22731                 href : s
22732             });
22733         });
22734
22735         
22736     },
22737     
22738     removeStylesheets : function()
22739     {
22740         var _this = this;
22741         
22742         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22743             s.remove();
22744         });
22745     },
22746     
22747     setStyle : function(style)
22748     {
22749         Roo.get(this.iframe.contentDocument.head).createChild({
22750             tag : 'style',
22751             type : 'text/css',
22752             html : style
22753         });
22754
22755         return;
22756     }
22757     
22758     // hide stuff that is not compatible
22759     /**
22760      * @event blur
22761      * @hide
22762      */
22763     /**
22764      * @event change
22765      * @hide
22766      */
22767     /**
22768      * @event focus
22769      * @hide
22770      */
22771     /**
22772      * @event specialkey
22773      * @hide
22774      */
22775     /**
22776      * @cfg {String} fieldClass @hide
22777      */
22778     /**
22779      * @cfg {String} focusClass @hide
22780      */
22781     /**
22782      * @cfg {String} autoCreate @hide
22783      */
22784     /**
22785      * @cfg {String} inputType @hide
22786      */
22787     /**
22788      * @cfg {String} invalidClass @hide
22789      */
22790     /**
22791      * @cfg {String} invalidText @hide
22792      */
22793     /**
22794      * @cfg {String} msgFx @hide
22795      */
22796     /**
22797      * @cfg {String} validateOnBlur @hide
22798      */
22799 });
22800
22801 Roo.HtmlEditorCore.white = [
22802         'area', 'br', 'img', 'input', 'hr', 'wbr',
22803         
22804        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22805        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22806        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22807        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22808        'table',   'ul',         'xmp', 
22809        
22810        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22811       'thead',   'tr', 
22812      
22813       'dir', 'menu', 'ol', 'ul', 'dl',
22814        
22815       'embed',  'object'
22816 ];
22817
22818
22819 Roo.HtmlEditorCore.black = [
22820     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22821         'applet', // 
22822         'base',   'basefont', 'bgsound', 'blink',  'body', 
22823         'frame',  'frameset', 'head',    'html',   'ilayer', 
22824         'iframe', 'layer',  'link',     'meta',    'object',   
22825         'script', 'style' ,'title',  'xml' // clean later..
22826 ];
22827 Roo.HtmlEditorCore.clean = [
22828     'script', 'style', 'title', 'xml'
22829 ];
22830 Roo.HtmlEditorCore.remove = [
22831     'font'
22832 ];
22833 // attributes..
22834
22835 Roo.HtmlEditorCore.ablack = [
22836     'on'
22837 ];
22838     
22839 Roo.HtmlEditorCore.aclean = [ 
22840     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22841 ];
22842
22843 // protocols..
22844 Roo.HtmlEditorCore.pwhite= [
22845         'http',  'https',  'mailto'
22846 ];
22847
22848 // white listed style attributes.
22849 Roo.HtmlEditorCore.cwhite= [
22850       //  'text-align', /// default is to allow most things..
22851       
22852          
22853 //        'font-size'//??
22854 ];
22855
22856 // black listed style attributes.
22857 Roo.HtmlEditorCore.cblack= [
22858       //  'font-size' -- this can be set by the project 
22859 ];
22860
22861
22862 Roo.HtmlEditorCore.swapCodes   =[ 
22863     [    8211, "--" ], 
22864     [    8212, "--" ], 
22865     [    8216,  "'" ],  
22866     [    8217, "'" ],  
22867     [    8220, '"' ],  
22868     [    8221, '"' ],  
22869     [    8226, "*" ],  
22870     [    8230, "..." ]
22871 ]; 
22872
22873     /*
22874  * - LGPL
22875  *
22876  * HtmlEditor
22877  * 
22878  */
22879
22880 /**
22881  * @class Roo.bootstrap.HtmlEditor
22882  * @extends Roo.bootstrap.TextArea
22883  * Bootstrap HtmlEditor class
22884
22885  * @constructor
22886  * Create a new HtmlEditor
22887  * @param {Object} config The config object
22888  */
22889
22890 Roo.bootstrap.HtmlEditor = function(config){
22891     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22892     if (!this.toolbars) {
22893         this.toolbars = [];
22894     }
22895     
22896     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22897     this.addEvents({
22898             /**
22899              * @event initialize
22900              * Fires when the editor is fully initialized (including the iframe)
22901              * @param {HtmlEditor} this
22902              */
22903             initialize: true,
22904             /**
22905              * @event activate
22906              * Fires when the editor is first receives the focus. Any insertion must wait
22907              * until after this event.
22908              * @param {HtmlEditor} this
22909              */
22910             activate: true,
22911              /**
22912              * @event beforesync
22913              * Fires before the textarea is updated with content from the editor iframe. Return false
22914              * to cancel the sync.
22915              * @param {HtmlEditor} this
22916              * @param {String} html
22917              */
22918             beforesync: true,
22919              /**
22920              * @event beforepush
22921              * Fires before the iframe editor is updated with content from the textarea. Return false
22922              * to cancel the push.
22923              * @param {HtmlEditor} this
22924              * @param {String} html
22925              */
22926             beforepush: true,
22927              /**
22928              * @event sync
22929              * Fires when the textarea is updated with content from the editor iframe.
22930              * @param {HtmlEditor} this
22931              * @param {String} html
22932              */
22933             sync: true,
22934              /**
22935              * @event push
22936              * Fires when the iframe editor is updated with content from the textarea.
22937              * @param {HtmlEditor} this
22938              * @param {String} html
22939              */
22940             push: true,
22941              /**
22942              * @event editmodechange
22943              * Fires when the editor switches edit modes
22944              * @param {HtmlEditor} this
22945              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22946              */
22947             editmodechange: true,
22948             /**
22949              * @event editorevent
22950              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22951              * @param {HtmlEditor} this
22952              */
22953             editorevent: true,
22954             /**
22955              * @event firstfocus
22956              * Fires when on first focus - needed by toolbars..
22957              * @param {HtmlEditor} this
22958              */
22959             firstfocus: true,
22960             /**
22961              * @event autosave
22962              * Auto save the htmlEditor value as a file into Events
22963              * @param {HtmlEditor} this
22964              */
22965             autosave: true,
22966             /**
22967              * @event savedpreview
22968              * preview the saved version of htmlEditor
22969              * @param {HtmlEditor} this
22970              */
22971             savedpreview: true
22972         });
22973 };
22974
22975
22976 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22977     
22978     
22979       /**
22980      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22981      */
22982     toolbars : false,
22983     
22984      /**
22985     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22986     */
22987     btns : [],
22988    
22989      /**
22990      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22991      *                        Roo.resizable.
22992      */
22993     resizable : false,
22994      /**
22995      * @cfg {Number} height (in pixels)
22996      */   
22997     height: 300,
22998    /**
22999      * @cfg {Number} width (in pixels)
23000      */   
23001     width: false,
23002     
23003     /**
23004      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23005      * 
23006      */
23007     stylesheets: false,
23008     
23009     // id of frame..
23010     frameId: false,
23011     
23012     // private properties
23013     validationEvent : false,
23014     deferHeight: true,
23015     initialized : false,
23016     activated : false,
23017     
23018     onFocus : Roo.emptyFn,
23019     iframePad:3,
23020     hideMode:'offsets',
23021     
23022     tbContainer : false,
23023     
23024     bodyCls : '',
23025     
23026     toolbarContainer :function() {
23027         return this.wrap.select('.x-html-editor-tb',true).first();
23028     },
23029
23030     /**
23031      * Protected method that will not generally be called directly. It
23032      * is called when the editor creates its toolbar. Override this method if you need to
23033      * add custom toolbar buttons.
23034      * @param {HtmlEditor} editor
23035      */
23036     createToolbar : function(){
23037         Roo.log('renewing');
23038         Roo.log("create toolbars");
23039         
23040         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23041         this.toolbars[0].render(this.toolbarContainer());
23042         
23043         return;
23044         
23045 //        if (!editor.toolbars || !editor.toolbars.length) {
23046 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23047 //        }
23048 //        
23049 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23050 //            editor.toolbars[i] = Roo.factory(
23051 //                    typeof(editor.toolbars[i]) == 'string' ?
23052 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23053 //                Roo.bootstrap.HtmlEditor);
23054 //            editor.toolbars[i].init(editor);
23055 //        }
23056     },
23057
23058      
23059     // private
23060     onRender : function(ct, position)
23061     {
23062        // Roo.log("Call onRender: " + this.xtype);
23063         var _t = this;
23064         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23065       
23066         this.wrap = this.inputEl().wrap({
23067             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23068         });
23069         
23070         this.editorcore.onRender(ct, position);
23071          
23072         if (this.resizable) {
23073             this.resizeEl = new Roo.Resizable(this.wrap, {
23074                 pinned : true,
23075                 wrap: true,
23076                 dynamic : true,
23077                 minHeight : this.height,
23078                 height: this.height,
23079                 handles : this.resizable,
23080                 width: this.width,
23081                 listeners : {
23082                     resize : function(r, w, h) {
23083                         _t.onResize(w,h); // -something
23084                     }
23085                 }
23086             });
23087             
23088         }
23089         this.createToolbar(this);
23090        
23091         
23092         if(!this.width && this.resizable){
23093             this.setSize(this.wrap.getSize());
23094         }
23095         if (this.resizeEl) {
23096             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23097             // should trigger onReize..
23098         }
23099         
23100     },
23101
23102     // private
23103     onResize : function(w, h)
23104     {
23105         Roo.log('resize: ' +w + ',' + h );
23106         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23107         var ew = false;
23108         var eh = false;
23109         
23110         if(this.inputEl() ){
23111             if(typeof w == 'number'){
23112                 var aw = w - this.wrap.getFrameWidth('lr');
23113                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23114                 ew = aw;
23115             }
23116             if(typeof h == 'number'){
23117                  var tbh = -11;  // fixme it needs to tool bar size!
23118                 for (var i =0; i < this.toolbars.length;i++) {
23119                     // fixme - ask toolbars for heights?
23120                     tbh += this.toolbars[i].el.getHeight();
23121                     //if (this.toolbars[i].footer) {
23122                     //    tbh += this.toolbars[i].footer.el.getHeight();
23123                     //}
23124                 }
23125               
23126                 
23127                 
23128                 
23129                 
23130                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23131                 ah -= 5; // knock a few pixes off for look..
23132                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23133                 var eh = ah;
23134             }
23135         }
23136         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23137         this.editorcore.onResize(ew,eh);
23138         
23139     },
23140
23141     /**
23142      * Toggles the editor between standard and source edit mode.
23143      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23144      */
23145     toggleSourceEdit : function(sourceEditMode)
23146     {
23147         this.editorcore.toggleSourceEdit(sourceEditMode);
23148         
23149         if(this.editorcore.sourceEditMode){
23150             Roo.log('editor - showing textarea');
23151             
23152 //            Roo.log('in');
23153 //            Roo.log(this.syncValue());
23154             this.syncValue();
23155             this.inputEl().removeClass(['hide', 'x-hidden']);
23156             this.inputEl().dom.removeAttribute('tabIndex');
23157             this.inputEl().focus();
23158         }else{
23159             Roo.log('editor - hiding textarea');
23160 //            Roo.log('out')
23161 //            Roo.log(this.pushValue()); 
23162             this.pushValue();
23163             
23164             this.inputEl().addClass(['hide', 'x-hidden']);
23165             this.inputEl().dom.setAttribute('tabIndex', -1);
23166             //this.deferFocus();
23167         }
23168          
23169         if(this.resizable){
23170             this.setSize(this.wrap.getSize());
23171         }
23172         
23173         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23174     },
23175  
23176     // private (for BoxComponent)
23177     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23178
23179     // private (for BoxComponent)
23180     getResizeEl : function(){
23181         return this.wrap;
23182     },
23183
23184     // private (for BoxComponent)
23185     getPositionEl : function(){
23186         return this.wrap;
23187     },
23188
23189     // private
23190     initEvents : function(){
23191         this.originalValue = this.getValue();
23192     },
23193
23194 //    /**
23195 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23196 //     * @method
23197 //     */
23198 //    markInvalid : Roo.emptyFn,
23199 //    /**
23200 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23201 //     * @method
23202 //     */
23203 //    clearInvalid : Roo.emptyFn,
23204
23205     setValue : function(v){
23206         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23207         this.editorcore.pushValue();
23208     },
23209
23210      
23211     // private
23212     deferFocus : function(){
23213         this.focus.defer(10, this);
23214     },
23215
23216     // doc'ed in Field
23217     focus : function(){
23218         this.editorcore.focus();
23219         
23220     },
23221       
23222
23223     // private
23224     onDestroy : function(){
23225         
23226         
23227         
23228         if(this.rendered){
23229             
23230             for (var i =0; i < this.toolbars.length;i++) {
23231                 // fixme - ask toolbars for heights?
23232                 this.toolbars[i].onDestroy();
23233             }
23234             
23235             this.wrap.dom.innerHTML = '';
23236             this.wrap.remove();
23237         }
23238     },
23239
23240     // private
23241     onFirstFocus : function(){
23242         //Roo.log("onFirstFocus");
23243         this.editorcore.onFirstFocus();
23244          for (var i =0; i < this.toolbars.length;i++) {
23245             this.toolbars[i].onFirstFocus();
23246         }
23247         
23248     },
23249     
23250     // private
23251     syncValue : function()
23252     {   
23253         this.editorcore.syncValue();
23254     },
23255     
23256     pushValue : function()
23257     {   
23258         this.editorcore.pushValue();
23259     }
23260      
23261     
23262     // hide stuff that is not compatible
23263     /**
23264      * @event blur
23265      * @hide
23266      */
23267     /**
23268      * @event change
23269      * @hide
23270      */
23271     /**
23272      * @event focus
23273      * @hide
23274      */
23275     /**
23276      * @event specialkey
23277      * @hide
23278      */
23279     /**
23280      * @cfg {String} fieldClass @hide
23281      */
23282     /**
23283      * @cfg {String} focusClass @hide
23284      */
23285     /**
23286      * @cfg {String} autoCreate @hide
23287      */
23288     /**
23289      * @cfg {String} inputType @hide
23290      */
23291     /**
23292      * @cfg {String} invalidClass @hide
23293      */
23294     /**
23295      * @cfg {String} invalidText @hide
23296      */
23297     /**
23298      * @cfg {String} msgFx @hide
23299      */
23300     /**
23301      * @cfg {String} validateOnBlur @hide
23302      */
23303 });
23304  
23305     
23306    
23307    
23308    
23309       
23310 Roo.namespace('Roo.bootstrap.htmleditor');
23311 /**
23312  * @class Roo.bootstrap.HtmlEditorToolbar1
23313  * Basic Toolbar
23314  * 
23315  * Usage:
23316  *
23317  new Roo.bootstrap.HtmlEditor({
23318     ....
23319     toolbars : [
23320         new Roo.bootstrap.HtmlEditorToolbar1({
23321             disable : { fonts: 1 , format: 1, ..., ... , ...],
23322             btns : [ .... ]
23323         })
23324     }
23325      
23326  * 
23327  * @cfg {Object} disable List of elements to disable..
23328  * @cfg {Array} btns List of additional buttons.
23329  * 
23330  * 
23331  * NEEDS Extra CSS? 
23332  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23333  */
23334  
23335 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23336 {
23337     
23338     Roo.apply(this, config);
23339     
23340     // default disabled, based on 'good practice'..
23341     this.disable = this.disable || {};
23342     Roo.applyIf(this.disable, {
23343         fontSize : true,
23344         colors : true,
23345         specialElements : true
23346     });
23347     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23348     
23349     this.editor = config.editor;
23350     this.editorcore = config.editor.editorcore;
23351     
23352     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23353     
23354     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23355     // dont call parent... till later.
23356 }
23357 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23358      
23359     bar : true,
23360     
23361     editor : false,
23362     editorcore : false,
23363     
23364     
23365     formats : [
23366         "p" ,  
23367         "h1","h2","h3","h4","h5","h6", 
23368         "pre", "code", 
23369         "abbr", "acronym", "address", "cite", "samp", "var",
23370         'div','span'
23371     ],
23372     
23373     onRender : function(ct, position)
23374     {
23375        // Roo.log("Call onRender: " + this.xtype);
23376         
23377        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23378        Roo.log(this.el);
23379        this.el.dom.style.marginBottom = '0';
23380        var _this = this;
23381        var editorcore = this.editorcore;
23382        var editor= this.editor;
23383        
23384        var children = [];
23385        var btn = function(id,cmd , toggle, handler, html){
23386        
23387             var  event = toggle ? 'toggle' : 'click';
23388        
23389             var a = {
23390                 size : 'sm',
23391                 xtype: 'Button',
23392                 xns: Roo.bootstrap,
23393                 glyphicon : id,
23394                 cmd : id || cmd,
23395                 enableToggle:toggle !== false,
23396                 html : html || '',
23397                 pressed : toggle ? false : null,
23398                 listeners : {}
23399             };
23400             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23401                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23402             };
23403             children.push(a);
23404             return a;
23405        }
23406        
23407     //    var cb_box = function...
23408         
23409         var style = {
23410                 xtype: 'Button',
23411                 size : 'sm',
23412                 xns: Roo.bootstrap,
23413                 glyphicon : 'font',
23414                 //html : 'submit'
23415                 menu : {
23416                     xtype: 'Menu',
23417                     xns: Roo.bootstrap,
23418                     items:  []
23419                 }
23420         };
23421         Roo.each(this.formats, function(f) {
23422             style.menu.items.push({
23423                 xtype :'MenuItem',
23424                 xns: Roo.bootstrap,
23425                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23426                 tagname : f,
23427                 listeners : {
23428                     click : function()
23429                     {
23430                         editorcore.insertTag(this.tagname);
23431                         editor.focus();
23432                     }
23433                 }
23434                 
23435             });
23436         });
23437         children.push(style);   
23438         
23439         btn('bold',false,true);
23440         btn('italic',false,true);
23441         btn('align-left', 'justifyleft',true);
23442         btn('align-center', 'justifycenter',true);
23443         btn('align-right' , 'justifyright',true);
23444         btn('link', false, false, function(btn) {
23445             //Roo.log("create link?");
23446             var url = prompt(this.createLinkText, this.defaultLinkValue);
23447             if(url && url != 'http:/'+'/'){
23448                 this.editorcore.relayCmd('createlink', url);
23449             }
23450         }),
23451         btn('list','insertunorderedlist',true);
23452         btn('pencil', false,true, function(btn){
23453                 Roo.log(this);
23454                 this.toggleSourceEdit(btn.pressed);
23455         });
23456         
23457         if (this.editor.btns.length > 0) {
23458             for (var i = 0; i<this.editor.btns.length; i++) {
23459                 children.push(this.editor.btns[i]);
23460             }
23461         }
23462         
23463         /*
23464         var cog = {
23465                 xtype: 'Button',
23466                 size : 'sm',
23467                 xns: Roo.bootstrap,
23468                 glyphicon : 'cog',
23469                 //html : 'submit'
23470                 menu : {
23471                     xtype: 'Menu',
23472                     xns: Roo.bootstrap,
23473                     items:  []
23474                 }
23475         };
23476         
23477         cog.menu.items.push({
23478             xtype :'MenuItem',
23479             xns: Roo.bootstrap,
23480             html : Clean styles,
23481             tagname : f,
23482             listeners : {
23483                 click : function()
23484                 {
23485                     editorcore.insertTag(this.tagname);
23486                     editor.focus();
23487                 }
23488             }
23489             
23490         });
23491        */
23492         
23493          
23494        this.xtype = 'NavSimplebar';
23495         
23496         for(var i=0;i< children.length;i++) {
23497             
23498             this.buttons.add(this.addxtypeChild(children[i]));
23499             
23500         }
23501         
23502         editor.on('editorevent', this.updateToolbar, this);
23503     },
23504     onBtnClick : function(id)
23505     {
23506        this.editorcore.relayCmd(id);
23507        this.editorcore.focus();
23508     },
23509     
23510     /**
23511      * Protected method that will not generally be called directly. It triggers
23512      * a toolbar update by reading the markup state of the current selection in the editor.
23513      */
23514     updateToolbar: function(){
23515
23516         if(!this.editorcore.activated){
23517             this.editor.onFirstFocus(); // is this neeed?
23518             return;
23519         }
23520
23521         var btns = this.buttons; 
23522         var doc = this.editorcore.doc;
23523         btns.get('bold').setActive(doc.queryCommandState('bold'));
23524         btns.get('italic').setActive(doc.queryCommandState('italic'));
23525         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23526         
23527         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23528         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23529         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23530         
23531         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23532         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23533          /*
23534         
23535         var ans = this.editorcore.getAllAncestors();
23536         if (this.formatCombo) {
23537             
23538             
23539             var store = this.formatCombo.store;
23540             this.formatCombo.setValue("");
23541             for (var i =0; i < ans.length;i++) {
23542                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23543                     // select it..
23544                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23545                     break;
23546                 }
23547             }
23548         }
23549         
23550         
23551         
23552         // hides menus... - so this cant be on a menu...
23553         Roo.bootstrap.MenuMgr.hideAll();
23554         */
23555         Roo.bootstrap.MenuMgr.hideAll();
23556         //this.editorsyncValue();
23557     },
23558     onFirstFocus: function() {
23559         this.buttons.each(function(item){
23560            item.enable();
23561         });
23562     },
23563     toggleSourceEdit : function(sourceEditMode){
23564         
23565           
23566         if(sourceEditMode){
23567             Roo.log("disabling buttons");
23568            this.buttons.each( function(item){
23569                 if(item.cmd != 'pencil'){
23570                     item.disable();
23571                 }
23572             });
23573           
23574         }else{
23575             Roo.log("enabling buttons");
23576             if(this.editorcore.initialized){
23577                 this.buttons.each( function(item){
23578                     item.enable();
23579                 });
23580             }
23581             
23582         }
23583         Roo.log("calling toggole on editor");
23584         // tell the editor that it's been pressed..
23585         this.editor.toggleSourceEdit(sourceEditMode);
23586        
23587     }
23588 });
23589
23590
23591
23592
23593
23594 /**
23595  * @class Roo.bootstrap.Table.AbstractSelectionModel
23596  * @extends Roo.util.Observable
23597  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23598  * implemented by descendant classes.  This class should not be directly instantiated.
23599  * @constructor
23600  */
23601 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23602     this.locked = false;
23603     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23604 };
23605
23606
23607 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23608     /** @ignore Called by the grid automatically. Do not call directly. */
23609     init : function(grid){
23610         this.grid = grid;
23611         this.initEvents();
23612     },
23613
23614     /**
23615      * Locks the selections.
23616      */
23617     lock : function(){
23618         this.locked = true;
23619     },
23620
23621     /**
23622      * Unlocks the selections.
23623      */
23624     unlock : function(){
23625         this.locked = false;
23626     },
23627
23628     /**
23629      * Returns true if the selections are locked.
23630      * @return {Boolean}
23631      */
23632     isLocked : function(){
23633         return this.locked;
23634     }
23635 });
23636 /**
23637  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23638  * @class Roo.bootstrap.Table.RowSelectionModel
23639  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23640  * It supports multiple selections and keyboard selection/navigation. 
23641  * @constructor
23642  * @param {Object} config
23643  */
23644
23645 Roo.bootstrap.Table.RowSelectionModel = function(config){
23646     Roo.apply(this, config);
23647     this.selections = new Roo.util.MixedCollection(false, function(o){
23648         return o.id;
23649     });
23650
23651     this.last = false;
23652     this.lastActive = false;
23653
23654     this.addEvents({
23655         /**
23656              * @event selectionchange
23657              * Fires when the selection changes
23658              * @param {SelectionModel} this
23659              */
23660             "selectionchange" : true,
23661         /**
23662              * @event afterselectionchange
23663              * Fires after the selection changes (eg. by key press or clicking)
23664              * @param {SelectionModel} this
23665              */
23666             "afterselectionchange" : true,
23667         /**
23668              * @event beforerowselect
23669              * Fires when a row is selected being selected, return false to cancel.
23670              * @param {SelectionModel} this
23671              * @param {Number} rowIndex The selected index
23672              * @param {Boolean} keepExisting False if other selections will be cleared
23673              */
23674             "beforerowselect" : true,
23675         /**
23676              * @event rowselect
23677              * Fires when a row is selected.
23678              * @param {SelectionModel} this
23679              * @param {Number} rowIndex The selected index
23680              * @param {Roo.data.Record} r The record
23681              */
23682             "rowselect" : true,
23683         /**
23684              * @event rowdeselect
23685              * Fires when a row is deselected.
23686              * @param {SelectionModel} this
23687              * @param {Number} rowIndex The selected index
23688              */
23689         "rowdeselect" : true
23690     });
23691     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23692     this.locked = false;
23693  };
23694
23695 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23696     /**
23697      * @cfg {Boolean} singleSelect
23698      * True to allow selection of only one row at a time (defaults to false)
23699      */
23700     singleSelect : false,
23701
23702     // private
23703     initEvents : function()
23704     {
23705
23706         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23707         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23708         //}else{ // allow click to work like normal
23709          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23710         //}
23711         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23712         this.grid.on("rowclick", this.handleMouseDown, this);
23713         
23714         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23715             "up" : function(e){
23716                 if(!e.shiftKey){
23717                     this.selectPrevious(e.shiftKey);
23718                 }else if(this.last !== false && this.lastActive !== false){
23719                     var last = this.last;
23720                     this.selectRange(this.last,  this.lastActive-1);
23721                     this.grid.getView().focusRow(this.lastActive);
23722                     if(last !== false){
23723                         this.last = last;
23724                     }
23725                 }else{
23726                     this.selectFirstRow();
23727                 }
23728                 this.fireEvent("afterselectionchange", this);
23729             },
23730             "down" : function(e){
23731                 if(!e.shiftKey){
23732                     this.selectNext(e.shiftKey);
23733                 }else if(this.last !== false && this.lastActive !== false){
23734                     var last = this.last;
23735                     this.selectRange(this.last,  this.lastActive+1);
23736                     this.grid.getView().focusRow(this.lastActive);
23737                     if(last !== false){
23738                         this.last = last;
23739                     }
23740                 }else{
23741                     this.selectFirstRow();
23742                 }
23743                 this.fireEvent("afterselectionchange", this);
23744             },
23745             scope: this
23746         });
23747         this.grid.store.on('load', function(){
23748             this.selections.clear();
23749         },this);
23750         /*
23751         var view = this.grid.view;
23752         view.on("refresh", this.onRefresh, this);
23753         view.on("rowupdated", this.onRowUpdated, this);
23754         view.on("rowremoved", this.onRemove, this);
23755         */
23756     },
23757
23758     // private
23759     onRefresh : function()
23760     {
23761         var ds = this.grid.store, i, v = this.grid.view;
23762         var s = this.selections;
23763         s.each(function(r){
23764             if((i = ds.indexOfId(r.id)) != -1){
23765                 v.onRowSelect(i);
23766             }else{
23767                 s.remove(r);
23768             }
23769         });
23770     },
23771
23772     // private
23773     onRemove : function(v, index, r){
23774         this.selections.remove(r);
23775     },
23776
23777     // private
23778     onRowUpdated : function(v, index, r){
23779         if(this.isSelected(r)){
23780             v.onRowSelect(index);
23781         }
23782     },
23783
23784     /**
23785      * Select records.
23786      * @param {Array} records The records to select
23787      * @param {Boolean} keepExisting (optional) True to keep existing selections
23788      */
23789     selectRecords : function(records, keepExisting)
23790     {
23791         if(!keepExisting){
23792             this.clearSelections();
23793         }
23794             var ds = this.grid.store;
23795         for(var i = 0, len = records.length; i < len; i++){
23796             this.selectRow(ds.indexOf(records[i]), true);
23797         }
23798     },
23799
23800     /**
23801      * Gets the number of selected rows.
23802      * @return {Number}
23803      */
23804     getCount : function(){
23805         return this.selections.length;
23806     },
23807
23808     /**
23809      * Selects the first row in the grid.
23810      */
23811     selectFirstRow : function(){
23812         this.selectRow(0);
23813     },
23814
23815     /**
23816      * Select the last row.
23817      * @param {Boolean} keepExisting (optional) True to keep existing selections
23818      */
23819     selectLastRow : function(keepExisting){
23820         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23821         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23822     },
23823
23824     /**
23825      * Selects the row immediately following the last selected row.
23826      * @param {Boolean} keepExisting (optional) True to keep existing selections
23827      */
23828     selectNext : function(keepExisting)
23829     {
23830             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23831             this.selectRow(this.last+1, keepExisting);
23832             this.grid.getView().focusRow(this.last);
23833         }
23834     },
23835
23836     /**
23837      * Selects the row that precedes the last selected row.
23838      * @param {Boolean} keepExisting (optional) True to keep existing selections
23839      */
23840     selectPrevious : function(keepExisting){
23841         if(this.last){
23842             this.selectRow(this.last-1, keepExisting);
23843             this.grid.getView().focusRow(this.last);
23844         }
23845     },
23846
23847     /**
23848      * Returns the selected records
23849      * @return {Array} Array of selected records
23850      */
23851     getSelections : function(){
23852         return [].concat(this.selections.items);
23853     },
23854
23855     /**
23856      * Returns the first selected record.
23857      * @return {Record}
23858      */
23859     getSelected : function(){
23860         return this.selections.itemAt(0);
23861     },
23862
23863
23864     /**
23865      * Clears all selections.
23866      */
23867     clearSelections : function(fast)
23868     {
23869         if(this.locked) {
23870             return;
23871         }
23872         if(fast !== true){
23873                 var ds = this.grid.store;
23874             var s = this.selections;
23875             s.each(function(r){
23876                 this.deselectRow(ds.indexOfId(r.id));
23877             }, this);
23878             s.clear();
23879         }else{
23880             this.selections.clear();
23881         }
23882         this.last = false;
23883     },
23884
23885
23886     /**
23887      * Selects all rows.
23888      */
23889     selectAll : function(){
23890         if(this.locked) {
23891             return;
23892         }
23893         this.selections.clear();
23894         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23895             this.selectRow(i, true);
23896         }
23897     },
23898
23899     /**
23900      * Returns True if there is a selection.
23901      * @return {Boolean}
23902      */
23903     hasSelection : function(){
23904         return this.selections.length > 0;
23905     },
23906
23907     /**
23908      * Returns True if the specified row is selected.
23909      * @param {Number/Record} record The record or index of the record to check
23910      * @return {Boolean}
23911      */
23912     isSelected : function(index){
23913             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23914         return (r && this.selections.key(r.id) ? true : false);
23915     },
23916
23917     /**
23918      * Returns True if the specified record id is selected.
23919      * @param {String} id The id of record to check
23920      * @return {Boolean}
23921      */
23922     isIdSelected : function(id){
23923         return (this.selections.key(id) ? true : false);
23924     },
23925
23926
23927     // private
23928     handleMouseDBClick : function(e, t){
23929         
23930     },
23931     // private
23932     handleMouseDown : function(e, t)
23933     {
23934             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23935         if(this.isLocked() || rowIndex < 0 ){
23936             return;
23937         };
23938         if(e.shiftKey && this.last !== false){
23939             var last = this.last;
23940             this.selectRange(last, rowIndex, e.ctrlKey);
23941             this.last = last; // reset the last
23942             t.focus();
23943     
23944         }else{
23945             var isSelected = this.isSelected(rowIndex);
23946             //Roo.log("select row:" + rowIndex);
23947             if(isSelected){
23948                 this.deselectRow(rowIndex);
23949             } else {
23950                         this.selectRow(rowIndex, true);
23951             }
23952     
23953             /*
23954                 if(e.button !== 0 && isSelected){
23955                 alert('rowIndex 2: ' + rowIndex);
23956                     view.focusRow(rowIndex);
23957                 }else if(e.ctrlKey && isSelected){
23958                     this.deselectRow(rowIndex);
23959                 }else if(!isSelected){
23960                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23961                     view.focusRow(rowIndex);
23962                 }
23963             */
23964         }
23965         this.fireEvent("afterselectionchange", this);
23966     },
23967     // private
23968     handleDragableRowClick :  function(grid, rowIndex, e) 
23969     {
23970         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23971             this.selectRow(rowIndex, false);
23972             grid.view.focusRow(rowIndex);
23973              this.fireEvent("afterselectionchange", this);
23974         }
23975     },
23976     
23977     /**
23978      * Selects multiple rows.
23979      * @param {Array} rows Array of the indexes of the row to select
23980      * @param {Boolean} keepExisting (optional) True to keep existing selections
23981      */
23982     selectRows : function(rows, keepExisting){
23983         if(!keepExisting){
23984             this.clearSelections();
23985         }
23986         for(var i = 0, len = rows.length; i < len; i++){
23987             this.selectRow(rows[i], true);
23988         }
23989     },
23990
23991     /**
23992      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23993      * @param {Number} startRow The index of the first row in the range
23994      * @param {Number} endRow The index of the last row in the range
23995      * @param {Boolean} keepExisting (optional) True to retain existing selections
23996      */
23997     selectRange : function(startRow, endRow, keepExisting){
23998         if(this.locked) {
23999             return;
24000         }
24001         if(!keepExisting){
24002             this.clearSelections();
24003         }
24004         if(startRow <= endRow){
24005             for(var i = startRow; i <= endRow; i++){
24006                 this.selectRow(i, true);
24007             }
24008         }else{
24009             for(var i = startRow; i >= endRow; i--){
24010                 this.selectRow(i, true);
24011             }
24012         }
24013     },
24014
24015     /**
24016      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24017      * @param {Number} startRow The index of the first row in the range
24018      * @param {Number} endRow The index of the last row in the range
24019      */
24020     deselectRange : function(startRow, endRow, preventViewNotify){
24021         if(this.locked) {
24022             return;
24023         }
24024         for(var i = startRow; i <= endRow; i++){
24025             this.deselectRow(i, preventViewNotify);
24026         }
24027     },
24028
24029     /**
24030      * Selects a row.
24031      * @param {Number} row The index of the row to select
24032      * @param {Boolean} keepExisting (optional) True to keep existing selections
24033      */
24034     selectRow : function(index, keepExisting, preventViewNotify)
24035     {
24036             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24037             return;
24038         }
24039         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24040             if(!keepExisting || this.singleSelect){
24041                 this.clearSelections();
24042             }
24043             
24044             var r = this.grid.store.getAt(index);
24045             //console.log('selectRow - record id :' + r.id);
24046             
24047             this.selections.add(r);
24048             this.last = this.lastActive = index;
24049             if(!preventViewNotify){
24050                 var proxy = new Roo.Element(
24051                                 this.grid.getRowDom(index)
24052                 );
24053                 proxy.addClass('bg-info info');
24054             }
24055             this.fireEvent("rowselect", this, index, r);
24056             this.fireEvent("selectionchange", this);
24057         }
24058     },
24059
24060     /**
24061      * Deselects a row.
24062      * @param {Number} row The index of the row to deselect
24063      */
24064     deselectRow : function(index, preventViewNotify)
24065     {
24066         if(this.locked) {
24067             return;
24068         }
24069         if(this.last == index){
24070             this.last = false;
24071         }
24072         if(this.lastActive == index){
24073             this.lastActive = false;
24074         }
24075         
24076         var r = this.grid.store.getAt(index);
24077         if (!r) {
24078             return;
24079         }
24080         
24081         this.selections.remove(r);
24082         //.console.log('deselectRow - record id :' + r.id);
24083         if(!preventViewNotify){
24084         
24085             var proxy = new Roo.Element(
24086                 this.grid.getRowDom(index)
24087             );
24088             proxy.removeClass('bg-info info');
24089         }
24090         this.fireEvent("rowdeselect", this, index);
24091         this.fireEvent("selectionchange", this);
24092     },
24093
24094     // private
24095     restoreLast : function(){
24096         if(this._last){
24097             this.last = this._last;
24098         }
24099     },
24100
24101     // private
24102     acceptsNav : function(row, col, cm){
24103         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24104     },
24105
24106     // private
24107     onEditorKey : function(field, e){
24108         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24109         if(k == e.TAB){
24110             e.stopEvent();
24111             ed.completeEdit();
24112             if(e.shiftKey){
24113                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24114             }else{
24115                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24116             }
24117         }else if(k == e.ENTER && !e.ctrlKey){
24118             e.stopEvent();
24119             ed.completeEdit();
24120             if(e.shiftKey){
24121                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24122             }else{
24123                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24124             }
24125         }else if(k == e.ESC){
24126             ed.cancelEdit();
24127         }
24128         if(newCell){
24129             g.startEditing(newCell[0], newCell[1]);
24130         }
24131     }
24132 });
24133 /*
24134  * Based on:
24135  * Ext JS Library 1.1.1
24136  * Copyright(c) 2006-2007, Ext JS, LLC.
24137  *
24138  * Originally Released Under LGPL - original licence link has changed is not relivant.
24139  *
24140  * Fork - LGPL
24141  * <script type="text/javascript">
24142  */
24143  
24144 /**
24145  * @class Roo.bootstrap.PagingToolbar
24146  * @extends Roo.bootstrap.NavSimplebar
24147  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24148  * @constructor
24149  * Create a new PagingToolbar
24150  * @param {Object} config The config object
24151  * @param {Roo.data.Store} store
24152  */
24153 Roo.bootstrap.PagingToolbar = function(config)
24154 {
24155     // old args format still supported... - xtype is prefered..
24156         // created from xtype...
24157     
24158     this.ds = config.dataSource;
24159     
24160     if (config.store && !this.ds) {
24161         this.store= Roo.factory(config.store, Roo.data);
24162         this.ds = this.store;
24163         this.ds.xmodule = this.xmodule || false;
24164     }
24165     
24166     this.toolbarItems = [];
24167     if (config.items) {
24168         this.toolbarItems = config.items;
24169     }
24170     
24171     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24172     
24173     this.cursor = 0;
24174     
24175     if (this.ds) { 
24176         this.bind(this.ds);
24177     }
24178     
24179     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24180     
24181 };
24182
24183 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24184     /**
24185      * @cfg {Roo.data.Store} dataSource
24186      * The underlying data store providing the paged data
24187      */
24188     /**
24189      * @cfg {String/HTMLElement/Element} container
24190      * container The id or element that will contain the toolbar
24191      */
24192     /**
24193      * @cfg {Boolean} displayInfo
24194      * True to display the displayMsg (defaults to false)
24195      */
24196     /**
24197      * @cfg {Number} pageSize
24198      * The number of records to display per page (defaults to 20)
24199      */
24200     pageSize: 20,
24201     /**
24202      * @cfg {String} displayMsg
24203      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24204      */
24205     displayMsg : 'Displaying {0} - {1} of {2}',
24206     /**
24207      * @cfg {String} emptyMsg
24208      * The message to display when no records are found (defaults to "No data to display")
24209      */
24210     emptyMsg : 'No data to display',
24211     /**
24212      * Customizable piece of the default paging text (defaults to "Page")
24213      * @type String
24214      */
24215     beforePageText : "Page",
24216     /**
24217      * Customizable piece of the default paging text (defaults to "of %0")
24218      * @type String
24219      */
24220     afterPageText : "of {0}",
24221     /**
24222      * Customizable piece of the default paging text (defaults to "First Page")
24223      * @type String
24224      */
24225     firstText : "First Page",
24226     /**
24227      * Customizable piece of the default paging text (defaults to "Previous Page")
24228      * @type String
24229      */
24230     prevText : "Previous Page",
24231     /**
24232      * Customizable piece of the default paging text (defaults to "Next Page")
24233      * @type String
24234      */
24235     nextText : "Next Page",
24236     /**
24237      * Customizable piece of the default paging text (defaults to "Last Page")
24238      * @type String
24239      */
24240     lastText : "Last Page",
24241     /**
24242      * Customizable piece of the default paging text (defaults to "Refresh")
24243      * @type String
24244      */
24245     refreshText : "Refresh",
24246
24247     buttons : false,
24248     // private
24249     onRender : function(ct, position) 
24250     {
24251         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24252         this.navgroup.parentId = this.id;
24253         this.navgroup.onRender(this.el, null);
24254         // add the buttons to the navgroup
24255         
24256         if(this.displayInfo){
24257             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24258             this.displayEl = this.el.select('.x-paging-info', true).first();
24259 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24260 //            this.displayEl = navel.el.select('span',true).first();
24261         }
24262         
24263         var _this = this;
24264         
24265         if(this.buttons){
24266             Roo.each(_this.buttons, function(e){ // this might need to use render????
24267                Roo.factory(e).onRender(_this.el, null);
24268             });
24269         }
24270             
24271         Roo.each(_this.toolbarItems, function(e) {
24272             _this.navgroup.addItem(e);
24273         });
24274         
24275         
24276         this.first = this.navgroup.addItem({
24277             tooltip: this.firstText,
24278             cls: "prev",
24279             icon : 'fa fa-backward',
24280             disabled: true,
24281             preventDefault: true,
24282             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24283         });
24284         
24285         this.prev =  this.navgroup.addItem({
24286             tooltip: this.prevText,
24287             cls: "prev",
24288             icon : 'fa fa-step-backward',
24289             disabled: true,
24290             preventDefault: true,
24291             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24292         });
24293     //this.addSeparator();
24294         
24295         
24296         var field = this.navgroup.addItem( {
24297             tagtype : 'span',
24298             cls : 'x-paging-position',
24299             
24300             html : this.beforePageText  +
24301                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24302                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24303          } ); //?? escaped?
24304         
24305         this.field = field.el.select('input', true).first();
24306         this.field.on("keydown", this.onPagingKeydown, this);
24307         this.field.on("focus", function(){this.dom.select();});
24308     
24309     
24310         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24311         //this.field.setHeight(18);
24312         //this.addSeparator();
24313         this.next = this.navgroup.addItem({
24314             tooltip: this.nextText,
24315             cls: "next",
24316             html : ' <i class="fa fa-step-forward">',
24317             disabled: true,
24318             preventDefault: true,
24319             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24320         });
24321         this.last = this.navgroup.addItem({
24322             tooltip: this.lastText,
24323             icon : 'fa fa-forward',
24324             cls: "next",
24325             disabled: true,
24326             preventDefault: true,
24327             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24328         });
24329     //this.addSeparator();
24330         this.loading = this.navgroup.addItem({
24331             tooltip: this.refreshText,
24332             icon: 'fa fa-refresh',
24333             preventDefault: true,
24334             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24335         });
24336         
24337     },
24338
24339     // private
24340     updateInfo : function(){
24341         if(this.displayEl){
24342             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24343             var msg = count == 0 ?
24344                 this.emptyMsg :
24345                 String.format(
24346                     this.displayMsg,
24347                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24348                 );
24349             this.displayEl.update(msg);
24350         }
24351     },
24352
24353     // private
24354     onLoad : function(ds, r, o)
24355     {
24356         this.cursor = o.params ? o.params.start : 0;
24357         var d = this.getPageData(),
24358             ap = d.activePage,
24359             ps = d.pages;
24360         
24361         
24362         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24363         this.field.dom.value = ap;
24364         this.first.setDisabled(ap == 1);
24365         this.prev.setDisabled(ap == 1);
24366         this.next.setDisabled(ap == ps);
24367         this.last.setDisabled(ap == ps);
24368         this.loading.enable();
24369         this.updateInfo();
24370     },
24371
24372     // private
24373     getPageData : function(){
24374         var total = this.ds.getTotalCount();
24375         return {
24376             total : total,
24377             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24378             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24379         };
24380     },
24381
24382     // private
24383     onLoadError : function(){
24384         this.loading.enable();
24385     },
24386
24387     // private
24388     onPagingKeydown : function(e){
24389         var k = e.getKey();
24390         var d = this.getPageData();
24391         if(k == e.RETURN){
24392             var v = this.field.dom.value, pageNum;
24393             if(!v || isNaN(pageNum = parseInt(v, 10))){
24394                 this.field.dom.value = d.activePage;
24395                 return;
24396             }
24397             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24398             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24399             e.stopEvent();
24400         }
24401         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))
24402         {
24403           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24404           this.field.dom.value = pageNum;
24405           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24406           e.stopEvent();
24407         }
24408         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24409         {
24410           var v = this.field.dom.value, pageNum; 
24411           var increment = (e.shiftKey) ? 10 : 1;
24412           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24413                 increment *= -1;
24414           }
24415           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24416             this.field.dom.value = d.activePage;
24417             return;
24418           }
24419           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24420           {
24421             this.field.dom.value = parseInt(v, 10) + increment;
24422             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24423             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24424           }
24425           e.stopEvent();
24426         }
24427     },
24428
24429     // private
24430     beforeLoad : function(){
24431         if(this.loading){
24432             this.loading.disable();
24433         }
24434     },
24435
24436     // private
24437     onClick : function(which){
24438         
24439         var ds = this.ds;
24440         if (!ds) {
24441             return;
24442         }
24443         
24444         switch(which){
24445             case "first":
24446                 ds.load({params:{start: 0, limit: this.pageSize}});
24447             break;
24448             case "prev":
24449                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24450             break;
24451             case "next":
24452                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24453             break;
24454             case "last":
24455                 var total = ds.getTotalCount();
24456                 var extra = total % this.pageSize;
24457                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24458                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24459             break;
24460             case "refresh":
24461                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24462             break;
24463         }
24464     },
24465
24466     /**
24467      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24468      * @param {Roo.data.Store} store The data store to unbind
24469      */
24470     unbind : function(ds){
24471         ds.un("beforeload", this.beforeLoad, this);
24472         ds.un("load", this.onLoad, this);
24473         ds.un("loadexception", this.onLoadError, this);
24474         ds.un("remove", this.updateInfo, this);
24475         ds.un("add", this.updateInfo, this);
24476         this.ds = undefined;
24477     },
24478
24479     /**
24480      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24481      * @param {Roo.data.Store} store The data store to bind
24482      */
24483     bind : function(ds){
24484         ds.on("beforeload", this.beforeLoad, this);
24485         ds.on("load", this.onLoad, this);
24486         ds.on("loadexception", this.onLoadError, this);
24487         ds.on("remove", this.updateInfo, this);
24488         ds.on("add", this.updateInfo, this);
24489         this.ds = ds;
24490     }
24491 });/*
24492  * - LGPL
24493  *
24494  * element
24495  * 
24496  */
24497
24498 /**
24499  * @class Roo.bootstrap.MessageBar
24500  * @extends Roo.bootstrap.Component
24501  * Bootstrap MessageBar class
24502  * @cfg {String} html contents of the MessageBar
24503  * @cfg {String} weight (info | success | warning | danger) default info
24504  * @cfg {String} beforeClass insert the bar before the given class
24505  * @cfg {Boolean} closable (true | false) default false
24506  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24507  * 
24508  * @constructor
24509  * Create a new Element
24510  * @param {Object} config The config object
24511  */
24512
24513 Roo.bootstrap.MessageBar = function(config){
24514     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24515 };
24516
24517 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24518     
24519     html: '',
24520     weight: 'info',
24521     closable: false,
24522     fixed: false,
24523     beforeClass: 'bootstrap-sticky-wrap',
24524     
24525     getAutoCreate : function(){
24526         
24527         var cfg = {
24528             tag: 'div',
24529             cls: 'alert alert-dismissable alert-' + this.weight,
24530             cn: [
24531                 {
24532                     tag: 'span',
24533                     cls: 'message',
24534                     html: this.html || ''
24535                 }
24536             ]
24537         };
24538         
24539         if(this.fixed){
24540             cfg.cls += ' alert-messages-fixed';
24541         }
24542         
24543         if(this.closable){
24544             cfg.cn.push({
24545                 tag: 'button',
24546                 cls: 'close',
24547                 html: 'x'
24548             });
24549         }
24550         
24551         return cfg;
24552     },
24553     
24554     onRender : function(ct, position)
24555     {
24556         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24557         
24558         if(!this.el){
24559             var cfg = Roo.apply({},  this.getAutoCreate());
24560             cfg.id = Roo.id();
24561             
24562             if (this.cls) {
24563                 cfg.cls += ' ' + this.cls;
24564             }
24565             if (this.style) {
24566                 cfg.style = this.style;
24567             }
24568             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24569             
24570             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24571         }
24572         
24573         this.el.select('>button.close').on('click', this.hide, this);
24574         
24575     },
24576     
24577     show : function()
24578     {
24579         if (!this.rendered) {
24580             this.render();
24581         }
24582         
24583         this.el.show();
24584         
24585         this.fireEvent('show', this);
24586         
24587     },
24588     
24589     hide : function()
24590     {
24591         if (!this.rendered) {
24592             this.render();
24593         }
24594         
24595         this.el.hide();
24596         
24597         this.fireEvent('hide', this);
24598     },
24599     
24600     update : function()
24601     {
24602 //        var e = this.el.dom.firstChild;
24603 //        
24604 //        if(this.closable){
24605 //            e = e.nextSibling;
24606 //        }
24607 //        
24608 //        e.data = this.html || '';
24609
24610         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24611     }
24612    
24613 });
24614
24615  
24616
24617      /*
24618  * - LGPL
24619  *
24620  * Graph
24621  * 
24622  */
24623
24624
24625 /**
24626  * @class Roo.bootstrap.Graph
24627  * @extends Roo.bootstrap.Component
24628  * Bootstrap Graph class
24629 > Prameters
24630  -sm {number} sm 4
24631  -md {number} md 5
24632  @cfg {String} graphtype  bar | vbar | pie
24633  @cfg {number} g_x coodinator | centre x (pie)
24634  @cfg {number} g_y coodinator | centre y (pie)
24635  @cfg {number} g_r radius (pie)
24636  @cfg {number} g_height height of the chart (respected by all elements in the set)
24637  @cfg {number} g_width width of the chart (respected by all elements in the set)
24638  @cfg {Object} title The title of the chart
24639     
24640  -{Array}  values
24641  -opts (object) options for the chart 
24642      o {
24643      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24644      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24645      o vgutter (number)
24646      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.
24647      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24648      o to
24649      o stretch (boolean)
24650      o }
24651  -opts (object) options for the pie
24652      o{
24653      o cut
24654      o startAngle (number)
24655      o endAngle (number)
24656      } 
24657  *
24658  * @constructor
24659  * Create a new Input
24660  * @param {Object} config The config object
24661  */
24662
24663 Roo.bootstrap.Graph = function(config){
24664     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24665     
24666     this.addEvents({
24667         // img events
24668         /**
24669          * @event click
24670          * The img click event for the img.
24671          * @param {Roo.EventObject} e
24672          */
24673         "click" : true
24674     });
24675 };
24676
24677 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24678     
24679     sm: 4,
24680     md: 5,
24681     graphtype: 'bar',
24682     g_height: 250,
24683     g_width: 400,
24684     g_x: 50,
24685     g_y: 50,
24686     g_r: 30,
24687     opts:{
24688         //g_colors: this.colors,
24689         g_type: 'soft',
24690         g_gutter: '20%'
24691
24692     },
24693     title : false,
24694
24695     getAutoCreate : function(){
24696         
24697         var cfg = {
24698             tag: 'div',
24699             html : null
24700         };
24701         
24702         
24703         return  cfg;
24704     },
24705
24706     onRender : function(ct,position){
24707         
24708         
24709         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24710         
24711         if (typeof(Raphael) == 'undefined') {
24712             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24713             return;
24714         }
24715         
24716         this.raphael = Raphael(this.el.dom);
24717         
24718                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24719                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24720                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24721                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24722                 /*
24723                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24724                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24725                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24726                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24727                 
24728                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24729                 r.barchart(330, 10, 300, 220, data1);
24730                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24731                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24732                 */
24733                 
24734                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24735                 // r.barchart(30, 30, 560, 250,  xdata, {
24736                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24737                 //     axis : "0 0 1 1",
24738                 //     axisxlabels :  xdata
24739                 //     //yvalues : cols,
24740                    
24741                 // });
24742 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24743 //        
24744 //        this.load(null,xdata,{
24745 //                axis : "0 0 1 1",
24746 //                axisxlabels :  xdata
24747 //                });
24748
24749     },
24750
24751     load : function(graphtype,xdata,opts)
24752     {
24753         this.raphael.clear();
24754         if(!graphtype) {
24755             graphtype = this.graphtype;
24756         }
24757         if(!opts){
24758             opts = this.opts;
24759         }
24760         var r = this.raphael,
24761             fin = function () {
24762                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24763             },
24764             fout = function () {
24765                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24766             },
24767             pfin = function() {
24768                 this.sector.stop();
24769                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24770
24771                 if (this.label) {
24772                     this.label[0].stop();
24773                     this.label[0].attr({ r: 7.5 });
24774                     this.label[1].attr({ "font-weight": 800 });
24775                 }
24776             },
24777             pfout = function() {
24778                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24779
24780                 if (this.label) {
24781                     this.label[0].animate({ r: 5 }, 500, "bounce");
24782                     this.label[1].attr({ "font-weight": 400 });
24783                 }
24784             };
24785
24786         switch(graphtype){
24787             case 'bar':
24788                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24789                 break;
24790             case 'hbar':
24791                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24792                 break;
24793             case 'pie':
24794 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24795 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24796 //            
24797                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24798                 
24799                 break;
24800
24801         }
24802         
24803         if(this.title){
24804             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24805         }
24806         
24807     },
24808     
24809     setTitle: function(o)
24810     {
24811         this.title = o;
24812     },
24813     
24814     initEvents: function() {
24815         
24816         if(!this.href){
24817             this.el.on('click', this.onClick, this);
24818         }
24819     },
24820     
24821     onClick : function(e)
24822     {
24823         Roo.log('img onclick');
24824         this.fireEvent('click', this, e);
24825     }
24826    
24827 });
24828
24829  
24830 /*
24831  * - LGPL
24832  *
24833  * numberBox
24834  * 
24835  */
24836 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24837
24838 /**
24839  * @class Roo.bootstrap.dash.NumberBox
24840  * @extends Roo.bootstrap.Component
24841  * Bootstrap NumberBox class
24842  * @cfg {String} headline Box headline
24843  * @cfg {String} content Box content
24844  * @cfg {String} icon Box icon
24845  * @cfg {String} footer Footer text
24846  * @cfg {String} fhref Footer href
24847  * 
24848  * @constructor
24849  * Create a new NumberBox
24850  * @param {Object} config The config object
24851  */
24852
24853
24854 Roo.bootstrap.dash.NumberBox = function(config){
24855     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24856     
24857 };
24858
24859 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24860     
24861     headline : '',
24862     content : '',
24863     icon : '',
24864     footer : '',
24865     fhref : '',
24866     ficon : '',
24867     
24868     getAutoCreate : function(){
24869         
24870         var cfg = {
24871             tag : 'div',
24872             cls : 'small-box ',
24873             cn : [
24874                 {
24875                     tag : 'div',
24876                     cls : 'inner',
24877                     cn :[
24878                         {
24879                             tag : 'h3',
24880                             cls : 'roo-headline',
24881                             html : this.headline
24882                         },
24883                         {
24884                             tag : 'p',
24885                             cls : 'roo-content',
24886                             html : this.content
24887                         }
24888                     ]
24889                 }
24890             ]
24891         };
24892         
24893         if(this.icon){
24894             cfg.cn.push({
24895                 tag : 'div',
24896                 cls : 'icon',
24897                 cn :[
24898                     {
24899                         tag : 'i',
24900                         cls : 'ion ' + this.icon
24901                     }
24902                 ]
24903             });
24904         }
24905         
24906         if(this.footer){
24907             var footer = {
24908                 tag : 'a',
24909                 cls : 'small-box-footer',
24910                 href : this.fhref || '#',
24911                 html : this.footer
24912             };
24913             
24914             cfg.cn.push(footer);
24915             
24916         }
24917         
24918         return  cfg;
24919     },
24920
24921     onRender : function(ct,position){
24922         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24923
24924
24925        
24926                 
24927     },
24928
24929     setHeadline: function (value)
24930     {
24931         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24932     },
24933     
24934     setFooter: function (value, href)
24935     {
24936         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24937         
24938         if(href){
24939             this.el.select('a.small-box-footer',true).first().attr('href', href);
24940         }
24941         
24942     },
24943
24944     setContent: function (value)
24945     {
24946         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24947     },
24948
24949     initEvents: function() 
24950     {   
24951         
24952     }
24953     
24954 });
24955
24956  
24957 /*
24958  * - LGPL
24959  *
24960  * TabBox
24961  * 
24962  */
24963 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24964
24965 /**
24966  * @class Roo.bootstrap.dash.TabBox
24967  * @extends Roo.bootstrap.Component
24968  * Bootstrap TabBox class
24969  * @cfg {String} title Title of the TabBox
24970  * @cfg {String} icon Icon of the TabBox
24971  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24972  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24973  * 
24974  * @constructor
24975  * Create a new TabBox
24976  * @param {Object} config The config object
24977  */
24978
24979
24980 Roo.bootstrap.dash.TabBox = function(config){
24981     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24982     this.addEvents({
24983         // raw events
24984         /**
24985          * @event addpane
24986          * When a pane is added
24987          * @param {Roo.bootstrap.dash.TabPane} pane
24988          */
24989         "addpane" : true,
24990         /**
24991          * @event activatepane
24992          * When a pane is activated
24993          * @param {Roo.bootstrap.dash.TabPane} pane
24994          */
24995         "activatepane" : true
24996         
24997          
24998     });
24999     
25000     this.panes = [];
25001 };
25002
25003 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25004
25005     title : '',
25006     icon : false,
25007     showtabs : true,
25008     tabScrollable : false,
25009     
25010     getChildContainer : function()
25011     {
25012         return this.el.select('.tab-content', true).first();
25013     },
25014     
25015     getAutoCreate : function(){
25016         
25017         var header = {
25018             tag: 'li',
25019             cls: 'pull-left header',
25020             html: this.title,
25021             cn : []
25022         };
25023         
25024         if(this.icon){
25025             header.cn.push({
25026                 tag: 'i',
25027                 cls: 'fa ' + this.icon
25028             });
25029         }
25030         
25031         var h = {
25032             tag: 'ul',
25033             cls: 'nav nav-tabs pull-right',
25034             cn: [
25035                 header
25036             ]
25037         };
25038         
25039         if(this.tabScrollable){
25040             h = {
25041                 tag: 'div',
25042                 cls: 'tab-header',
25043                 cn: [
25044                     {
25045                         tag: 'ul',
25046                         cls: 'nav nav-tabs pull-right',
25047                         cn: [
25048                             header
25049                         ]
25050                     }
25051                 ]
25052             };
25053         }
25054         
25055         var cfg = {
25056             tag: 'div',
25057             cls: 'nav-tabs-custom',
25058             cn: [
25059                 h,
25060                 {
25061                     tag: 'div',
25062                     cls: 'tab-content no-padding',
25063                     cn: []
25064                 }
25065             ]
25066         };
25067
25068         return  cfg;
25069     },
25070     initEvents : function()
25071     {
25072         //Roo.log('add add pane handler');
25073         this.on('addpane', this.onAddPane, this);
25074     },
25075      /**
25076      * Updates the box title
25077      * @param {String} html to set the title to.
25078      */
25079     setTitle : function(value)
25080     {
25081         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25082     },
25083     onAddPane : function(pane)
25084     {
25085         this.panes.push(pane);
25086         //Roo.log('addpane');
25087         //Roo.log(pane);
25088         // tabs are rendere left to right..
25089         if(!this.showtabs){
25090             return;
25091         }
25092         
25093         var ctr = this.el.select('.nav-tabs', true).first();
25094          
25095          
25096         var existing = ctr.select('.nav-tab',true);
25097         var qty = existing.getCount();;
25098         
25099         
25100         var tab = ctr.createChild({
25101             tag : 'li',
25102             cls : 'nav-tab' + (qty ? '' : ' active'),
25103             cn : [
25104                 {
25105                     tag : 'a',
25106                     href:'#',
25107                     html : pane.title
25108                 }
25109             ]
25110         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25111         pane.tab = tab;
25112         
25113         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25114         if (!qty) {
25115             pane.el.addClass('active');
25116         }
25117         
25118                 
25119     },
25120     onTabClick : function(ev,un,ob,pane)
25121     {
25122         //Roo.log('tab - prev default');
25123         ev.preventDefault();
25124         
25125         
25126         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25127         pane.tab.addClass('active');
25128         //Roo.log(pane.title);
25129         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25130         // technically we should have a deactivate event.. but maybe add later.
25131         // and it should not de-activate the selected tab...
25132         this.fireEvent('activatepane', pane);
25133         pane.el.addClass('active');
25134         pane.fireEvent('activate');
25135         
25136         
25137     },
25138     
25139     getActivePane : function()
25140     {
25141         var r = false;
25142         Roo.each(this.panes, function(p) {
25143             if(p.el.hasClass('active')){
25144                 r = p;
25145                 return false;
25146             }
25147             
25148             return;
25149         });
25150         
25151         return r;
25152     }
25153     
25154     
25155 });
25156
25157  
25158 /*
25159  * - LGPL
25160  *
25161  * Tab pane
25162  * 
25163  */
25164 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25165 /**
25166  * @class Roo.bootstrap.TabPane
25167  * @extends Roo.bootstrap.Component
25168  * Bootstrap TabPane class
25169  * @cfg {Boolean} active (false | true) Default false
25170  * @cfg {String} title title of panel
25171
25172  * 
25173  * @constructor
25174  * Create a new TabPane
25175  * @param {Object} config The config object
25176  */
25177
25178 Roo.bootstrap.dash.TabPane = function(config){
25179     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25180     
25181     this.addEvents({
25182         // raw events
25183         /**
25184          * @event activate
25185          * When a pane is activated
25186          * @param {Roo.bootstrap.dash.TabPane} pane
25187          */
25188         "activate" : true
25189          
25190     });
25191 };
25192
25193 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25194     
25195     active : false,
25196     title : '',
25197     
25198     // the tabBox that this is attached to.
25199     tab : false,
25200      
25201     getAutoCreate : function() 
25202     {
25203         var cfg = {
25204             tag: 'div',
25205             cls: 'tab-pane'
25206         };
25207         
25208         if(this.active){
25209             cfg.cls += ' active';
25210         }
25211         
25212         return cfg;
25213     },
25214     initEvents  : function()
25215     {
25216         //Roo.log('trigger add pane handler');
25217         this.parent().fireEvent('addpane', this)
25218     },
25219     
25220      /**
25221      * Updates the tab title 
25222      * @param {String} html to set the title to.
25223      */
25224     setTitle: function(str)
25225     {
25226         if (!this.tab) {
25227             return;
25228         }
25229         this.title = str;
25230         this.tab.select('a', true).first().dom.innerHTML = str;
25231         
25232     }
25233     
25234     
25235     
25236 });
25237
25238  
25239
25240
25241  /*
25242  * - LGPL
25243  *
25244  * menu
25245  * 
25246  */
25247 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25248
25249 /**
25250  * @class Roo.bootstrap.menu.Menu
25251  * @extends Roo.bootstrap.Component
25252  * Bootstrap Menu class - container for Menu
25253  * @cfg {String} html Text of the menu
25254  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25255  * @cfg {String} icon Font awesome icon
25256  * @cfg {String} pos Menu align to (top | bottom) default bottom
25257  * 
25258  * 
25259  * @constructor
25260  * Create a new Menu
25261  * @param {Object} config The config object
25262  */
25263
25264
25265 Roo.bootstrap.menu.Menu = function(config){
25266     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25267     
25268     this.addEvents({
25269         /**
25270          * @event beforeshow
25271          * Fires before this menu is displayed
25272          * @param {Roo.bootstrap.menu.Menu} this
25273          */
25274         beforeshow : true,
25275         /**
25276          * @event beforehide
25277          * Fires before this menu is hidden
25278          * @param {Roo.bootstrap.menu.Menu} this
25279          */
25280         beforehide : true,
25281         /**
25282          * @event show
25283          * Fires after this menu is displayed
25284          * @param {Roo.bootstrap.menu.Menu} this
25285          */
25286         show : true,
25287         /**
25288          * @event hide
25289          * Fires after this menu is hidden
25290          * @param {Roo.bootstrap.menu.Menu} this
25291          */
25292         hide : true,
25293         /**
25294          * @event click
25295          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25296          * @param {Roo.bootstrap.menu.Menu} this
25297          * @param {Roo.EventObject} e
25298          */
25299         click : true
25300     });
25301     
25302 };
25303
25304 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25305     
25306     submenu : false,
25307     html : '',
25308     weight : 'default',
25309     icon : false,
25310     pos : 'bottom',
25311     
25312     
25313     getChildContainer : function() {
25314         if(this.isSubMenu){
25315             return this.el;
25316         }
25317         
25318         return this.el.select('ul.dropdown-menu', true).first();  
25319     },
25320     
25321     getAutoCreate : function()
25322     {
25323         var text = [
25324             {
25325                 tag : 'span',
25326                 cls : 'roo-menu-text',
25327                 html : this.html
25328             }
25329         ];
25330         
25331         if(this.icon){
25332             text.unshift({
25333                 tag : 'i',
25334                 cls : 'fa ' + this.icon
25335             })
25336         }
25337         
25338         
25339         var cfg = {
25340             tag : 'div',
25341             cls : 'btn-group',
25342             cn : [
25343                 {
25344                     tag : 'button',
25345                     cls : 'dropdown-button btn btn-' + this.weight,
25346                     cn : text
25347                 },
25348                 {
25349                     tag : 'button',
25350                     cls : 'dropdown-toggle btn btn-' + this.weight,
25351                     cn : [
25352                         {
25353                             tag : 'span',
25354                             cls : 'caret'
25355                         }
25356                     ]
25357                 },
25358                 {
25359                     tag : 'ul',
25360                     cls : 'dropdown-menu'
25361                 }
25362             ]
25363             
25364         };
25365         
25366         if(this.pos == 'top'){
25367             cfg.cls += ' dropup';
25368         }
25369         
25370         if(this.isSubMenu){
25371             cfg = {
25372                 tag : 'ul',
25373                 cls : 'dropdown-menu'
25374             }
25375         }
25376         
25377         return cfg;
25378     },
25379     
25380     onRender : function(ct, position)
25381     {
25382         this.isSubMenu = ct.hasClass('dropdown-submenu');
25383         
25384         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25385     },
25386     
25387     initEvents : function() 
25388     {
25389         if(this.isSubMenu){
25390             return;
25391         }
25392         
25393         this.hidden = true;
25394         
25395         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25396         this.triggerEl.on('click', this.onTriggerPress, this);
25397         
25398         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25399         this.buttonEl.on('click', this.onClick, this);
25400         
25401     },
25402     
25403     list : function()
25404     {
25405         if(this.isSubMenu){
25406             return this.el;
25407         }
25408         
25409         return this.el.select('ul.dropdown-menu', true).first();
25410     },
25411     
25412     onClick : function(e)
25413     {
25414         this.fireEvent("click", this, e);
25415     },
25416     
25417     onTriggerPress  : function(e)
25418     {   
25419         if (this.isVisible()) {
25420             this.hide();
25421         } else {
25422             this.show();
25423         }
25424     },
25425     
25426     isVisible : function(){
25427         return !this.hidden;
25428     },
25429     
25430     show : function()
25431     {
25432         this.fireEvent("beforeshow", this);
25433         
25434         this.hidden = false;
25435         this.el.addClass('open');
25436         
25437         Roo.get(document).on("mouseup", this.onMouseUp, this);
25438         
25439         this.fireEvent("show", this);
25440         
25441         
25442     },
25443     
25444     hide : function()
25445     {
25446         this.fireEvent("beforehide", this);
25447         
25448         this.hidden = true;
25449         this.el.removeClass('open');
25450         
25451         Roo.get(document).un("mouseup", this.onMouseUp);
25452         
25453         this.fireEvent("hide", this);
25454     },
25455     
25456     onMouseUp : function()
25457     {
25458         this.hide();
25459     }
25460     
25461 });
25462
25463  
25464  /*
25465  * - LGPL
25466  *
25467  * menu item
25468  * 
25469  */
25470 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25471
25472 /**
25473  * @class Roo.bootstrap.menu.Item
25474  * @extends Roo.bootstrap.Component
25475  * Bootstrap MenuItem class
25476  * @cfg {Boolean} submenu (true | false) default false
25477  * @cfg {String} html text of the item
25478  * @cfg {String} href the link
25479  * @cfg {Boolean} disable (true | false) default false
25480  * @cfg {Boolean} preventDefault (true | false) default true
25481  * @cfg {String} icon Font awesome icon
25482  * @cfg {String} pos Submenu align to (left | right) default right 
25483  * 
25484  * 
25485  * @constructor
25486  * Create a new Item
25487  * @param {Object} config The config object
25488  */
25489
25490
25491 Roo.bootstrap.menu.Item = function(config){
25492     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25493     this.addEvents({
25494         /**
25495          * @event mouseover
25496          * Fires when the mouse is hovering over this menu
25497          * @param {Roo.bootstrap.menu.Item} this
25498          * @param {Roo.EventObject} e
25499          */
25500         mouseover : true,
25501         /**
25502          * @event mouseout
25503          * Fires when the mouse exits this menu
25504          * @param {Roo.bootstrap.menu.Item} this
25505          * @param {Roo.EventObject} e
25506          */
25507         mouseout : true,
25508         // raw events
25509         /**
25510          * @event click
25511          * The raw click event for the entire grid.
25512          * @param {Roo.EventObject} e
25513          */
25514         click : true
25515     });
25516 };
25517
25518 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25519     
25520     submenu : false,
25521     href : '',
25522     html : '',
25523     preventDefault: true,
25524     disable : false,
25525     icon : false,
25526     pos : 'right',
25527     
25528     getAutoCreate : function()
25529     {
25530         var text = [
25531             {
25532                 tag : 'span',
25533                 cls : 'roo-menu-item-text',
25534                 html : this.html
25535             }
25536         ];
25537         
25538         if(this.icon){
25539             text.unshift({
25540                 tag : 'i',
25541                 cls : 'fa ' + this.icon
25542             })
25543         }
25544         
25545         var cfg = {
25546             tag : 'li',
25547             cn : [
25548                 {
25549                     tag : 'a',
25550                     href : this.href || '#',
25551                     cn : text
25552                 }
25553             ]
25554         };
25555         
25556         if(this.disable){
25557             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25558         }
25559         
25560         if(this.submenu){
25561             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25562             
25563             if(this.pos == 'left'){
25564                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25565             }
25566         }
25567         
25568         return cfg;
25569     },
25570     
25571     initEvents : function() 
25572     {
25573         this.el.on('mouseover', this.onMouseOver, this);
25574         this.el.on('mouseout', this.onMouseOut, this);
25575         
25576         this.el.select('a', true).first().on('click', this.onClick, this);
25577         
25578     },
25579     
25580     onClick : function(e)
25581     {
25582         if(this.preventDefault){
25583             e.preventDefault();
25584         }
25585         
25586         this.fireEvent("click", this, e);
25587     },
25588     
25589     onMouseOver : function(e)
25590     {
25591         if(this.submenu && this.pos == 'left'){
25592             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25593         }
25594         
25595         this.fireEvent("mouseover", this, e);
25596     },
25597     
25598     onMouseOut : function(e)
25599     {
25600         this.fireEvent("mouseout", this, e);
25601     }
25602 });
25603
25604  
25605
25606  /*
25607  * - LGPL
25608  *
25609  * menu separator
25610  * 
25611  */
25612 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25613
25614 /**
25615  * @class Roo.bootstrap.menu.Separator
25616  * @extends Roo.bootstrap.Component
25617  * Bootstrap Separator class
25618  * 
25619  * @constructor
25620  * Create a new Separator
25621  * @param {Object} config The config object
25622  */
25623
25624
25625 Roo.bootstrap.menu.Separator = function(config){
25626     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25627 };
25628
25629 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25630     
25631     getAutoCreate : function(){
25632         var cfg = {
25633             tag : 'li',
25634             cls: 'divider'
25635         };
25636         
25637         return cfg;
25638     }
25639    
25640 });
25641
25642  
25643
25644  /*
25645  * - LGPL
25646  *
25647  * Tooltip
25648  * 
25649  */
25650
25651 /**
25652  * @class Roo.bootstrap.Tooltip
25653  * Bootstrap Tooltip class
25654  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25655  * to determine which dom element triggers the tooltip.
25656  * 
25657  * It needs to add support for additional attributes like tooltip-position
25658  * 
25659  * @constructor
25660  * Create a new Toolti
25661  * @param {Object} config The config object
25662  */
25663
25664 Roo.bootstrap.Tooltip = function(config){
25665     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25666     
25667     this.alignment = Roo.bootstrap.Tooltip.alignment;
25668     
25669     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25670         this.alignment = config.alignment;
25671     }
25672     
25673 };
25674
25675 Roo.apply(Roo.bootstrap.Tooltip, {
25676     /**
25677      * @function init initialize tooltip monitoring.
25678      * @static
25679      */
25680     currentEl : false,
25681     currentTip : false,
25682     currentRegion : false,
25683     
25684     //  init : delay?
25685     
25686     init : function()
25687     {
25688         Roo.get(document).on('mouseover', this.enter ,this);
25689         Roo.get(document).on('mouseout', this.leave, this);
25690          
25691         
25692         this.currentTip = new Roo.bootstrap.Tooltip();
25693     },
25694     
25695     enter : function(ev)
25696     {
25697         var dom = ev.getTarget();
25698         
25699         //Roo.log(['enter',dom]);
25700         var el = Roo.fly(dom);
25701         if (this.currentEl) {
25702             //Roo.log(dom);
25703             //Roo.log(this.currentEl);
25704             //Roo.log(this.currentEl.contains(dom));
25705             if (this.currentEl == el) {
25706                 return;
25707             }
25708             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25709                 return;
25710             }
25711
25712         }
25713         
25714         if (this.currentTip.el) {
25715             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25716         }    
25717         //Roo.log(ev);
25718         
25719         if(!el || el.dom == document){
25720             return;
25721         }
25722         
25723         var bindEl = el;
25724         
25725         // you can not look for children, as if el is the body.. then everythign is the child..
25726         if (!el.attr('tooltip')) { //
25727             if (!el.select("[tooltip]").elements.length) {
25728                 return;
25729             }
25730             // is the mouse over this child...?
25731             bindEl = el.select("[tooltip]").first();
25732             var xy = ev.getXY();
25733             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25734                 //Roo.log("not in region.");
25735                 return;
25736             }
25737             //Roo.log("child element over..");
25738             
25739         }
25740         this.currentEl = bindEl;
25741         this.currentTip.bind(bindEl);
25742         this.currentRegion = Roo.lib.Region.getRegion(dom);
25743         this.currentTip.enter();
25744         
25745     },
25746     leave : function(ev)
25747     {
25748         var dom = ev.getTarget();
25749         //Roo.log(['leave',dom]);
25750         if (!this.currentEl) {
25751             return;
25752         }
25753         
25754         
25755         if (dom != this.currentEl.dom) {
25756             return;
25757         }
25758         var xy = ev.getXY();
25759         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25760             return;
25761         }
25762         // only activate leave if mouse cursor is outside... bounding box..
25763         
25764         
25765         
25766         
25767         if (this.currentTip) {
25768             this.currentTip.leave();
25769         }
25770         //Roo.log('clear currentEl');
25771         this.currentEl = false;
25772         
25773         
25774     },
25775     alignment : {
25776         'left' : ['r-l', [-2,0], 'right'],
25777         'right' : ['l-r', [2,0], 'left'],
25778         'bottom' : ['t-b', [0,2], 'top'],
25779         'top' : [ 'b-t', [0,-2], 'bottom']
25780     }
25781     
25782 });
25783
25784
25785 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25786     
25787     
25788     bindEl : false,
25789     
25790     delay : null, // can be { show : 300 , hide: 500}
25791     
25792     timeout : null,
25793     
25794     hoverState : null, //???
25795     
25796     placement : 'bottom', 
25797     
25798     alignment : false,
25799     
25800     getAutoCreate : function(){
25801     
25802         var cfg = {
25803            cls : 'tooltip',
25804            role : 'tooltip',
25805            cn : [
25806                 {
25807                     cls : 'tooltip-arrow'
25808                 },
25809                 {
25810                     cls : 'tooltip-inner'
25811                 }
25812            ]
25813         };
25814         
25815         return cfg;
25816     },
25817     bind : function(el)
25818     {
25819         this.bindEl = el;
25820     },
25821       
25822     
25823     enter : function () {
25824        
25825         if (this.timeout != null) {
25826             clearTimeout(this.timeout);
25827         }
25828         
25829         this.hoverState = 'in';
25830          //Roo.log("enter - show");
25831         if (!this.delay || !this.delay.show) {
25832             this.show();
25833             return;
25834         }
25835         var _t = this;
25836         this.timeout = setTimeout(function () {
25837             if (_t.hoverState == 'in') {
25838                 _t.show();
25839             }
25840         }, this.delay.show);
25841     },
25842     leave : function()
25843     {
25844         clearTimeout(this.timeout);
25845     
25846         this.hoverState = 'out';
25847          if (!this.delay || !this.delay.hide) {
25848             this.hide();
25849             return;
25850         }
25851        
25852         var _t = this;
25853         this.timeout = setTimeout(function () {
25854             //Roo.log("leave - timeout");
25855             
25856             if (_t.hoverState == 'out') {
25857                 _t.hide();
25858                 Roo.bootstrap.Tooltip.currentEl = false;
25859             }
25860         }, delay);
25861     },
25862     
25863     show : function (msg)
25864     {
25865         if (!this.el) {
25866             this.render(document.body);
25867         }
25868         // set content.
25869         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25870         
25871         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25872         
25873         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25874         
25875         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25876         
25877         var placement = typeof this.placement == 'function' ?
25878             this.placement.call(this, this.el, on_el) :
25879             this.placement;
25880             
25881         var autoToken = /\s?auto?\s?/i;
25882         var autoPlace = autoToken.test(placement);
25883         if (autoPlace) {
25884             placement = placement.replace(autoToken, '') || 'top';
25885         }
25886         
25887         //this.el.detach()
25888         //this.el.setXY([0,0]);
25889         this.el.show();
25890         //this.el.dom.style.display='block';
25891         
25892         //this.el.appendTo(on_el);
25893         
25894         var p = this.getPosition();
25895         var box = this.el.getBox();
25896         
25897         if (autoPlace) {
25898             // fixme..
25899         }
25900         
25901         var align = this.alignment[placement];
25902         
25903         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25904         
25905         if(placement == 'top' || placement == 'bottom'){
25906             if(xy[0] < 0){
25907                 placement = 'right';
25908             }
25909             
25910             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25911                 placement = 'left';
25912             }
25913             
25914             var scroll = Roo.select('body', true).first().getScroll();
25915             
25916             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25917                 placement = 'top';
25918             }
25919             
25920         }
25921         
25922         this.el.alignTo(this.bindEl, align[0],align[1]);
25923         //var arrow = this.el.select('.arrow',true).first();
25924         //arrow.set(align[2], 
25925         
25926         this.el.addClass(placement);
25927         
25928         this.el.addClass('in fade');
25929         
25930         this.hoverState = null;
25931         
25932         if (this.el.hasClass('fade')) {
25933             // fade it?
25934         }
25935         
25936     },
25937     hide : function()
25938     {
25939          
25940         if (!this.el) {
25941             return;
25942         }
25943         //this.el.setXY([0,0]);
25944         this.el.removeClass('in');
25945         //this.el.hide();
25946         
25947     }
25948     
25949 });
25950  
25951
25952  /*
25953  * - LGPL
25954  *
25955  * Location Picker
25956  * 
25957  */
25958
25959 /**
25960  * @class Roo.bootstrap.LocationPicker
25961  * @extends Roo.bootstrap.Component
25962  * Bootstrap LocationPicker class
25963  * @cfg {Number} latitude Position when init default 0
25964  * @cfg {Number} longitude Position when init default 0
25965  * @cfg {Number} zoom default 15
25966  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25967  * @cfg {Boolean} mapTypeControl default false
25968  * @cfg {Boolean} disableDoubleClickZoom default false
25969  * @cfg {Boolean} scrollwheel default true
25970  * @cfg {Boolean} streetViewControl default false
25971  * @cfg {Number} radius default 0
25972  * @cfg {String} locationName
25973  * @cfg {Boolean} draggable default true
25974  * @cfg {Boolean} enableAutocomplete default false
25975  * @cfg {Boolean} enableReverseGeocode default true
25976  * @cfg {String} markerTitle
25977  * 
25978  * @constructor
25979  * Create a new LocationPicker
25980  * @param {Object} config The config object
25981  */
25982
25983
25984 Roo.bootstrap.LocationPicker = function(config){
25985     
25986     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25987     
25988     this.addEvents({
25989         /**
25990          * @event initial
25991          * Fires when the picker initialized.
25992          * @param {Roo.bootstrap.LocationPicker} this
25993          * @param {Google Location} location
25994          */
25995         initial : true,
25996         /**
25997          * @event positionchanged
25998          * Fires when the picker position changed.
25999          * @param {Roo.bootstrap.LocationPicker} this
26000          * @param {Google Location} location
26001          */
26002         positionchanged : true,
26003         /**
26004          * @event resize
26005          * Fires when the map resize.
26006          * @param {Roo.bootstrap.LocationPicker} this
26007          */
26008         resize : true,
26009         /**
26010          * @event show
26011          * Fires when the map show.
26012          * @param {Roo.bootstrap.LocationPicker} this
26013          */
26014         show : true,
26015         /**
26016          * @event hide
26017          * Fires when the map hide.
26018          * @param {Roo.bootstrap.LocationPicker} this
26019          */
26020         hide : true,
26021         /**
26022          * @event mapClick
26023          * Fires when click the map.
26024          * @param {Roo.bootstrap.LocationPicker} this
26025          * @param {Map event} e
26026          */
26027         mapClick : true,
26028         /**
26029          * @event mapRightClick
26030          * Fires when right click the map.
26031          * @param {Roo.bootstrap.LocationPicker} this
26032          * @param {Map event} e
26033          */
26034         mapRightClick : true,
26035         /**
26036          * @event markerClick
26037          * Fires when click the marker.
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          * @param {Map event} e
26040          */
26041         markerClick : true,
26042         /**
26043          * @event markerRightClick
26044          * Fires when right click the marker.
26045          * @param {Roo.bootstrap.LocationPicker} this
26046          * @param {Map event} e
26047          */
26048         markerRightClick : true,
26049         /**
26050          * @event OverlayViewDraw
26051          * Fires when OverlayView Draw
26052          * @param {Roo.bootstrap.LocationPicker} this
26053          */
26054         OverlayViewDraw : true,
26055         /**
26056          * @event OverlayViewOnAdd
26057          * Fires when OverlayView Draw
26058          * @param {Roo.bootstrap.LocationPicker} this
26059          */
26060         OverlayViewOnAdd : true,
26061         /**
26062          * @event OverlayViewOnRemove
26063          * Fires when OverlayView Draw
26064          * @param {Roo.bootstrap.LocationPicker} this
26065          */
26066         OverlayViewOnRemove : true,
26067         /**
26068          * @event OverlayViewShow
26069          * Fires when OverlayView Draw
26070          * @param {Roo.bootstrap.LocationPicker} this
26071          * @param {Pixel} cpx
26072          */
26073         OverlayViewShow : true,
26074         /**
26075          * @event OverlayViewHide
26076          * Fires when OverlayView Draw
26077          * @param {Roo.bootstrap.LocationPicker} this
26078          */
26079         OverlayViewHide : true,
26080         /**
26081          * @event loadexception
26082          * Fires when load google lib failed.
26083          * @param {Roo.bootstrap.LocationPicker} this
26084          */
26085         loadexception : true
26086     });
26087         
26088 };
26089
26090 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26091     
26092     gMapContext: false,
26093     
26094     latitude: 0,
26095     longitude: 0,
26096     zoom: 15,
26097     mapTypeId: false,
26098     mapTypeControl: false,
26099     disableDoubleClickZoom: false,
26100     scrollwheel: true,
26101     streetViewControl: false,
26102     radius: 0,
26103     locationName: '',
26104     draggable: true,
26105     enableAutocomplete: false,
26106     enableReverseGeocode: true,
26107     markerTitle: '',
26108     
26109     getAutoCreate: function()
26110     {
26111
26112         var cfg = {
26113             tag: 'div',
26114             cls: 'roo-location-picker'
26115         };
26116         
26117         return cfg
26118     },
26119     
26120     initEvents: function(ct, position)
26121     {       
26122         if(!this.el.getWidth() || this.isApplied()){
26123             return;
26124         }
26125         
26126         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26127         
26128         this.initial();
26129     },
26130     
26131     initial: function()
26132     {
26133         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26134             this.fireEvent('loadexception', this);
26135             return;
26136         }
26137         
26138         if(!this.mapTypeId){
26139             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26140         }
26141         
26142         this.gMapContext = this.GMapContext();
26143         
26144         this.initOverlayView();
26145         
26146         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26147         
26148         var _this = this;
26149                 
26150         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26151             _this.setPosition(_this.gMapContext.marker.position);
26152         });
26153         
26154         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26155             _this.fireEvent('mapClick', this, event);
26156             
26157         });
26158
26159         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26160             _this.fireEvent('mapRightClick', this, event);
26161             
26162         });
26163         
26164         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26165             _this.fireEvent('markerClick', this, event);
26166             
26167         });
26168
26169         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26170             _this.fireEvent('markerRightClick', this, event);
26171             
26172         });
26173         
26174         this.setPosition(this.gMapContext.location);
26175         
26176         this.fireEvent('initial', this, this.gMapContext.location);
26177     },
26178     
26179     initOverlayView: function()
26180     {
26181         var _this = this;
26182         
26183         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26184             
26185             draw: function()
26186             {
26187                 _this.fireEvent('OverlayViewDraw', _this);
26188             },
26189             
26190             onAdd: function()
26191             {
26192                 _this.fireEvent('OverlayViewOnAdd', _this);
26193             },
26194             
26195             onRemove: function()
26196             {
26197                 _this.fireEvent('OverlayViewOnRemove', _this);
26198             },
26199             
26200             show: function(cpx)
26201             {
26202                 _this.fireEvent('OverlayViewShow', _this, cpx);
26203             },
26204             
26205             hide: function()
26206             {
26207                 _this.fireEvent('OverlayViewHide', _this);
26208             }
26209             
26210         });
26211     },
26212     
26213     fromLatLngToContainerPixel: function(event)
26214     {
26215         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26216     },
26217     
26218     isApplied: function() 
26219     {
26220         return this.getGmapContext() == false ? false : true;
26221     },
26222     
26223     getGmapContext: function() 
26224     {
26225         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26226     },
26227     
26228     GMapContext: function() 
26229     {
26230         var position = new google.maps.LatLng(this.latitude, this.longitude);
26231         
26232         var _map = new google.maps.Map(this.el.dom, {
26233             center: position,
26234             zoom: this.zoom,
26235             mapTypeId: this.mapTypeId,
26236             mapTypeControl: this.mapTypeControl,
26237             disableDoubleClickZoom: this.disableDoubleClickZoom,
26238             scrollwheel: this.scrollwheel,
26239             streetViewControl: this.streetViewControl,
26240             locationName: this.locationName,
26241             draggable: this.draggable,
26242             enableAutocomplete: this.enableAutocomplete,
26243             enableReverseGeocode: this.enableReverseGeocode
26244         });
26245         
26246         var _marker = new google.maps.Marker({
26247             position: position,
26248             map: _map,
26249             title: this.markerTitle,
26250             draggable: this.draggable
26251         });
26252         
26253         return {
26254             map: _map,
26255             marker: _marker,
26256             circle: null,
26257             location: position,
26258             radius: this.radius,
26259             locationName: this.locationName,
26260             addressComponents: {
26261                 formatted_address: null,
26262                 addressLine1: null,
26263                 addressLine2: null,
26264                 streetName: null,
26265                 streetNumber: null,
26266                 city: null,
26267                 district: null,
26268                 state: null,
26269                 stateOrProvince: null
26270             },
26271             settings: this,
26272             domContainer: this.el.dom,
26273             geodecoder: new google.maps.Geocoder()
26274         };
26275     },
26276     
26277     drawCircle: function(center, radius, options) 
26278     {
26279         if (this.gMapContext.circle != null) {
26280             this.gMapContext.circle.setMap(null);
26281         }
26282         if (radius > 0) {
26283             radius *= 1;
26284             options = Roo.apply({}, options, {
26285                 strokeColor: "#0000FF",
26286                 strokeOpacity: .35,
26287                 strokeWeight: 2,
26288                 fillColor: "#0000FF",
26289                 fillOpacity: .2
26290             });
26291             
26292             options.map = this.gMapContext.map;
26293             options.radius = radius;
26294             options.center = center;
26295             this.gMapContext.circle = new google.maps.Circle(options);
26296             return this.gMapContext.circle;
26297         }
26298         
26299         return null;
26300     },
26301     
26302     setPosition: function(location) 
26303     {
26304         this.gMapContext.location = location;
26305         this.gMapContext.marker.setPosition(location);
26306         this.gMapContext.map.panTo(location);
26307         this.drawCircle(location, this.gMapContext.radius, {});
26308         
26309         var _this = this;
26310         
26311         if (this.gMapContext.settings.enableReverseGeocode) {
26312             this.gMapContext.geodecoder.geocode({
26313                 latLng: this.gMapContext.location
26314             }, function(results, status) {
26315                 
26316                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26317                     _this.gMapContext.locationName = results[0].formatted_address;
26318                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26319                     
26320                     _this.fireEvent('positionchanged', this, location);
26321                 }
26322             });
26323             
26324             return;
26325         }
26326         
26327         this.fireEvent('positionchanged', this, location);
26328     },
26329     
26330     resize: function()
26331     {
26332         google.maps.event.trigger(this.gMapContext.map, "resize");
26333         
26334         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26335         
26336         this.fireEvent('resize', this);
26337     },
26338     
26339     setPositionByLatLng: function(latitude, longitude)
26340     {
26341         this.setPosition(new google.maps.LatLng(latitude, longitude));
26342     },
26343     
26344     getCurrentPosition: function() 
26345     {
26346         return {
26347             latitude: this.gMapContext.location.lat(),
26348             longitude: this.gMapContext.location.lng()
26349         };
26350     },
26351     
26352     getAddressName: function() 
26353     {
26354         return this.gMapContext.locationName;
26355     },
26356     
26357     getAddressComponents: function() 
26358     {
26359         return this.gMapContext.addressComponents;
26360     },
26361     
26362     address_component_from_google_geocode: function(address_components) 
26363     {
26364         var result = {};
26365         
26366         for (var i = 0; i < address_components.length; i++) {
26367             var component = address_components[i];
26368             if (component.types.indexOf("postal_code") >= 0) {
26369                 result.postalCode = component.short_name;
26370             } else if (component.types.indexOf("street_number") >= 0) {
26371                 result.streetNumber = component.short_name;
26372             } else if (component.types.indexOf("route") >= 0) {
26373                 result.streetName = component.short_name;
26374             } else if (component.types.indexOf("neighborhood") >= 0) {
26375                 result.city = component.short_name;
26376             } else if (component.types.indexOf("locality") >= 0) {
26377                 result.city = component.short_name;
26378             } else if (component.types.indexOf("sublocality") >= 0) {
26379                 result.district = component.short_name;
26380             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26381                 result.stateOrProvince = component.short_name;
26382             } else if (component.types.indexOf("country") >= 0) {
26383                 result.country = component.short_name;
26384             }
26385         }
26386         
26387         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26388         result.addressLine2 = "";
26389         return result;
26390     },
26391     
26392     setZoomLevel: function(zoom)
26393     {
26394         this.gMapContext.map.setZoom(zoom);
26395     },
26396     
26397     show: function()
26398     {
26399         if(!this.el){
26400             return;
26401         }
26402         
26403         this.el.show();
26404         
26405         this.resize();
26406         
26407         this.fireEvent('show', this);
26408     },
26409     
26410     hide: function()
26411     {
26412         if(!this.el){
26413             return;
26414         }
26415         
26416         this.el.hide();
26417         
26418         this.fireEvent('hide', this);
26419     }
26420     
26421 });
26422
26423 Roo.apply(Roo.bootstrap.LocationPicker, {
26424     
26425     OverlayView : function(map, options)
26426     {
26427         options = options || {};
26428         
26429         this.setMap(map);
26430     }
26431     
26432     
26433 });/*
26434  * - LGPL
26435  *
26436  * Alert
26437  * 
26438  */
26439
26440 /**
26441  * @class Roo.bootstrap.Alert
26442  * @extends Roo.bootstrap.Component
26443  * Bootstrap Alert class
26444  * @cfg {String} title The title of alert
26445  * @cfg {String} html The content of alert
26446  * @cfg {String} weight (  success | info | warning | danger )
26447  * @cfg {String} faicon font-awesomeicon
26448  * 
26449  * @constructor
26450  * Create a new alert
26451  * @param {Object} config The config object
26452  */
26453
26454
26455 Roo.bootstrap.Alert = function(config){
26456     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26457     
26458 };
26459
26460 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26461     
26462     title: '',
26463     html: '',
26464     weight: false,
26465     faicon: false,
26466     
26467     getAutoCreate : function()
26468     {
26469         
26470         var cfg = {
26471             tag : 'div',
26472             cls : 'alert',
26473             cn : [
26474                 {
26475                     tag : 'i',
26476                     cls : 'roo-alert-icon'
26477                     
26478                 },
26479                 {
26480                     tag : 'b',
26481                     cls : 'roo-alert-title',
26482                     html : this.title
26483                 },
26484                 {
26485                     tag : 'span',
26486                     cls : 'roo-alert-text',
26487                     html : this.html
26488                 }
26489             ]
26490         };
26491         
26492         if(this.faicon){
26493             cfg.cn[0].cls += ' fa ' + this.faicon;
26494         }
26495         
26496         if(this.weight){
26497             cfg.cls += ' alert-' + this.weight;
26498         }
26499         
26500         return cfg;
26501     },
26502     
26503     initEvents: function() 
26504     {
26505         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26506     },
26507     
26508     setTitle : function(str)
26509     {
26510         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26511     },
26512     
26513     setText : function(str)
26514     {
26515         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26516     },
26517     
26518     setWeight : function(weight)
26519     {
26520         if(this.weight){
26521             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26522         }
26523         
26524         this.weight = weight;
26525         
26526         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26527     },
26528     
26529     setIcon : function(icon)
26530     {
26531         if(this.faicon){
26532             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26533         }
26534         
26535         this.faicon = icon;
26536         
26537         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26538     },
26539     
26540     hide: function() 
26541     {
26542         this.el.hide();   
26543     },
26544     
26545     show: function() 
26546     {  
26547         this.el.show();   
26548     }
26549     
26550 });
26551
26552  
26553 /*
26554 * Licence: LGPL
26555 */
26556
26557 /**
26558  * @class Roo.bootstrap.UploadCropbox
26559  * @extends Roo.bootstrap.Component
26560  * Bootstrap UploadCropbox class
26561  * @cfg {String} emptyText show when image has been loaded
26562  * @cfg {String} rotateNotify show when image too small to rotate
26563  * @cfg {Number} errorTimeout default 3000
26564  * @cfg {Number} minWidth default 300
26565  * @cfg {Number} minHeight default 300
26566  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26567  * @cfg {Boolean} isDocument (true|false) default false
26568  * @cfg {String} url action url
26569  * @cfg {String} paramName default 'imageUpload'
26570  * @cfg {String} method default POST
26571  * @cfg {Boolean} loadMask (true|false) default true
26572  * @cfg {Boolean} loadingText default 'Loading...'
26573  * 
26574  * @constructor
26575  * Create a new UploadCropbox
26576  * @param {Object} config The config object
26577  */
26578
26579 Roo.bootstrap.UploadCropbox = function(config){
26580     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26581     
26582     this.addEvents({
26583         /**
26584          * @event beforeselectfile
26585          * Fire before select file
26586          * @param {Roo.bootstrap.UploadCropbox} this
26587          */
26588         "beforeselectfile" : true,
26589         /**
26590          * @event initial
26591          * Fire after initEvent
26592          * @param {Roo.bootstrap.UploadCropbox} this
26593          */
26594         "initial" : true,
26595         /**
26596          * @event crop
26597          * Fire after initEvent
26598          * @param {Roo.bootstrap.UploadCropbox} this
26599          * @param {String} data
26600          */
26601         "crop" : true,
26602         /**
26603          * @event prepare
26604          * Fire when preparing the file data
26605          * @param {Roo.bootstrap.UploadCropbox} this
26606          * @param {Object} file
26607          */
26608         "prepare" : true,
26609         /**
26610          * @event exception
26611          * Fire when get exception
26612          * @param {Roo.bootstrap.UploadCropbox} this
26613          * @param {XMLHttpRequest} xhr
26614          */
26615         "exception" : true,
26616         /**
26617          * @event beforeloadcanvas
26618          * Fire before load the canvas
26619          * @param {Roo.bootstrap.UploadCropbox} this
26620          * @param {String} src
26621          */
26622         "beforeloadcanvas" : true,
26623         /**
26624          * @event trash
26625          * Fire when trash image
26626          * @param {Roo.bootstrap.UploadCropbox} this
26627          */
26628         "trash" : true,
26629         /**
26630          * @event download
26631          * Fire when download the image
26632          * @param {Roo.bootstrap.UploadCropbox} this
26633          */
26634         "download" : true,
26635         /**
26636          * @event footerbuttonclick
26637          * Fire when footerbuttonclick
26638          * @param {Roo.bootstrap.UploadCropbox} this
26639          * @param {String} type
26640          */
26641         "footerbuttonclick" : true,
26642         /**
26643          * @event resize
26644          * Fire when resize
26645          * @param {Roo.bootstrap.UploadCropbox} this
26646          */
26647         "resize" : true,
26648         /**
26649          * @event rotate
26650          * Fire when rotate the image
26651          * @param {Roo.bootstrap.UploadCropbox} this
26652          * @param {String} pos
26653          */
26654         "rotate" : true,
26655         /**
26656          * @event inspect
26657          * Fire when inspect the file
26658          * @param {Roo.bootstrap.UploadCropbox} this
26659          * @param {Object} file
26660          */
26661         "inspect" : true,
26662         /**
26663          * @event upload
26664          * Fire when xhr upload the file
26665          * @param {Roo.bootstrap.UploadCropbox} this
26666          * @param {Object} data
26667          */
26668         "upload" : true,
26669         /**
26670          * @event arrange
26671          * Fire when arrange the file data
26672          * @param {Roo.bootstrap.UploadCropbox} this
26673          * @param {Object} formData
26674          */
26675         "arrange" : true
26676     });
26677     
26678     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26679 };
26680
26681 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26682     
26683     emptyText : 'Click to upload image',
26684     rotateNotify : 'Image is too small to rotate',
26685     errorTimeout : 3000,
26686     scale : 0,
26687     baseScale : 1,
26688     rotate : 0,
26689     dragable : false,
26690     pinching : false,
26691     mouseX : 0,
26692     mouseY : 0,
26693     cropData : false,
26694     minWidth : 300,
26695     minHeight : 300,
26696     file : false,
26697     exif : {},
26698     baseRotate : 1,
26699     cropType : 'image/jpeg',
26700     buttons : false,
26701     canvasLoaded : false,
26702     isDocument : false,
26703     method : 'POST',
26704     paramName : 'imageUpload',
26705     loadMask : true,
26706     loadingText : 'Loading...',
26707     maskEl : false,
26708     
26709     getAutoCreate : function()
26710     {
26711         var cfg = {
26712             tag : 'div',
26713             cls : 'roo-upload-cropbox',
26714             cn : [
26715                 {
26716                     tag : 'input',
26717                     cls : 'roo-upload-cropbox-selector',
26718                     type : 'file'
26719                 },
26720                 {
26721                     tag : 'div',
26722                     cls : 'roo-upload-cropbox-body',
26723                     style : 'cursor:pointer',
26724                     cn : [
26725                         {
26726                             tag : 'div',
26727                             cls : 'roo-upload-cropbox-preview'
26728                         },
26729                         {
26730                             tag : 'div',
26731                             cls : 'roo-upload-cropbox-thumb'
26732                         },
26733                         {
26734                             tag : 'div',
26735                             cls : 'roo-upload-cropbox-empty-notify',
26736                             html : this.emptyText
26737                         },
26738                         {
26739                             tag : 'div',
26740                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26741                             html : this.rotateNotify
26742                         }
26743                     ]
26744                 },
26745                 {
26746                     tag : 'div',
26747                     cls : 'roo-upload-cropbox-footer',
26748                     cn : {
26749                         tag : 'div',
26750                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26751                         cn : []
26752                     }
26753                 }
26754             ]
26755         };
26756         
26757         return cfg;
26758     },
26759     
26760     onRender : function(ct, position)
26761     {
26762         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26763         
26764         if (this.buttons.length) {
26765             
26766             Roo.each(this.buttons, function(bb) {
26767                 
26768                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26769                 
26770                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26771                 
26772             }, this);
26773         }
26774         
26775         if(this.loadMask){
26776             this.maskEl = this.el;
26777         }
26778     },
26779     
26780     initEvents : function()
26781     {
26782         this.urlAPI = (window.createObjectURL && window) || 
26783                                 (window.URL && URL.revokeObjectURL && URL) || 
26784                                 (window.webkitURL && webkitURL);
26785                         
26786         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26787         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26788         
26789         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26790         this.selectorEl.hide();
26791         
26792         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26793         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26794         
26795         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26796         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26797         this.thumbEl.hide();
26798         
26799         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26800         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26801         
26802         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26803         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26804         this.errorEl.hide();
26805         
26806         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26807         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26808         this.footerEl.hide();
26809         
26810         this.setThumbBoxSize();
26811         
26812         this.bind();
26813         
26814         this.resize();
26815         
26816         this.fireEvent('initial', this);
26817     },
26818
26819     bind : function()
26820     {
26821         var _this = this;
26822         
26823         window.addEventListener("resize", function() { _this.resize(); } );
26824         
26825         this.bodyEl.on('click', this.beforeSelectFile, this);
26826         
26827         if(Roo.isTouch){
26828             this.bodyEl.on('touchstart', this.onTouchStart, this);
26829             this.bodyEl.on('touchmove', this.onTouchMove, this);
26830             this.bodyEl.on('touchend', this.onTouchEnd, this);
26831         }
26832         
26833         if(!Roo.isTouch){
26834             this.bodyEl.on('mousedown', this.onMouseDown, this);
26835             this.bodyEl.on('mousemove', this.onMouseMove, this);
26836             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26837             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26838             Roo.get(document).on('mouseup', this.onMouseUp, this);
26839         }
26840         
26841         this.selectorEl.on('change', this.onFileSelected, this);
26842     },
26843     
26844     reset : function()
26845     {    
26846         this.scale = 0;
26847         this.baseScale = 1;
26848         this.rotate = 0;
26849         this.baseRotate = 1;
26850         this.dragable = false;
26851         this.pinching = false;
26852         this.mouseX = 0;
26853         this.mouseY = 0;
26854         this.cropData = false;
26855         this.notifyEl.dom.innerHTML = this.emptyText;
26856         
26857         this.selectorEl.dom.value = '';
26858         
26859     },
26860     
26861     resize : function()
26862     {
26863         if(this.fireEvent('resize', this) != false){
26864             this.setThumbBoxPosition();
26865             this.setCanvasPosition();
26866         }
26867     },
26868     
26869     onFooterButtonClick : function(e, el, o, type)
26870     {
26871         switch (type) {
26872             case 'rotate-left' :
26873                 this.onRotateLeft(e);
26874                 break;
26875             case 'rotate-right' :
26876                 this.onRotateRight(e);
26877                 break;
26878             case 'picture' :
26879                 this.beforeSelectFile(e);
26880                 break;
26881             case 'trash' :
26882                 this.trash(e);
26883                 break;
26884             case 'crop' :
26885                 this.crop(e);
26886                 break;
26887             case 'download' :
26888                 this.download(e);
26889                 break;
26890             default :
26891                 break;
26892         }
26893         
26894         this.fireEvent('footerbuttonclick', this, type);
26895     },
26896     
26897     beforeSelectFile : function(e)
26898     {
26899         e.preventDefault();
26900         
26901         if(this.fireEvent('beforeselectfile', this) != false){
26902             this.selectorEl.dom.click();
26903         }
26904     },
26905     
26906     onFileSelected : function(e)
26907     {
26908         e.preventDefault();
26909         
26910         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26911             return;
26912         }
26913         
26914         var file = this.selectorEl.dom.files[0];
26915         
26916         if(this.fireEvent('inspect', this, file) != false){
26917             this.prepare(file);
26918         }
26919         
26920     },
26921     
26922     trash : function(e)
26923     {
26924         this.fireEvent('trash', this);
26925     },
26926     
26927     download : function(e)
26928     {
26929         this.fireEvent('download', this);
26930     },
26931     
26932     loadCanvas : function(src)
26933     {   
26934         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26935             
26936             this.reset();
26937             
26938             this.imageEl = document.createElement('img');
26939             
26940             var _this = this;
26941             
26942             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26943             
26944             this.imageEl.src = src;
26945         }
26946     },
26947     
26948     onLoadCanvas : function()
26949     {   
26950         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26951         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26952         
26953         this.bodyEl.un('click', this.beforeSelectFile, this);
26954         
26955         this.notifyEl.hide();
26956         this.thumbEl.show();
26957         this.footerEl.show();
26958         
26959         this.baseRotateLevel();
26960         
26961         if(this.isDocument){
26962             this.setThumbBoxSize();
26963         }
26964         
26965         this.setThumbBoxPosition();
26966         
26967         this.baseScaleLevel();
26968         
26969         this.draw();
26970         
26971         this.resize();
26972         
26973         this.canvasLoaded = true;
26974         
26975         if(this.loadMask){
26976             this.maskEl.unmask();
26977         }
26978         
26979     },
26980     
26981     setCanvasPosition : function()
26982     {   
26983         if(!this.canvasEl){
26984             return;
26985         }
26986         
26987         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26988         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26989         
26990         this.previewEl.setLeft(pw);
26991         this.previewEl.setTop(ph);
26992         
26993     },
26994     
26995     onMouseDown : function(e)
26996     {   
26997         e.stopEvent();
26998         
26999         this.dragable = true;
27000         this.pinching = false;
27001         
27002         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27003             this.dragable = false;
27004             return;
27005         }
27006         
27007         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27008         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27009         
27010     },
27011     
27012     onMouseMove : function(e)
27013     {   
27014         e.stopEvent();
27015         
27016         if(!this.canvasLoaded){
27017             return;
27018         }
27019         
27020         if (!this.dragable){
27021             return;
27022         }
27023         
27024         var minX = Math.ceil(this.thumbEl.getLeft(true));
27025         var minY = Math.ceil(this.thumbEl.getTop(true));
27026         
27027         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27028         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27029         
27030         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27031         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27032         
27033         x = x - this.mouseX;
27034         y = y - this.mouseY;
27035         
27036         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27037         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27038         
27039         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27040         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27041         
27042         this.previewEl.setLeft(bgX);
27043         this.previewEl.setTop(bgY);
27044         
27045         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27046         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27047     },
27048     
27049     onMouseUp : function(e)
27050     {   
27051         e.stopEvent();
27052         
27053         this.dragable = false;
27054     },
27055     
27056     onMouseWheel : function(e)
27057     {   
27058         e.stopEvent();
27059         
27060         this.startScale = this.scale;
27061         
27062         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27063         
27064         if(!this.zoomable()){
27065             this.scale = this.startScale;
27066             return;
27067         }
27068         
27069         this.draw();
27070         
27071         return;
27072     },
27073     
27074     zoomable : function()
27075     {
27076         var minScale = this.thumbEl.getWidth() / this.minWidth;
27077         
27078         if(this.minWidth < this.minHeight){
27079             minScale = this.thumbEl.getHeight() / this.minHeight;
27080         }
27081         
27082         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27083         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27084         
27085         if(
27086                 this.isDocument &&
27087                 (this.rotate == 0 || this.rotate == 180) && 
27088                 (
27089                     width > this.imageEl.OriginWidth || 
27090                     height > this.imageEl.OriginHeight ||
27091                     (width < this.minWidth && height < this.minHeight)
27092                 )
27093         ){
27094             return false;
27095         }
27096         
27097         if(
27098                 this.isDocument &&
27099                 (this.rotate == 90 || this.rotate == 270) && 
27100                 (
27101                     width > this.imageEl.OriginWidth || 
27102                     height > this.imageEl.OriginHeight ||
27103                     (width < this.minHeight && height < this.minWidth)
27104                 )
27105         ){
27106             return false;
27107         }
27108         
27109         if(
27110                 !this.isDocument &&
27111                 (this.rotate == 0 || this.rotate == 180) && 
27112                 (
27113                     width < this.minWidth || 
27114                     width > this.imageEl.OriginWidth || 
27115                     height < this.minHeight || 
27116                     height > this.imageEl.OriginHeight
27117                 )
27118         ){
27119             return false;
27120         }
27121         
27122         if(
27123                 !this.isDocument &&
27124                 (this.rotate == 90 || this.rotate == 270) && 
27125                 (
27126                     width < this.minHeight || 
27127                     width > this.imageEl.OriginWidth || 
27128                     height < this.minWidth || 
27129                     height > this.imageEl.OriginHeight
27130                 )
27131         ){
27132             return false;
27133         }
27134         
27135         return true;
27136         
27137     },
27138     
27139     onRotateLeft : function(e)
27140     {   
27141         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27142             
27143             var minScale = this.thumbEl.getWidth() / this.minWidth;
27144             
27145             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27146             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27147             
27148             this.startScale = this.scale;
27149             
27150             while (this.getScaleLevel() < minScale){
27151             
27152                 this.scale = this.scale + 1;
27153                 
27154                 if(!this.zoomable()){
27155                     break;
27156                 }
27157                 
27158                 if(
27159                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27160                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27161                 ){
27162                     continue;
27163                 }
27164                 
27165                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27166
27167                 this.draw();
27168                 
27169                 return;
27170             }
27171             
27172             this.scale = this.startScale;
27173             
27174             this.onRotateFail();
27175             
27176             return false;
27177         }
27178         
27179         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27180
27181         if(this.isDocument){
27182             this.setThumbBoxSize();
27183             this.setThumbBoxPosition();
27184             this.setCanvasPosition();
27185         }
27186         
27187         this.draw();
27188         
27189         this.fireEvent('rotate', this, 'left');
27190         
27191     },
27192     
27193     onRotateRight : function(e)
27194     {
27195         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27196             
27197             var minScale = this.thumbEl.getWidth() / this.minWidth;
27198         
27199             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27200             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27201             
27202             this.startScale = this.scale;
27203             
27204             while (this.getScaleLevel() < minScale){
27205             
27206                 this.scale = this.scale + 1;
27207                 
27208                 if(!this.zoomable()){
27209                     break;
27210                 }
27211                 
27212                 if(
27213                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27214                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27215                 ){
27216                     continue;
27217                 }
27218                 
27219                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27220
27221                 this.draw();
27222                 
27223                 return;
27224             }
27225             
27226             this.scale = this.startScale;
27227             
27228             this.onRotateFail();
27229             
27230             return false;
27231         }
27232         
27233         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27234
27235         if(this.isDocument){
27236             this.setThumbBoxSize();
27237             this.setThumbBoxPosition();
27238             this.setCanvasPosition();
27239         }
27240         
27241         this.draw();
27242         
27243         this.fireEvent('rotate', this, 'right');
27244     },
27245     
27246     onRotateFail : function()
27247     {
27248         this.errorEl.show(true);
27249         
27250         var _this = this;
27251         
27252         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27253     },
27254     
27255     draw : function()
27256     {
27257         this.previewEl.dom.innerHTML = '';
27258         
27259         var canvasEl = document.createElement("canvas");
27260         
27261         var contextEl = canvasEl.getContext("2d");
27262         
27263         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27264         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27265         var center = this.imageEl.OriginWidth / 2;
27266         
27267         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27268             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27269             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27270             center = this.imageEl.OriginHeight / 2;
27271         }
27272         
27273         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27274         
27275         contextEl.translate(center, center);
27276         contextEl.rotate(this.rotate * Math.PI / 180);
27277
27278         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27279         
27280         this.canvasEl = document.createElement("canvas");
27281         
27282         this.contextEl = this.canvasEl.getContext("2d");
27283         
27284         switch (this.rotate) {
27285             case 0 :
27286                 
27287                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27288                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27289                 
27290                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27291                 
27292                 break;
27293             case 90 : 
27294                 
27295                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27296                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27297                 
27298                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27299                     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);
27300                     break;
27301                 }
27302                 
27303                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27304                 
27305                 break;
27306             case 180 :
27307                 
27308                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27309                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27310                 
27311                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27312                     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);
27313                     break;
27314                 }
27315                 
27316                 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);
27317                 
27318                 break;
27319             case 270 :
27320                 
27321                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27322                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27323         
27324                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27325                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27326                     break;
27327                 }
27328                 
27329                 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);
27330                 
27331                 break;
27332             default : 
27333                 break;
27334         }
27335         
27336         this.previewEl.appendChild(this.canvasEl);
27337         
27338         this.setCanvasPosition();
27339     },
27340     
27341     crop : function()
27342     {
27343         if(!this.canvasLoaded){
27344             return;
27345         }
27346         
27347         var imageCanvas = document.createElement("canvas");
27348         
27349         var imageContext = imageCanvas.getContext("2d");
27350         
27351         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27352         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27353         
27354         var center = imageCanvas.width / 2;
27355         
27356         imageContext.translate(center, center);
27357         
27358         imageContext.rotate(this.rotate * Math.PI / 180);
27359         
27360         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27361         
27362         var canvas = document.createElement("canvas");
27363         
27364         var context = canvas.getContext("2d");
27365                 
27366         canvas.width = this.minWidth;
27367         canvas.height = this.minHeight;
27368
27369         switch (this.rotate) {
27370             case 0 :
27371                 
27372                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27373                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27374                 
27375                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27376                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27377                 
27378                 var targetWidth = this.minWidth - 2 * x;
27379                 var targetHeight = this.minHeight - 2 * y;
27380                 
27381                 var scale = 1;
27382                 
27383                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27384                     scale = targetWidth / width;
27385                 }
27386                 
27387                 if(x > 0 && y == 0){
27388                     scale = targetHeight / height;
27389                 }
27390                 
27391                 if(x > 0 && y > 0){
27392                     scale = targetWidth / width;
27393                     
27394                     if(width < height){
27395                         scale = targetHeight / height;
27396                     }
27397                 }
27398                 
27399                 context.scale(scale, scale);
27400                 
27401                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27402                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27403
27404                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27405                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27406
27407                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27408                 
27409                 break;
27410             case 90 : 
27411                 
27412                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27413                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27414                 
27415                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27416                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27417                 
27418                 var targetWidth = this.minWidth - 2 * x;
27419                 var targetHeight = this.minHeight - 2 * y;
27420                 
27421                 var scale = 1;
27422                 
27423                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27424                     scale = targetWidth / width;
27425                 }
27426                 
27427                 if(x > 0 && y == 0){
27428                     scale = targetHeight / height;
27429                 }
27430                 
27431                 if(x > 0 && y > 0){
27432                     scale = targetWidth / width;
27433                     
27434                     if(width < height){
27435                         scale = targetHeight / height;
27436                     }
27437                 }
27438                 
27439                 context.scale(scale, scale);
27440                 
27441                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27442                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27443
27444                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27445                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27446                 
27447                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27448                 
27449                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27450                 
27451                 break;
27452             case 180 :
27453                 
27454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27456                 
27457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27459                 
27460                 var targetWidth = this.minWidth - 2 * x;
27461                 var targetHeight = this.minHeight - 2 * y;
27462                 
27463                 var scale = 1;
27464                 
27465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27466                     scale = targetWidth / width;
27467                 }
27468                 
27469                 if(x > 0 && y == 0){
27470                     scale = targetHeight / height;
27471                 }
27472                 
27473                 if(x > 0 && y > 0){
27474                     scale = targetWidth / width;
27475                     
27476                     if(width < height){
27477                         scale = targetHeight / height;
27478                     }
27479                 }
27480                 
27481                 context.scale(scale, scale);
27482                 
27483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27485
27486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27488
27489                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27490                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27491                 
27492                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27493                 
27494                 break;
27495             case 270 :
27496                 
27497                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27498                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27499                 
27500                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27501                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27502                 
27503                 var targetWidth = this.minWidth - 2 * x;
27504                 var targetHeight = this.minHeight - 2 * y;
27505                 
27506                 var scale = 1;
27507                 
27508                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27509                     scale = targetWidth / width;
27510                 }
27511                 
27512                 if(x > 0 && y == 0){
27513                     scale = targetHeight / height;
27514                 }
27515                 
27516                 if(x > 0 && y > 0){
27517                     scale = targetWidth / width;
27518                     
27519                     if(width < height){
27520                         scale = targetHeight / height;
27521                     }
27522                 }
27523                 
27524                 context.scale(scale, scale);
27525                 
27526                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27527                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27528
27529                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27530                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27531                 
27532                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27533                 
27534                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27535                 
27536                 break;
27537             default : 
27538                 break;
27539         }
27540         
27541         this.cropData = canvas.toDataURL(this.cropType);
27542         
27543         if(this.fireEvent('crop', this, this.cropData) !== false){
27544             this.process(this.file, this.cropData);
27545         }
27546         
27547         return;
27548         
27549     },
27550     
27551     setThumbBoxSize : function()
27552     {
27553         var width, height;
27554         
27555         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27556             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27557             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27558             
27559             this.minWidth = width;
27560             this.minHeight = height;
27561             
27562             if(this.rotate == 90 || this.rotate == 270){
27563                 this.minWidth = height;
27564                 this.minHeight = width;
27565             }
27566         }
27567         
27568         height = 300;
27569         width = Math.ceil(this.minWidth * height / this.minHeight);
27570         
27571         if(this.minWidth > this.minHeight){
27572             width = 300;
27573             height = Math.ceil(this.minHeight * width / this.minWidth);
27574         }
27575         
27576         this.thumbEl.setStyle({
27577             width : width + 'px',
27578             height : height + 'px'
27579         });
27580
27581         return;
27582             
27583     },
27584     
27585     setThumbBoxPosition : function()
27586     {
27587         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27588         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27589         
27590         this.thumbEl.setLeft(x);
27591         this.thumbEl.setTop(y);
27592         
27593     },
27594     
27595     baseRotateLevel : function()
27596     {
27597         this.baseRotate = 1;
27598         
27599         if(
27600                 typeof(this.exif) != 'undefined' &&
27601                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27602                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27603         ){
27604             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27605         }
27606         
27607         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27608         
27609     },
27610     
27611     baseScaleLevel : function()
27612     {
27613         var width, height;
27614         
27615         if(this.isDocument){
27616             
27617             if(this.baseRotate == 6 || this.baseRotate == 8){
27618             
27619                 height = this.thumbEl.getHeight();
27620                 this.baseScale = height / this.imageEl.OriginWidth;
27621
27622                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27623                     width = this.thumbEl.getWidth();
27624                     this.baseScale = width / this.imageEl.OriginHeight;
27625                 }
27626
27627                 return;
27628             }
27629
27630             height = this.thumbEl.getHeight();
27631             this.baseScale = height / this.imageEl.OriginHeight;
27632
27633             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27634                 width = this.thumbEl.getWidth();
27635                 this.baseScale = width / this.imageEl.OriginWidth;
27636             }
27637
27638             return;
27639         }
27640         
27641         if(this.baseRotate == 6 || this.baseRotate == 8){
27642             
27643             width = this.thumbEl.getHeight();
27644             this.baseScale = width / this.imageEl.OriginHeight;
27645             
27646             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27647                 height = this.thumbEl.getWidth();
27648                 this.baseScale = height / this.imageEl.OriginHeight;
27649             }
27650             
27651             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27652                 height = this.thumbEl.getWidth();
27653                 this.baseScale = height / this.imageEl.OriginHeight;
27654                 
27655                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27656                     width = this.thumbEl.getHeight();
27657                     this.baseScale = width / this.imageEl.OriginWidth;
27658                 }
27659             }
27660             
27661             return;
27662         }
27663         
27664         width = this.thumbEl.getWidth();
27665         this.baseScale = width / this.imageEl.OriginWidth;
27666         
27667         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27668             height = this.thumbEl.getHeight();
27669             this.baseScale = height / this.imageEl.OriginHeight;
27670         }
27671         
27672         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27673             
27674             height = this.thumbEl.getHeight();
27675             this.baseScale = height / this.imageEl.OriginHeight;
27676             
27677             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27678                 width = this.thumbEl.getWidth();
27679                 this.baseScale = width / this.imageEl.OriginWidth;
27680             }
27681             
27682         }
27683         
27684         return;
27685     },
27686     
27687     getScaleLevel : function()
27688     {
27689         return this.baseScale * Math.pow(1.1, this.scale);
27690     },
27691     
27692     onTouchStart : function(e)
27693     {
27694         if(!this.canvasLoaded){
27695             this.beforeSelectFile(e);
27696             return;
27697         }
27698         
27699         var touches = e.browserEvent.touches;
27700         
27701         if(!touches){
27702             return;
27703         }
27704         
27705         if(touches.length == 1){
27706             this.onMouseDown(e);
27707             return;
27708         }
27709         
27710         if(touches.length != 2){
27711             return;
27712         }
27713         
27714         var coords = [];
27715         
27716         for(var i = 0, finger; finger = touches[i]; i++){
27717             coords.push(finger.pageX, finger.pageY);
27718         }
27719         
27720         var x = Math.pow(coords[0] - coords[2], 2);
27721         var y = Math.pow(coords[1] - coords[3], 2);
27722         
27723         this.startDistance = Math.sqrt(x + y);
27724         
27725         this.startScale = this.scale;
27726         
27727         this.pinching = true;
27728         this.dragable = false;
27729         
27730     },
27731     
27732     onTouchMove : function(e)
27733     {
27734         if(!this.pinching && !this.dragable){
27735             return;
27736         }
27737         
27738         var touches = e.browserEvent.touches;
27739         
27740         if(!touches){
27741             return;
27742         }
27743         
27744         if(this.dragable){
27745             this.onMouseMove(e);
27746             return;
27747         }
27748         
27749         var coords = [];
27750         
27751         for(var i = 0, finger; finger = touches[i]; i++){
27752             coords.push(finger.pageX, finger.pageY);
27753         }
27754         
27755         var x = Math.pow(coords[0] - coords[2], 2);
27756         var y = Math.pow(coords[1] - coords[3], 2);
27757         
27758         this.endDistance = Math.sqrt(x + y);
27759         
27760         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27761         
27762         if(!this.zoomable()){
27763             this.scale = this.startScale;
27764             return;
27765         }
27766         
27767         this.draw();
27768         
27769     },
27770     
27771     onTouchEnd : function(e)
27772     {
27773         this.pinching = false;
27774         this.dragable = false;
27775         
27776     },
27777     
27778     process : function(file, crop)
27779     {
27780         if(this.loadMask){
27781             this.maskEl.mask(this.loadingText);
27782         }
27783         
27784         this.xhr = new XMLHttpRequest();
27785         
27786         file.xhr = this.xhr;
27787
27788         this.xhr.open(this.method, this.url, true);
27789         
27790         var headers = {
27791             "Accept": "application/json",
27792             "Cache-Control": "no-cache",
27793             "X-Requested-With": "XMLHttpRequest"
27794         };
27795         
27796         for (var headerName in headers) {
27797             var headerValue = headers[headerName];
27798             if (headerValue) {
27799                 this.xhr.setRequestHeader(headerName, headerValue);
27800             }
27801         }
27802         
27803         var _this = this;
27804         
27805         this.xhr.onload = function()
27806         {
27807             _this.xhrOnLoad(_this.xhr);
27808         }
27809         
27810         this.xhr.onerror = function()
27811         {
27812             _this.xhrOnError(_this.xhr);
27813         }
27814         
27815         var formData = new FormData();
27816
27817         formData.append('returnHTML', 'NO');
27818         
27819         if(crop){
27820             formData.append('crop', crop);
27821         }
27822         
27823         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27824             formData.append(this.paramName, file, file.name);
27825         }
27826         
27827         if(typeof(file.filename) != 'undefined'){
27828             formData.append('filename', file.filename);
27829         }
27830         
27831         if(typeof(file.mimetype) != 'undefined'){
27832             formData.append('mimetype', file.mimetype);
27833         }
27834         
27835         if(this.fireEvent('arrange', this, formData) != false){
27836             this.xhr.send(formData);
27837         };
27838     },
27839     
27840     xhrOnLoad : function(xhr)
27841     {
27842         if(this.loadMask){
27843             this.maskEl.unmask();
27844         }
27845         
27846         if (xhr.readyState !== 4) {
27847             this.fireEvent('exception', this, xhr);
27848             return;
27849         }
27850
27851         var response = Roo.decode(xhr.responseText);
27852         
27853         if(!response.success){
27854             this.fireEvent('exception', this, xhr);
27855             return;
27856         }
27857         
27858         var response = Roo.decode(xhr.responseText);
27859         
27860         this.fireEvent('upload', this, response);
27861         
27862     },
27863     
27864     xhrOnError : function()
27865     {
27866         if(this.loadMask){
27867             this.maskEl.unmask();
27868         }
27869         
27870         Roo.log('xhr on error');
27871         
27872         var response = Roo.decode(xhr.responseText);
27873           
27874         Roo.log(response);
27875         
27876     },
27877     
27878     prepare : function(file)
27879     {   
27880         if(this.loadMask){
27881             this.maskEl.mask(this.loadingText);
27882         }
27883         
27884         this.file = false;
27885         this.exif = {};
27886         
27887         if(typeof(file) === 'string'){
27888             this.loadCanvas(file);
27889             return;
27890         }
27891         
27892         if(!file || !this.urlAPI){
27893             return;
27894         }
27895         
27896         this.file = file;
27897         this.cropType = file.type;
27898         
27899         var _this = this;
27900         
27901         if(this.fireEvent('prepare', this, this.file) != false){
27902             
27903             var reader = new FileReader();
27904             
27905             reader.onload = function (e) {
27906                 if (e.target.error) {
27907                     Roo.log(e.target.error);
27908                     return;
27909                 }
27910                 
27911                 var buffer = e.target.result,
27912                     dataView = new DataView(buffer),
27913                     offset = 2,
27914                     maxOffset = dataView.byteLength - 4,
27915                     markerBytes,
27916                     markerLength;
27917                 
27918                 if (dataView.getUint16(0) === 0xffd8) {
27919                     while (offset < maxOffset) {
27920                         markerBytes = dataView.getUint16(offset);
27921                         
27922                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27923                             markerLength = dataView.getUint16(offset + 2) + 2;
27924                             if (offset + markerLength > dataView.byteLength) {
27925                                 Roo.log('Invalid meta data: Invalid segment size.');
27926                                 break;
27927                             }
27928                             
27929                             if(markerBytes == 0xffe1){
27930                                 _this.parseExifData(
27931                                     dataView,
27932                                     offset,
27933                                     markerLength
27934                                 );
27935                             }
27936                             
27937                             offset += markerLength;
27938                             
27939                             continue;
27940                         }
27941                         
27942                         break;
27943                     }
27944                     
27945                 }
27946                 
27947                 var url = _this.urlAPI.createObjectURL(_this.file);
27948                 
27949                 _this.loadCanvas(url);
27950                 
27951                 return;
27952             }
27953             
27954             reader.readAsArrayBuffer(this.file);
27955             
27956         }
27957         
27958     },
27959     
27960     parseExifData : function(dataView, offset, length)
27961     {
27962         var tiffOffset = offset + 10,
27963             littleEndian,
27964             dirOffset;
27965     
27966         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27967             // No Exif data, might be XMP data instead
27968             return;
27969         }
27970         
27971         // Check for the ASCII code for "Exif" (0x45786966):
27972         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27973             // No Exif data, might be XMP data instead
27974             return;
27975         }
27976         if (tiffOffset + 8 > dataView.byteLength) {
27977             Roo.log('Invalid Exif data: Invalid segment size.');
27978             return;
27979         }
27980         // Check for the two null bytes:
27981         if (dataView.getUint16(offset + 8) !== 0x0000) {
27982             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27983             return;
27984         }
27985         // Check the byte alignment:
27986         switch (dataView.getUint16(tiffOffset)) {
27987         case 0x4949:
27988             littleEndian = true;
27989             break;
27990         case 0x4D4D:
27991             littleEndian = false;
27992             break;
27993         default:
27994             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27995             return;
27996         }
27997         // Check for the TIFF tag marker (0x002A):
27998         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27999             Roo.log('Invalid Exif data: Missing TIFF marker.');
28000             return;
28001         }
28002         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28003         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28004         
28005         this.parseExifTags(
28006             dataView,
28007             tiffOffset,
28008             tiffOffset + dirOffset,
28009             littleEndian
28010         );
28011     },
28012     
28013     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28014     {
28015         var tagsNumber,
28016             dirEndOffset,
28017             i;
28018         if (dirOffset + 6 > dataView.byteLength) {
28019             Roo.log('Invalid Exif data: Invalid directory offset.');
28020             return;
28021         }
28022         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28023         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28024         if (dirEndOffset + 4 > dataView.byteLength) {
28025             Roo.log('Invalid Exif data: Invalid directory size.');
28026             return;
28027         }
28028         for (i = 0; i < tagsNumber; i += 1) {
28029             this.parseExifTag(
28030                 dataView,
28031                 tiffOffset,
28032                 dirOffset + 2 + 12 * i, // tag offset
28033                 littleEndian
28034             );
28035         }
28036         // Return the offset to the next directory:
28037         return dataView.getUint32(dirEndOffset, littleEndian);
28038     },
28039     
28040     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28041     {
28042         var tag = dataView.getUint16(offset, littleEndian);
28043         
28044         this.exif[tag] = this.getExifValue(
28045             dataView,
28046             tiffOffset,
28047             offset,
28048             dataView.getUint16(offset + 2, littleEndian), // tag type
28049             dataView.getUint32(offset + 4, littleEndian), // tag length
28050             littleEndian
28051         );
28052     },
28053     
28054     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28055     {
28056         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28057             tagSize,
28058             dataOffset,
28059             values,
28060             i,
28061             str,
28062             c;
28063     
28064         if (!tagType) {
28065             Roo.log('Invalid Exif data: Invalid tag type.');
28066             return;
28067         }
28068         
28069         tagSize = tagType.size * length;
28070         // Determine if the value is contained in the dataOffset bytes,
28071         // or if the value at the dataOffset is a pointer to the actual data:
28072         dataOffset = tagSize > 4 ?
28073                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28074         if (dataOffset + tagSize > dataView.byteLength) {
28075             Roo.log('Invalid Exif data: Invalid data offset.');
28076             return;
28077         }
28078         if (length === 1) {
28079             return tagType.getValue(dataView, dataOffset, littleEndian);
28080         }
28081         values = [];
28082         for (i = 0; i < length; i += 1) {
28083             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28084         }
28085         
28086         if (tagType.ascii) {
28087             str = '';
28088             // Concatenate the chars:
28089             for (i = 0; i < values.length; i += 1) {
28090                 c = values[i];
28091                 // Ignore the terminating NULL byte(s):
28092                 if (c === '\u0000') {
28093                     break;
28094                 }
28095                 str += c;
28096             }
28097             return str;
28098         }
28099         return values;
28100     }
28101     
28102 });
28103
28104 Roo.apply(Roo.bootstrap.UploadCropbox, {
28105     tags : {
28106         'Orientation': 0x0112
28107     },
28108     
28109     Orientation: {
28110             1: 0, //'top-left',
28111 //            2: 'top-right',
28112             3: 180, //'bottom-right',
28113 //            4: 'bottom-left',
28114 //            5: 'left-top',
28115             6: 90, //'right-top',
28116 //            7: 'right-bottom',
28117             8: 270 //'left-bottom'
28118     },
28119     
28120     exifTagTypes : {
28121         // byte, 8-bit unsigned int:
28122         1: {
28123             getValue: function (dataView, dataOffset) {
28124                 return dataView.getUint8(dataOffset);
28125             },
28126             size: 1
28127         },
28128         // ascii, 8-bit byte:
28129         2: {
28130             getValue: function (dataView, dataOffset) {
28131                 return String.fromCharCode(dataView.getUint8(dataOffset));
28132             },
28133             size: 1,
28134             ascii: true
28135         },
28136         // short, 16 bit int:
28137         3: {
28138             getValue: function (dataView, dataOffset, littleEndian) {
28139                 return dataView.getUint16(dataOffset, littleEndian);
28140             },
28141             size: 2
28142         },
28143         // long, 32 bit int:
28144         4: {
28145             getValue: function (dataView, dataOffset, littleEndian) {
28146                 return dataView.getUint32(dataOffset, littleEndian);
28147             },
28148             size: 4
28149         },
28150         // rational = two long values, first is numerator, second is denominator:
28151         5: {
28152             getValue: function (dataView, dataOffset, littleEndian) {
28153                 return dataView.getUint32(dataOffset, littleEndian) /
28154                     dataView.getUint32(dataOffset + 4, littleEndian);
28155             },
28156             size: 8
28157         },
28158         // slong, 32 bit signed int:
28159         9: {
28160             getValue: function (dataView, dataOffset, littleEndian) {
28161                 return dataView.getInt32(dataOffset, littleEndian);
28162             },
28163             size: 4
28164         },
28165         // srational, two slongs, first is numerator, second is denominator:
28166         10: {
28167             getValue: function (dataView, dataOffset, littleEndian) {
28168                 return dataView.getInt32(dataOffset, littleEndian) /
28169                     dataView.getInt32(dataOffset + 4, littleEndian);
28170             },
28171             size: 8
28172         }
28173     },
28174     
28175     footer : {
28176         STANDARD : [
28177             {
28178                 tag : 'div',
28179                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28180                 action : 'rotate-left',
28181                 cn : [
28182                     {
28183                         tag : 'button',
28184                         cls : 'btn btn-default',
28185                         html : '<i class="fa fa-undo"></i>'
28186                     }
28187                 ]
28188             },
28189             {
28190                 tag : 'div',
28191                 cls : 'btn-group roo-upload-cropbox-picture',
28192                 action : 'picture',
28193                 cn : [
28194                     {
28195                         tag : 'button',
28196                         cls : 'btn btn-default',
28197                         html : '<i class="fa fa-picture-o"></i>'
28198                     }
28199                 ]
28200             },
28201             {
28202                 tag : 'div',
28203                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28204                 action : 'rotate-right',
28205                 cn : [
28206                     {
28207                         tag : 'button',
28208                         cls : 'btn btn-default',
28209                         html : '<i class="fa fa-repeat"></i>'
28210                     }
28211                 ]
28212             }
28213         ],
28214         DOCUMENT : [
28215             {
28216                 tag : 'div',
28217                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28218                 action : 'rotate-left',
28219                 cn : [
28220                     {
28221                         tag : 'button',
28222                         cls : 'btn btn-default',
28223                         html : '<i class="fa fa-undo"></i>'
28224                     }
28225                 ]
28226             },
28227             {
28228                 tag : 'div',
28229                 cls : 'btn-group roo-upload-cropbox-download',
28230                 action : 'download',
28231                 cn : [
28232                     {
28233                         tag : 'button',
28234                         cls : 'btn btn-default',
28235                         html : '<i class="fa fa-download"></i>'
28236                     }
28237                 ]
28238             },
28239             {
28240                 tag : 'div',
28241                 cls : 'btn-group roo-upload-cropbox-crop',
28242                 action : 'crop',
28243                 cn : [
28244                     {
28245                         tag : 'button',
28246                         cls : 'btn btn-default',
28247                         html : '<i class="fa fa-crop"></i>'
28248                     }
28249                 ]
28250             },
28251             {
28252                 tag : 'div',
28253                 cls : 'btn-group roo-upload-cropbox-trash',
28254                 action : 'trash',
28255                 cn : [
28256                     {
28257                         tag : 'button',
28258                         cls : 'btn btn-default',
28259                         html : '<i class="fa fa-trash"></i>'
28260                     }
28261                 ]
28262             },
28263             {
28264                 tag : 'div',
28265                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28266                 action : 'rotate-right',
28267                 cn : [
28268                     {
28269                         tag : 'button',
28270                         cls : 'btn btn-default',
28271                         html : '<i class="fa fa-repeat"></i>'
28272                     }
28273                 ]
28274             }
28275         ],
28276         ROTATOR : [
28277             {
28278                 tag : 'div',
28279                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28280                 action : 'rotate-left',
28281                 cn : [
28282                     {
28283                         tag : 'button',
28284                         cls : 'btn btn-default',
28285                         html : '<i class="fa fa-undo"></i>'
28286                     }
28287                 ]
28288             },
28289             {
28290                 tag : 'div',
28291                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28292                 action : 'rotate-right',
28293                 cn : [
28294                     {
28295                         tag : 'button',
28296                         cls : 'btn btn-default',
28297                         html : '<i class="fa fa-repeat"></i>'
28298                     }
28299                 ]
28300             }
28301         ]
28302     }
28303 });
28304
28305 /*
28306 * Licence: LGPL
28307 */
28308
28309 /**
28310  * @class Roo.bootstrap.DocumentManager
28311  * @extends Roo.bootstrap.Component
28312  * Bootstrap DocumentManager class
28313  * @cfg {String} paramName default 'imageUpload'
28314  * @cfg {String} toolTipName default 'filename'
28315  * @cfg {String} method default POST
28316  * @cfg {String} url action url
28317  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28318  * @cfg {Boolean} multiple multiple upload default true
28319  * @cfg {Number} thumbSize default 300
28320  * @cfg {String} fieldLabel
28321  * @cfg {Number} labelWidth default 4
28322  * @cfg {String} labelAlign (left|top) default left
28323  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28324 * @cfg {Number} labellg set the width of label (1-12)
28325  * @cfg {Number} labelmd set the width of label (1-12)
28326  * @cfg {Number} labelsm set the width of label (1-12)
28327  * @cfg {Number} labelxs set the width of label (1-12)
28328  * 
28329  * @constructor
28330  * Create a new DocumentManager
28331  * @param {Object} config The config object
28332  */
28333
28334 Roo.bootstrap.DocumentManager = function(config){
28335     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28336     
28337     this.files = [];
28338     this.delegates = [];
28339     
28340     this.addEvents({
28341         /**
28342          * @event initial
28343          * Fire when initial the DocumentManager
28344          * @param {Roo.bootstrap.DocumentManager} this
28345          */
28346         "initial" : true,
28347         /**
28348          * @event inspect
28349          * inspect selected file
28350          * @param {Roo.bootstrap.DocumentManager} this
28351          * @param {File} file
28352          */
28353         "inspect" : true,
28354         /**
28355          * @event exception
28356          * Fire when xhr load exception
28357          * @param {Roo.bootstrap.DocumentManager} this
28358          * @param {XMLHttpRequest} xhr
28359          */
28360         "exception" : true,
28361         /**
28362          * @event afterupload
28363          * Fire when xhr load exception
28364          * @param {Roo.bootstrap.DocumentManager} this
28365          * @param {XMLHttpRequest} xhr
28366          */
28367         "afterupload" : true,
28368         /**
28369          * @event prepare
28370          * prepare the form data
28371          * @param {Roo.bootstrap.DocumentManager} this
28372          * @param {Object} formData
28373          */
28374         "prepare" : true,
28375         /**
28376          * @event remove
28377          * Fire when remove the file
28378          * @param {Roo.bootstrap.DocumentManager} this
28379          * @param {Object} file
28380          */
28381         "remove" : true,
28382         /**
28383          * @event refresh
28384          * Fire after refresh the file
28385          * @param {Roo.bootstrap.DocumentManager} this
28386          */
28387         "refresh" : true,
28388         /**
28389          * @event click
28390          * Fire after click the image
28391          * @param {Roo.bootstrap.DocumentManager} this
28392          * @param {Object} file
28393          */
28394         "click" : true,
28395         /**
28396          * @event edit
28397          * Fire when upload a image and editable set to true
28398          * @param {Roo.bootstrap.DocumentManager} this
28399          * @param {Object} file
28400          */
28401         "edit" : true,
28402         /**
28403          * @event beforeselectfile
28404          * Fire before select file
28405          * @param {Roo.bootstrap.DocumentManager} this
28406          */
28407         "beforeselectfile" : true,
28408         /**
28409          * @event process
28410          * Fire before process file
28411          * @param {Roo.bootstrap.DocumentManager} this
28412          * @param {Object} file
28413          */
28414         "process" : true
28415         
28416     });
28417 };
28418
28419 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28420     
28421     boxes : 0,
28422     inputName : '',
28423     thumbSize : 300,
28424     multiple : true,
28425     files : false,
28426     method : 'POST',
28427     url : '',
28428     paramName : 'imageUpload',
28429     toolTipName : 'filename',
28430     fieldLabel : '',
28431     labelWidth : 4,
28432     labelAlign : 'left',
28433     editable : true,
28434     delegates : false,
28435     xhr : false, 
28436     
28437     labellg : 0,
28438     labelmd : 0,
28439     labelsm : 0,
28440     labelxs : 0,
28441     
28442     getAutoCreate : function()
28443     {   
28444         var managerWidget = {
28445             tag : 'div',
28446             cls : 'roo-document-manager',
28447             cn : [
28448                 {
28449                     tag : 'input',
28450                     cls : 'roo-document-manager-selector',
28451                     type : 'file'
28452                 },
28453                 {
28454                     tag : 'div',
28455                     cls : 'roo-document-manager-uploader',
28456                     cn : [
28457                         {
28458                             tag : 'div',
28459                             cls : 'roo-document-manager-upload-btn',
28460                             html : '<i class="fa fa-plus"></i>'
28461                         }
28462                     ]
28463                     
28464                 }
28465             ]
28466         };
28467         
28468         var content = [
28469             {
28470                 tag : 'div',
28471                 cls : 'column col-md-12',
28472                 cn : managerWidget
28473             }
28474         ];
28475         
28476         if(this.fieldLabel.length){
28477             
28478             content = [
28479                 {
28480                     tag : 'div',
28481                     cls : 'column col-md-12',
28482                     html : this.fieldLabel
28483                 },
28484                 {
28485                     tag : 'div',
28486                     cls : 'column col-md-12',
28487                     cn : managerWidget
28488                 }
28489             ];
28490
28491             if(this.labelAlign == 'left'){
28492                 content = [
28493                     {
28494                         tag : 'div',
28495                         cls : 'column',
28496                         html : this.fieldLabel
28497                     },
28498                     {
28499                         tag : 'div',
28500                         cls : 'column',
28501                         cn : managerWidget
28502                     }
28503                 ];
28504                 
28505                 if(this.labelWidth > 12){
28506                     content[0].style = "width: " + this.labelWidth + 'px';
28507                 }
28508
28509                 if(this.labelWidth < 13 && this.labelmd == 0){
28510                     this.labelmd = this.labelWidth;
28511                 }
28512
28513                 if(this.labellg > 0){
28514                     content[0].cls += ' col-lg-' + this.labellg;
28515                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28516                 }
28517
28518                 if(this.labelmd > 0){
28519                     content[0].cls += ' col-md-' + this.labelmd;
28520                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28521                 }
28522
28523                 if(this.labelsm > 0){
28524                     content[0].cls += ' col-sm-' + this.labelsm;
28525                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28526                 }
28527
28528                 if(this.labelxs > 0){
28529                     content[0].cls += ' col-xs-' + this.labelxs;
28530                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28531                 }
28532                 
28533             }
28534         }
28535         
28536         var cfg = {
28537             tag : 'div',
28538             cls : 'row clearfix',
28539             cn : content
28540         };
28541         
28542         return cfg;
28543         
28544     },
28545     
28546     initEvents : function()
28547     {
28548         this.managerEl = this.el.select('.roo-document-manager', true).first();
28549         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28550         
28551         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28552         this.selectorEl.hide();
28553         
28554         if(this.multiple){
28555             this.selectorEl.attr('multiple', 'multiple');
28556         }
28557         
28558         this.selectorEl.on('change', this.onFileSelected, this);
28559         
28560         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28561         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28562         
28563         this.uploader.on('click', this.onUploaderClick, this);
28564         
28565         this.renderProgressDialog();
28566         
28567         var _this = this;
28568         
28569         window.addEventListener("resize", function() { _this.refresh(); } );
28570         
28571         this.fireEvent('initial', this);
28572     },
28573     
28574     renderProgressDialog : function()
28575     {
28576         var _this = this;
28577         
28578         this.progressDialog = new Roo.bootstrap.Modal({
28579             cls : 'roo-document-manager-progress-dialog',
28580             allow_close : false,
28581             title : '',
28582             buttons : [
28583                 {
28584                     name  :'cancel',
28585                     weight : 'danger',
28586                     html : 'Cancel'
28587                 }
28588             ], 
28589             listeners : { 
28590                 btnclick : function() {
28591                     _this.uploadCancel();
28592                     this.hide();
28593                 }
28594             }
28595         });
28596          
28597         this.progressDialog.render(Roo.get(document.body));
28598          
28599         this.progress = new Roo.bootstrap.Progress({
28600             cls : 'roo-document-manager-progress',
28601             active : true,
28602             striped : true
28603         });
28604         
28605         this.progress.render(this.progressDialog.getChildContainer());
28606         
28607         this.progressBar = new Roo.bootstrap.ProgressBar({
28608             cls : 'roo-document-manager-progress-bar',
28609             aria_valuenow : 0,
28610             aria_valuemin : 0,
28611             aria_valuemax : 12,
28612             panel : 'success'
28613         });
28614         
28615         this.progressBar.render(this.progress.getChildContainer());
28616     },
28617     
28618     onUploaderClick : function(e)
28619     {
28620         e.preventDefault();
28621      
28622         if(this.fireEvent('beforeselectfile', this) != false){
28623             this.selectorEl.dom.click();
28624         }
28625         
28626     },
28627     
28628     onFileSelected : function(e)
28629     {
28630         e.preventDefault();
28631         
28632         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28633             return;
28634         }
28635         
28636         Roo.each(this.selectorEl.dom.files, function(file){
28637             if(this.fireEvent('inspect', this, file) != false){
28638                 this.files.push(file);
28639             }
28640         }, this);
28641         
28642         this.queue();
28643         
28644     },
28645     
28646     queue : function()
28647     {
28648         this.selectorEl.dom.value = '';
28649         
28650         if(!this.files.length){
28651             return;
28652         }
28653         
28654         if(this.boxes > 0 && this.files.length > this.boxes){
28655             this.files = this.files.slice(0, this.boxes);
28656         }
28657         
28658         this.uploader.show();
28659         
28660         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28661             this.uploader.hide();
28662         }
28663         
28664         var _this = this;
28665         
28666         var files = [];
28667         
28668         var docs = [];
28669         
28670         Roo.each(this.files, function(file){
28671             
28672             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28673                 var f = this.renderPreview(file);
28674                 files.push(f);
28675                 return;
28676             }
28677             
28678             if(file.type.indexOf('image') != -1){
28679                 this.delegates.push(
28680                     (function(){
28681                         _this.process(file);
28682                     }).createDelegate(this)
28683                 );
28684         
28685                 return;
28686             }
28687             
28688             docs.push(
28689                 (function(){
28690                     _this.process(file);
28691                 }).createDelegate(this)
28692             );
28693             
28694         }, this);
28695         
28696         this.files = files;
28697         
28698         this.delegates = this.delegates.concat(docs);
28699         
28700         if(!this.delegates.length){
28701             this.refresh();
28702             return;
28703         }
28704         
28705         this.progressBar.aria_valuemax = this.delegates.length;
28706         
28707         this.arrange();
28708         
28709         return;
28710     },
28711     
28712     arrange : function()
28713     {
28714         if(!this.delegates.length){
28715             this.progressDialog.hide();
28716             this.refresh();
28717             return;
28718         }
28719         
28720         var delegate = this.delegates.shift();
28721         
28722         this.progressDialog.show();
28723         
28724         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28725         
28726         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28727         
28728         delegate();
28729     },
28730     
28731     refresh : function()
28732     {
28733         this.uploader.show();
28734         
28735         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28736             this.uploader.hide();
28737         }
28738         
28739         Roo.isTouch ? this.closable(false) : this.closable(true);
28740         
28741         this.fireEvent('refresh', this);
28742     },
28743     
28744     onRemove : function(e, el, o)
28745     {
28746         e.preventDefault();
28747         
28748         this.fireEvent('remove', this, o);
28749         
28750     },
28751     
28752     remove : function(o)
28753     {
28754         var files = [];
28755         
28756         Roo.each(this.files, function(file){
28757             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28758                 files.push(file);
28759                 return;
28760             }
28761
28762             o.target.remove();
28763
28764         }, this);
28765         
28766         this.files = files;
28767         
28768         this.refresh();
28769     },
28770     
28771     clear : function()
28772     {
28773         Roo.each(this.files, function(file){
28774             if(!file.target){
28775                 return;
28776             }
28777             
28778             file.target.remove();
28779
28780         }, this);
28781         
28782         this.files = [];
28783         
28784         this.refresh();
28785     },
28786     
28787     onClick : function(e, el, o)
28788     {
28789         e.preventDefault();
28790         
28791         this.fireEvent('click', this, o);
28792         
28793     },
28794     
28795     closable : function(closable)
28796     {
28797         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28798             
28799             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28800             
28801             if(closable){
28802                 el.show();
28803                 return;
28804             }
28805             
28806             el.hide();
28807             
28808         }, this);
28809     },
28810     
28811     xhrOnLoad : function(xhr)
28812     {
28813         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28814             el.remove();
28815         }, this);
28816         
28817         if (xhr.readyState !== 4) {
28818             this.arrange();
28819             this.fireEvent('exception', this, xhr);
28820             return;
28821         }
28822
28823         var response = Roo.decode(xhr.responseText);
28824         
28825         if(!response.success){
28826             this.arrange();
28827             this.fireEvent('exception', this, xhr);
28828             return;
28829         }
28830         
28831         var file = this.renderPreview(response.data);
28832         
28833         this.files.push(file);
28834         
28835         this.arrange();
28836         
28837         this.fireEvent('afterupload', this, xhr);
28838         
28839     },
28840     
28841     xhrOnError : function(xhr)
28842     {
28843         Roo.log('xhr on error');
28844         
28845         var response = Roo.decode(xhr.responseText);
28846           
28847         Roo.log(response);
28848         
28849         this.arrange();
28850     },
28851     
28852     process : function(file)
28853     {
28854         if(this.fireEvent('process', this, file) !== false){
28855             if(this.editable && file.type.indexOf('image') != -1){
28856                 this.fireEvent('edit', this, file);
28857                 return;
28858             }
28859
28860             this.uploadStart(file, false);
28861
28862             return;
28863         }
28864         
28865     },
28866     
28867     uploadStart : function(file, crop)
28868     {
28869         this.xhr = new XMLHttpRequest();
28870         
28871         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28872             this.arrange();
28873             return;
28874         }
28875         
28876         file.xhr = this.xhr;
28877             
28878         this.managerEl.createChild({
28879             tag : 'div',
28880             cls : 'roo-document-manager-loading',
28881             cn : [
28882                 {
28883                     tag : 'div',
28884                     tooltip : file.name,
28885                     cls : 'roo-document-manager-thumb',
28886                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28887                 }
28888             ]
28889
28890         });
28891
28892         this.xhr.open(this.method, this.url, true);
28893         
28894         var headers = {
28895             "Accept": "application/json",
28896             "Cache-Control": "no-cache",
28897             "X-Requested-With": "XMLHttpRequest"
28898         };
28899         
28900         for (var headerName in headers) {
28901             var headerValue = headers[headerName];
28902             if (headerValue) {
28903                 this.xhr.setRequestHeader(headerName, headerValue);
28904             }
28905         }
28906         
28907         var _this = this;
28908         
28909         this.xhr.onload = function()
28910         {
28911             _this.xhrOnLoad(_this.xhr);
28912         }
28913         
28914         this.xhr.onerror = function()
28915         {
28916             _this.xhrOnError(_this.xhr);
28917         }
28918         
28919         var formData = new FormData();
28920
28921         formData.append('returnHTML', 'NO');
28922         
28923         if(crop){
28924             formData.append('crop', crop);
28925         }
28926         
28927         formData.append(this.paramName, file, file.name);
28928         
28929         var options = {
28930             file : file, 
28931             manually : false
28932         };
28933         
28934         if(this.fireEvent('prepare', this, formData, options) != false){
28935             
28936             if(options.manually){
28937                 return;
28938             }
28939             
28940             this.xhr.send(formData);
28941             return;
28942         };
28943         
28944         this.uploadCancel();
28945     },
28946     
28947     uploadCancel : function()
28948     {
28949         if (this.xhr) {
28950             this.xhr.abort();
28951         }
28952         
28953         this.delegates = [];
28954         
28955         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28956             el.remove();
28957         }, this);
28958         
28959         this.arrange();
28960     },
28961     
28962     renderPreview : function(file)
28963     {
28964         if(typeof(file.target) != 'undefined' && file.target){
28965             return file;
28966         }
28967         
28968         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28969         
28970         var previewEl = this.managerEl.createChild({
28971             tag : 'div',
28972             cls : 'roo-document-manager-preview',
28973             cn : [
28974                 {
28975                     tag : 'div',
28976                     tooltip : file[this.toolTipName],
28977                     cls : 'roo-document-manager-thumb',
28978                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28979                 },
28980                 {
28981                     tag : 'button',
28982                     cls : 'close',
28983                     html : '<i class="fa fa-times-circle"></i>'
28984                 }
28985             ]
28986         });
28987
28988         var close = previewEl.select('button.close', true).first();
28989
28990         close.on('click', this.onRemove, this, file);
28991
28992         file.target = previewEl;
28993
28994         var image = previewEl.select('img', true).first();
28995         
28996         var _this = this;
28997         
28998         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28999         
29000         image.on('click', this.onClick, this, file);
29001         
29002         return file;
29003         
29004     },
29005     
29006     onPreviewLoad : function(file, image)
29007     {
29008         if(typeof(file.target) == 'undefined' || !file.target){
29009             return;
29010         }
29011         
29012         var width = image.dom.naturalWidth || image.dom.width;
29013         var height = image.dom.naturalHeight || image.dom.height;
29014         
29015         if(width > height){
29016             file.target.addClass('wide');
29017             return;
29018         }
29019         
29020         file.target.addClass('tall');
29021         return;
29022         
29023     },
29024     
29025     uploadFromSource : function(file, crop)
29026     {
29027         this.xhr = new XMLHttpRequest();
29028         
29029         this.managerEl.createChild({
29030             tag : 'div',
29031             cls : 'roo-document-manager-loading',
29032             cn : [
29033                 {
29034                     tag : 'div',
29035                     tooltip : file.name,
29036                     cls : 'roo-document-manager-thumb',
29037                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29038                 }
29039             ]
29040
29041         });
29042
29043         this.xhr.open(this.method, this.url, true);
29044         
29045         var headers = {
29046             "Accept": "application/json",
29047             "Cache-Control": "no-cache",
29048             "X-Requested-With": "XMLHttpRequest"
29049         };
29050         
29051         for (var headerName in headers) {
29052             var headerValue = headers[headerName];
29053             if (headerValue) {
29054                 this.xhr.setRequestHeader(headerName, headerValue);
29055             }
29056         }
29057         
29058         var _this = this;
29059         
29060         this.xhr.onload = function()
29061         {
29062             _this.xhrOnLoad(_this.xhr);
29063         }
29064         
29065         this.xhr.onerror = function()
29066         {
29067             _this.xhrOnError(_this.xhr);
29068         }
29069         
29070         var formData = new FormData();
29071
29072         formData.append('returnHTML', 'NO');
29073         
29074         formData.append('crop', crop);
29075         
29076         if(typeof(file.filename) != 'undefined'){
29077             formData.append('filename', file.filename);
29078         }
29079         
29080         if(typeof(file.mimetype) != 'undefined'){
29081             formData.append('mimetype', file.mimetype);
29082         }
29083         
29084         Roo.log(formData);
29085         
29086         if(this.fireEvent('prepare', this, formData) != false){
29087             this.xhr.send(formData);
29088         };
29089     }
29090 });
29091
29092 /*
29093 * Licence: LGPL
29094 */
29095
29096 /**
29097  * @class Roo.bootstrap.DocumentViewer
29098  * @extends Roo.bootstrap.Component
29099  * Bootstrap DocumentViewer class
29100  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29101  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29102  * 
29103  * @constructor
29104  * Create a new DocumentViewer
29105  * @param {Object} config The config object
29106  */
29107
29108 Roo.bootstrap.DocumentViewer = function(config){
29109     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29110     
29111     this.addEvents({
29112         /**
29113          * @event initial
29114          * Fire after initEvent
29115          * @param {Roo.bootstrap.DocumentViewer} this
29116          */
29117         "initial" : true,
29118         /**
29119          * @event click
29120          * Fire after click
29121          * @param {Roo.bootstrap.DocumentViewer} this
29122          */
29123         "click" : true,
29124         /**
29125          * @event download
29126          * Fire after download button
29127          * @param {Roo.bootstrap.DocumentViewer} this
29128          */
29129         "download" : true,
29130         /**
29131          * @event trash
29132          * Fire after trash button
29133          * @param {Roo.bootstrap.DocumentViewer} this
29134          */
29135         "trash" : true
29136         
29137     });
29138 };
29139
29140 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29141     
29142     showDownload : true,
29143     
29144     showTrash : true,
29145     
29146     getAutoCreate : function()
29147     {
29148         var cfg = {
29149             tag : 'div',
29150             cls : 'roo-document-viewer',
29151             cn : [
29152                 {
29153                     tag : 'div',
29154                     cls : 'roo-document-viewer-body',
29155                     cn : [
29156                         {
29157                             tag : 'div',
29158                             cls : 'roo-document-viewer-thumb',
29159                             cn : [
29160                                 {
29161                                     tag : 'img',
29162                                     cls : 'roo-document-viewer-image'
29163                                 }
29164                             ]
29165                         }
29166                     ]
29167                 },
29168                 {
29169                     tag : 'div',
29170                     cls : 'roo-document-viewer-footer',
29171                     cn : {
29172                         tag : 'div',
29173                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29174                         cn : [
29175                             {
29176                                 tag : 'div',
29177                                 cls : 'btn-group roo-document-viewer-download',
29178                                 cn : [
29179                                     {
29180                                         tag : 'button',
29181                                         cls : 'btn btn-default',
29182                                         html : '<i class="fa fa-download"></i>'
29183                                     }
29184                                 ]
29185                             },
29186                             {
29187                                 tag : 'div',
29188                                 cls : 'btn-group roo-document-viewer-trash',
29189                                 cn : [
29190                                     {
29191                                         tag : 'button',
29192                                         cls : 'btn btn-default',
29193                                         html : '<i class="fa fa-trash"></i>'
29194                                     }
29195                                 ]
29196                             }
29197                         ]
29198                     }
29199                 }
29200             ]
29201         };
29202         
29203         return cfg;
29204     },
29205     
29206     initEvents : function()
29207     {
29208         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29209         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29210         
29211         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29212         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29213         
29214         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29215         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29216         
29217         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29218         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29219         
29220         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29221         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29222         
29223         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29224         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29225         
29226         this.bodyEl.on('click', this.onClick, this);
29227         this.downloadBtn.on('click', this.onDownload, this);
29228         this.trashBtn.on('click', this.onTrash, this);
29229         
29230         this.downloadBtn.hide();
29231         this.trashBtn.hide();
29232         
29233         if(this.showDownload){
29234             this.downloadBtn.show();
29235         }
29236         
29237         if(this.showTrash){
29238             this.trashBtn.show();
29239         }
29240         
29241         if(!this.showDownload && !this.showTrash) {
29242             this.footerEl.hide();
29243         }
29244         
29245     },
29246     
29247     initial : function()
29248     {
29249         this.fireEvent('initial', this);
29250         
29251     },
29252     
29253     onClick : function(e)
29254     {
29255         e.preventDefault();
29256         
29257         this.fireEvent('click', this);
29258     },
29259     
29260     onDownload : function(e)
29261     {
29262         e.preventDefault();
29263         
29264         this.fireEvent('download', this);
29265     },
29266     
29267     onTrash : function(e)
29268     {
29269         e.preventDefault();
29270         
29271         this.fireEvent('trash', this);
29272     }
29273     
29274 });
29275 /*
29276  * - LGPL
29277  *
29278  * nav progress bar
29279  * 
29280  */
29281
29282 /**
29283  * @class Roo.bootstrap.NavProgressBar
29284  * @extends Roo.bootstrap.Component
29285  * Bootstrap NavProgressBar class
29286  * 
29287  * @constructor
29288  * Create a new nav progress bar
29289  * @param {Object} config The config object
29290  */
29291
29292 Roo.bootstrap.NavProgressBar = function(config){
29293     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29294
29295     this.bullets = this.bullets || [];
29296    
29297 //    Roo.bootstrap.NavProgressBar.register(this);
29298      this.addEvents({
29299         /**
29300              * @event changed
29301              * Fires when the active item changes
29302              * @param {Roo.bootstrap.NavProgressBar} this
29303              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29304              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29305          */
29306         'changed': true
29307      });
29308     
29309 };
29310
29311 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29312     
29313     bullets : [],
29314     barItems : [],
29315     
29316     getAutoCreate : function()
29317     {
29318         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29319         
29320         cfg = {
29321             tag : 'div',
29322             cls : 'roo-navigation-bar-group',
29323             cn : [
29324                 {
29325                     tag : 'div',
29326                     cls : 'roo-navigation-top-bar'
29327                 },
29328                 {
29329                     tag : 'div',
29330                     cls : 'roo-navigation-bullets-bar',
29331                     cn : [
29332                         {
29333                             tag : 'ul',
29334                             cls : 'roo-navigation-bar'
29335                         }
29336                     ]
29337                 },
29338                 
29339                 {
29340                     tag : 'div',
29341                     cls : 'roo-navigation-bottom-bar'
29342                 }
29343             ]
29344             
29345         };
29346         
29347         return cfg;
29348         
29349     },
29350     
29351     initEvents: function() 
29352     {
29353         
29354     },
29355     
29356     onRender : function(ct, position) 
29357     {
29358         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29359         
29360         if(this.bullets.length){
29361             Roo.each(this.bullets, function(b){
29362                this.addItem(b);
29363             }, this);
29364         }
29365         
29366         this.format();
29367         
29368     },
29369     
29370     addItem : function(cfg)
29371     {
29372         var item = new Roo.bootstrap.NavProgressItem(cfg);
29373         
29374         item.parentId = this.id;
29375         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29376         
29377         if(cfg.html){
29378             var top = new Roo.bootstrap.Element({
29379                 tag : 'div',
29380                 cls : 'roo-navigation-bar-text'
29381             });
29382             
29383             var bottom = new Roo.bootstrap.Element({
29384                 tag : 'div',
29385                 cls : 'roo-navigation-bar-text'
29386             });
29387             
29388             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29389             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29390             
29391             var topText = new Roo.bootstrap.Element({
29392                 tag : 'span',
29393                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29394             });
29395             
29396             var bottomText = new Roo.bootstrap.Element({
29397                 tag : 'span',
29398                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29399             });
29400             
29401             topText.onRender(top.el, null);
29402             bottomText.onRender(bottom.el, null);
29403             
29404             item.topEl = top;
29405             item.bottomEl = bottom;
29406         }
29407         
29408         this.barItems.push(item);
29409         
29410         return item;
29411     },
29412     
29413     getActive : function()
29414     {
29415         var active = false;
29416         
29417         Roo.each(this.barItems, function(v){
29418             
29419             if (!v.isActive()) {
29420                 return;
29421             }
29422             
29423             active = v;
29424             return false;
29425             
29426         });
29427         
29428         return active;
29429     },
29430     
29431     setActiveItem : function(item)
29432     {
29433         var prev = false;
29434         
29435         Roo.each(this.barItems, function(v){
29436             if (v.rid == item.rid) {
29437                 return ;
29438             }
29439             
29440             if (v.isActive()) {
29441                 v.setActive(false);
29442                 prev = v;
29443             }
29444         });
29445
29446         item.setActive(true);
29447         
29448         this.fireEvent('changed', this, item, prev);
29449     },
29450     
29451     getBarItem: function(rid)
29452     {
29453         var ret = false;
29454         
29455         Roo.each(this.barItems, function(e) {
29456             if (e.rid != rid) {
29457                 return;
29458             }
29459             
29460             ret =  e;
29461             return false;
29462         });
29463         
29464         return ret;
29465     },
29466     
29467     indexOfItem : function(item)
29468     {
29469         var index = false;
29470         
29471         Roo.each(this.barItems, function(v, i){
29472             
29473             if (v.rid != item.rid) {
29474                 return;
29475             }
29476             
29477             index = i;
29478             return false
29479         });
29480         
29481         return index;
29482     },
29483     
29484     setActiveNext : function()
29485     {
29486         var i = this.indexOfItem(this.getActive());
29487         
29488         if (i > this.barItems.length) {
29489             return;
29490         }
29491         
29492         this.setActiveItem(this.barItems[i+1]);
29493     },
29494     
29495     setActivePrev : function()
29496     {
29497         var i = this.indexOfItem(this.getActive());
29498         
29499         if (i  < 1) {
29500             return;
29501         }
29502         
29503         this.setActiveItem(this.barItems[i-1]);
29504     },
29505     
29506     format : function()
29507     {
29508         if(!this.barItems.length){
29509             return;
29510         }
29511      
29512         var width = 100 / this.barItems.length;
29513         
29514         Roo.each(this.barItems, function(i){
29515             i.el.setStyle('width', width + '%');
29516             i.topEl.el.setStyle('width', width + '%');
29517             i.bottomEl.el.setStyle('width', width + '%');
29518         }, this);
29519         
29520     }
29521     
29522 });
29523 /*
29524  * - LGPL
29525  *
29526  * Nav Progress Item
29527  * 
29528  */
29529
29530 /**
29531  * @class Roo.bootstrap.NavProgressItem
29532  * @extends Roo.bootstrap.Component
29533  * Bootstrap NavProgressItem class
29534  * @cfg {String} rid the reference id
29535  * @cfg {Boolean} active (true|false) Is item active default false
29536  * @cfg {Boolean} disabled (true|false) Is item active default false
29537  * @cfg {String} html
29538  * @cfg {String} position (top|bottom) text position default bottom
29539  * @cfg {String} icon show icon instead of number
29540  * 
29541  * @constructor
29542  * Create a new NavProgressItem
29543  * @param {Object} config The config object
29544  */
29545 Roo.bootstrap.NavProgressItem = function(config){
29546     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29547     this.addEvents({
29548         // raw events
29549         /**
29550          * @event click
29551          * The raw click event for the entire grid.
29552          * @param {Roo.bootstrap.NavProgressItem} this
29553          * @param {Roo.EventObject} e
29554          */
29555         "click" : true
29556     });
29557    
29558 };
29559
29560 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29561     
29562     rid : '',
29563     active : false,
29564     disabled : false,
29565     html : '',
29566     position : 'bottom',
29567     icon : false,
29568     
29569     getAutoCreate : function()
29570     {
29571         var iconCls = 'roo-navigation-bar-item-icon';
29572         
29573         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29574         
29575         var cfg = {
29576             tag: 'li',
29577             cls: 'roo-navigation-bar-item',
29578             cn : [
29579                 {
29580                     tag : 'i',
29581                     cls : iconCls
29582                 }
29583             ]
29584         };
29585         
29586         if(this.active){
29587             cfg.cls += ' active';
29588         }
29589         if(this.disabled){
29590             cfg.cls += ' disabled';
29591         }
29592         
29593         return cfg;
29594     },
29595     
29596     disable : function()
29597     {
29598         this.setDisabled(true);
29599     },
29600     
29601     enable : function()
29602     {
29603         this.setDisabled(false);
29604     },
29605     
29606     initEvents: function() 
29607     {
29608         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29609         
29610         this.iconEl.on('click', this.onClick, this);
29611     },
29612     
29613     onClick : function(e)
29614     {
29615         e.preventDefault();
29616         
29617         if(this.disabled){
29618             return;
29619         }
29620         
29621         if(this.fireEvent('click', this, e) === false){
29622             return;
29623         };
29624         
29625         this.parent().setActiveItem(this);
29626     },
29627     
29628     isActive: function () 
29629     {
29630         return this.active;
29631     },
29632     
29633     setActive : function(state)
29634     {
29635         if(this.active == state){
29636             return;
29637         }
29638         
29639         this.active = state;
29640         
29641         if (state) {
29642             this.el.addClass('active');
29643             return;
29644         }
29645         
29646         this.el.removeClass('active');
29647         
29648         return;
29649     },
29650     
29651     setDisabled : function(state)
29652     {
29653         if(this.disabled == state){
29654             return;
29655         }
29656         
29657         this.disabled = state;
29658         
29659         if (state) {
29660             this.el.addClass('disabled');
29661             return;
29662         }
29663         
29664         this.el.removeClass('disabled');
29665     },
29666     
29667     tooltipEl : function()
29668     {
29669         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29670     }
29671 });
29672  
29673
29674  /*
29675  * - LGPL
29676  *
29677  * FieldLabel
29678  * 
29679  */
29680
29681 /**
29682  * @class Roo.bootstrap.FieldLabel
29683  * @extends Roo.bootstrap.Component
29684  * Bootstrap FieldLabel class
29685  * @cfg {String} html contents of the element
29686  * @cfg {String} tag tag of the element default label
29687  * @cfg {String} cls class of the element
29688  * @cfg {String} target label target 
29689  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29690  * @cfg {String} invalidClass default "text-warning"
29691  * @cfg {String} validClass default "text-success"
29692  * @cfg {String} iconTooltip default "This field is required"
29693  * @cfg {String} indicatorpos (left|right) default left
29694  * 
29695  * @constructor
29696  * Create a new FieldLabel
29697  * @param {Object} config The config object
29698  */
29699
29700 Roo.bootstrap.FieldLabel = function(config){
29701     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29702     
29703     this.addEvents({
29704             /**
29705              * @event invalid
29706              * Fires after the field has been marked as invalid.
29707              * @param {Roo.form.FieldLabel} this
29708              * @param {String} msg The validation message
29709              */
29710             invalid : true,
29711             /**
29712              * @event valid
29713              * Fires after the field has been validated with no errors.
29714              * @param {Roo.form.FieldLabel} this
29715              */
29716             valid : true
29717         });
29718 };
29719
29720 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29721     
29722     tag: 'label',
29723     cls: '',
29724     html: '',
29725     target: '',
29726     allowBlank : true,
29727     invalidClass : 'has-warning',
29728     validClass : 'has-success',
29729     iconTooltip : 'This field is required',
29730     indicatorpos : 'left',
29731     
29732     getAutoCreate : function(){
29733         
29734         var cfg = {
29735             tag : this.tag,
29736             cls : 'roo-bootstrap-field-label ' + this.cls,
29737             for : this.target,
29738             cn : [
29739                 {
29740                     tag : 'i',
29741                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29742                     tooltip : this.iconTooltip
29743                 },
29744                 {
29745                     tag : 'span',
29746                     html : this.html
29747                 }
29748             ] 
29749         };
29750         
29751         if(this.indicatorpos == 'right'){
29752             var cfg = {
29753                 tag : this.tag,
29754                 cls : 'roo-bootstrap-field-label ' + this.cls,
29755                 for : this.target,
29756                 cn : [
29757                     {
29758                         tag : 'span',
29759                         html : this.html
29760                     },
29761                     {
29762                         tag : 'i',
29763                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29764                         tooltip : this.iconTooltip
29765                     }
29766                 ] 
29767             };
29768         }
29769         
29770         return cfg;
29771     },
29772     
29773     initEvents: function() 
29774     {
29775         Roo.bootstrap.Element.superclass.initEvents.call(this);
29776         
29777         this.indicator = this.indicatorEl();
29778         
29779         if(this.indicator){
29780             this.indicator.removeClass('visible');
29781             this.indicator.addClass('invisible');
29782         }
29783         
29784         Roo.bootstrap.FieldLabel.register(this);
29785     },
29786     
29787     indicatorEl : function()
29788     {
29789         var indicator = this.el.select('i.roo-required-indicator',true).first();
29790         
29791         if(!indicator){
29792             return false;
29793         }
29794         
29795         return indicator;
29796         
29797     },
29798     
29799     /**
29800      * Mark this field as valid
29801      */
29802     markValid : function()
29803     {
29804         if(this.indicator){
29805             this.indicator.removeClass('visible');
29806             this.indicator.addClass('invisible');
29807         }
29808         
29809         this.el.removeClass(this.invalidClass);
29810         
29811         this.el.addClass(this.validClass);
29812         
29813         this.fireEvent('valid', this);
29814     },
29815     
29816     /**
29817      * Mark this field as invalid
29818      * @param {String} msg The validation message
29819      */
29820     markInvalid : function(msg)
29821     {
29822         if(this.indicator){
29823             this.indicator.removeClass('invisible');
29824             this.indicator.addClass('visible');
29825         }
29826         
29827         this.el.removeClass(this.validClass);
29828         
29829         this.el.addClass(this.invalidClass);
29830         
29831         this.fireEvent('invalid', this, msg);
29832     }
29833     
29834    
29835 });
29836
29837 Roo.apply(Roo.bootstrap.FieldLabel, {
29838     
29839     groups: {},
29840     
29841      /**
29842     * register a FieldLabel Group
29843     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29844     */
29845     register : function(label)
29846     {
29847         if(this.groups.hasOwnProperty(label.target)){
29848             return;
29849         }
29850      
29851         this.groups[label.target] = label;
29852         
29853     },
29854     /**
29855     * fetch a FieldLabel Group based on the target
29856     * @param {string} target
29857     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29858     */
29859     get: function(target) {
29860         if (typeof(this.groups[target]) == 'undefined') {
29861             return false;
29862         }
29863         
29864         return this.groups[target] ;
29865     }
29866 });
29867
29868  
29869
29870  /*
29871  * - LGPL
29872  *
29873  * page DateSplitField.
29874  * 
29875  */
29876
29877
29878 /**
29879  * @class Roo.bootstrap.DateSplitField
29880  * @extends Roo.bootstrap.Component
29881  * Bootstrap DateSplitField class
29882  * @cfg {string} fieldLabel - the label associated
29883  * @cfg {Number} labelWidth set the width of label (0-12)
29884  * @cfg {String} labelAlign (top|left)
29885  * @cfg {Boolean} dayAllowBlank (true|false) default false
29886  * @cfg {Boolean} monthAllowBlank (true|false) default false
29887  * @cfg {Boolean} yearAllowBlank (true|false) default false
29888  * @cfg {string} dayPlaceholder 
29889  * @cfg {string} monthPlaceholder
29890  * @cfg {string} yearPlaceholder
29891  * @cfg {string} dayFormat default 'd'
29892  * @cfg {string} monthFormat default 'm'
29893  * @cfg {string} yearFormat default 'Y'
29894  * @cfg {Number} labellg set the width of label (1-12)
29895  * @cfg {Number} labelmd set the width of label (1-12)
29896  * @cfg {Number} labelsm set the width of label (1-12)
29897  * @cfg {Number} labelxs set the width of label (1-12)
29898
29899  *     
29900  * @constructor
29901  * Create a new DateSplitField
29902  * @param {Object} config The config object
29903  */
29904
29905 Roo.bootstrap.DateSplitField = function(config){
29906     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29907     
29908     this.addEvents({
29909         // raw events
29910          /**
29911          * @event years
29912          * getting the data of years
29913          * @param {Roo.bootstrap.DateSplitField} this
29914          * @param {Object} years
29915          */
29916         "years" : true,
29917         /**
29918          * @event days
29919          * getting the data of days
29920          * @param {Roo.bootstrap.DateSplitField} this
29921          * @param {Object} days
29922          */
29923         "days" : true,
29924         /**
29925          * @event invalid
29926          * Fires after the field has been marked as invalid.
29927          * @param {Roo.form.Field} this
29928          * @param {String} msg The validation message
29929          */
29930         invalid : true,
29931        /**
29932          * @event valid
29933          * Fires after the field has been validated with no errors.
29934          * @param {Roo.form.Field} this
29935          */
29936         valid : true
29937     });
29938 };
29939
29940 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29941     
29942     fieldLabel : '',
29943     labelAlign : 'top',
29944     labelWidth : 3,
29945     dayAllowBlank : false,
29946     monthAllowBlank : false,
29947     yearAllowBlank : false,
29948     dayPlaceholder : '',
29949     monthPlaceholder : '',
29950     yearPlaceholder : '',
29951     dayFormat : 'd',
29952     monthFormat : 'm',
29953     yearFormat : 'Y',
29954     isFormField : true,
29955     labellg : 0,
29956     labelmd : 0,
29957     labelsm : 0,
29958     labelxs : 0,
29959     
29960     getAutoCreate : function()
29961     {
29962         var cfg = {
29963             tag : 'div',
29964             cls : 'row roo-date-split-field-group',
29965             cn : [
29966                 {
29967                     tag : 'input',
29968                     type : 'hidden',
29969                     cls : 'form-hidden-field roo-date-split-field-group-value',
29970                     name : this.name
29971                 }
29972             ]
29973         };
29974         
29975         var labelCls = 'col-md-12';
29976         var contentCls = 'col-md-4';
29977         
29978         if(this.fieldLabel){
29979             
29980             var label = {
29981                 tag : 'div',
29982                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29983                 cn : [
29984                     {
29985                         tag : 'label',
29986                         html : this.fieldLabel
29987                     }
29988                 ]
29989             };
29990             
29991             if(this.labelAlign == 'left'){
29992             
29993                 if(this.labelWidth > 12){
29994                     label.style = "width: " + this.labelWidth + 'px';
29995                 }
29996
29997                 if(this.labelWidth < 13 && this.labelmd == 0){
29998                     this.labelmd = this.labelWidth;
29999                 }
30000
30001                 if(this.labellg > 0){
30002                     labelCls = ' col-lg-' + this.labellg;
30003                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30004                 }
30005
30006                 if(this.labelmd > 0){
30007                     labelCls = ' col-md-' + this.labelmd;
30008                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30009                 }
30010
30011                 if(this.labelsm > 0){
30012                     labelCls = ' col-sm-' + this.labelsm;
30013                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30014                 }
30015
30016                 if(this.labelxs > 0){
30017                     labelCls = ' col-xs-' + this.labelxs;
30018                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30019                 }
30020             }
30021             
30022             label.cls += ' ' + labelCls;
30023             
30024             cfg.cn.push(label);
30025         }
30026         
30027         Roo.each(['day', 'month', 'year'], function(t){
30028             cfg.cn.push({
30029                 tag : 'div',
30030                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30031             });
30032         }, this);
30033         
30034         return cfg;
30035     },
30036     
30037     inputEl: function ()
30038     {
30039         return this.el.select('.roo-date-split-field-group-value', true).first();
30040     },
30041     
30042     onRender : function(ct, position) 
30043     {
30044         var _this = this;
30045         
30046         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30047         
30048         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30049         
30050         this.dayField = new Roo.bootstrap.ComboBox({
30051             allowBlank : this.dayAllowBlank,
30052             alwaysQuery : true,
30053             displayField : 'value',
30054             editable : false,
30055             fieldLabel : '',
30056             forceSelection : true,
30057             mode : 'local',
30058             placeholder : this.dayPlaceholder,
30059             selectOnFocus : true,
30060             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30061             triggerAction : 'all',
30062             typeAhead : true,
30063             valueField : 'value',
30064             store : new Roo.data.SimpleStore({
30065                 data : (function() {    
30066                     var days = [];
30067                     _this.fireEvent('days', _this, days);
30068                     return days;
30069                 })(),
30070                 fields : [ 'value' ]
30071             }),
30072             listeners : {
30073                 select : function (_self, record, index)
30074                 {
30075                     _this.setValue(_this.getValue());
30076                 }
30077             }
30078         });
30079
30080         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30081         
30082         this.monthField = new Roo.bootstrap.MonthField({
30083             after : '<i class=\"fa fa-calendar\"></i>',
30084             allowBlank : this.monthAllowBlank,
30085             placeholder : this.monthPlaceholder,
30086             readOnly : true,
30087             listeners : {
30088                 render : function (_self)
30089                 {
30090                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30091                         e.preventDefault();
30092                         _self.focus();
30093                     });
30094                 },
30095                 select : function (_self, oldvalue, newvalue)
30096                 {
30097                     _this.setValue(_this.getValue());
30098                 }
30099             }
30100         });
30101         
30102         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30103         
30104         this.yearField = new Roo.bootstrap.ComboBox({
30105             allowBlank : this.yearAllowBlank,
30106             alwaysQuery : true,
30107             displayField : 'value',
30108             editable : false,
30109             fieldLabel : '',
30110             forceSelection : true,
30111             mode : 'local',
30112             placeholder : this.yearPlaceholder,
30113             selectOnFocus : true,
30114             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30115             triggerAction : 'all',
30116             typeAhead : true,
30117             valueField : 'value',
30118             store : new Roo.data.SimpleStore({
30119                 data : (function() {
30120                     var years = [];
30121                     _this.fireEvent('years', _this, years);
30122                     return years;
30123                 })(),
30124                 fields : [ 'value' ]
30125             }),
30126             listeners : {
30127                 select : function (_self, record, index)
30128                 {
30129                     _this.setValue(_this.getValue());
30130                 }
30131             }
30132         });
30133
30134         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30135     },
30136     
30137     setValue : function(v, format)
30138     {
30139         this.inputEl.dom.value = v;
30140         
30141         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30142         
30143         var d = Date.parseDate(v, f);
30144         
30145         if(!d){
30146             this.validate();
30147             return;
30148         }
30149         
30150         this.setDay(d.format(this.dayFormat));
30151         this.setMonth(d.format(this.monthFormat));
30152         this.setYear(d.format(this.yearFormat));
30153         
30154         this.validate();
30155         
30156         return;
30157     },
30158     
30159     setDay : function(v)
30160     {
30161         this.dayField.setValue(v);
30162         this.inputEl.dom.value = this.getValue();
30163         this.validate();
30164         return;
30165     },
30166     
30167     setMonth : function(v)
30168     {
30169         this.monthField.setValue(v, true);
30170         this.inputEl.dom.value = this.getValue();
30171         this.validate();
30172         return;
30173     },
30174     
30175     setYear : function(v)
30176     {
30177         this.yearField.setValue(v);
30178         this.inputEl.dom.value = this.getValue();
30179         this.validate();
30180         return;
30181     },
30182     
30183     getDay : function()
30184     {
30185         return this.dayField.getValue();
30186     },
30187     
30188     getMonth : function()
30189     {
30190         return this.monthField.getValue();
30191     },
30192     
30193     getYear : function()
30194     {
30195         return this.yearField.getValue();
30196     },
30197     
30198     getValue : function()
30199     {
30200         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30201         
30202         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30203         
30204         return date;
30205     },
30206     
30207     reset : function()
30208     {
30209         this.setDay('');
30210         this.setMonth('');
30211         this.setYear('');
30212         this.inputEl.dom.value = '';
30213         this.validate();
30214         return;
30215     },
30216     
30217     validate : function()
30218     {
30219         var d = this.dayField.validate();
30220         var m = this.monthField.validate();
30221         var y = this.yearField.validate();
30222         
30223         var valid = true;
30224         
30225         if(
30226                 (!this.dayAllowBlank && !d) ||
30227                 (!this.monthAllowBlank && !m) ||
30228                 (!this.yearAllowBlank && !y)
30229         ){
30230             valid = false;
30231         }
30232         
30233         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30234             return valid;
30235         }
30236         
30237         if(valid){
30238             this.markValid();
30239             return valid;
30240         }
30241         
30242         this.markInvalid();
30243         
30244         return valid;
30245     },
30246     
30247     markValid : function()
30248     {
30249         
30250         var label = this.el.select('label', true).first();
30251         var icon = this.el.select('i.fa-star', true).first();
30252
30253         if(label && icon){
30254             icon.remove();
30255         }
30256         
30257         this.fireEvent('valid', this);
30258     },
30259     
30260      /**
30261      * Mark this field as invalid
30262      * @param {String} msg The validation message
30263      */
30264     markInvalid : function(msg)
30265     {
30266         
30267         var label = this.el.select('label', true).first();
30268         var icon = this.el.select('i.fa-star', true).first();
30269
30270         if(label && !icon){
30271             this.el.select('.roo-date-split-field-label', true).createChild({
30272                 tag : 'i',
30273                 cls : 'text-danger fa fa-lg fa-star',
30274                 tooltip : 'This field is required',
30275                 style : 'margin-right:5px;'
30276             }, label, true);
30277         }
30278         
30279         this.fireEvent('invalid', this, msg);
30280     },
30281     
30282     clearInvalid : function()
30283     {
30284         var label = this.el.select('label', true).first();
30285         var icon = this.el.select('i.fa-star', true).first();
30286
30287         if(label && icon){
30288             icon.remove();
30289         }
30290         
30291         this.fireEvent('valid', this);
30292     },
30293     
30294     getName: function()
30295     {
30296         return this.name;
30297     }
30298     
30299 });
30300
30301  /**
30302  *
30303  * This is based on 
30304  * http://masonry.desandro.com
30305  *
30306  * The idea is to render all the bricks based on vertical width...
30307  *
30308  * The original code extends 'outlayer' - we might need to use that....
30309  * 
30310  */
30311
30312
30313 /**
30314  * @class Roo.bootstrap.LayoutMasonry
30315  * @extends Roo.bootstrap.Component
30316  * Bootstrap Layout Masonry class
30317  * 
30318  * @constructor
30319  * Create a new Element
30320  * @param {Object} config The config object
30321  */
30322
30323 Roo.bootstrap.LayoutMasonry = function(config){
30324     
30325     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30326     
30327     this.bricks = [];
30328     
30329     Roo.bootstrap.LayoutMasonry.register(this);
30330     
30331     this.addEvents({
30332         // raw events
30333         /**
30334          * @event layout
30335          * Fire after layout the items
30336          * @param {Roo.bootstrap.LayoutMasonry} this
30337          * @param {Roo.EventObject} e
30338          */
30339         "layout" : true
30340     });
30341     
30342 };
30343
30344 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30345     
30346     /**
30347      * @cfg {Boolean} isLayoutInstant = no animation?
30348      */   
30349     isLayoutInstant : false, // needed?
30350    
30351     /**
30352      * @cfg {Number} boxWidth  width of the columns
30353      */   
30354     boxWidth : 450,
30355     
30356       /**
30357      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30358      */   
30359     boxHeight : 0,
30360     
30361     /**
30362      * @cfg {Number} padWidth padding below box..
30363      */   
30364     padWidth : 10, 
30365     
30366     /**
30367      * @cfg {Number} gutter gutter width..
30368      */   
30369     gutter : 10,
30370     
30371      /**
30372      * @cfg {Number} maxCols maximum number of columns
30373      */   
30374     
30375     maxCols: 0,
30376     
30377     /**
30378      * @cfg {Boolean} isAutoInitial defalut true
30379      */   
30380     isAutoInitial : true, 
30381     
30382     containerWidth: 0,
30383     
30384     /**
30385      * @cfg {Boolean} isHorizontal defalut false
30386      */   
30387     isHorizontal : false, 
30388
30389     currentSize : null,
30390     
30391     tag: 'div',
30392     
30393     cls: '',
30394     
30395     bricks: null, //CompositeElement
30396     
30397     cols : 1,
30398     
30399     _isLayoutInited : false,
30400     
30401 //    isAlternative : false, // only use for vertical layout...
30402     
30403     /**
30404      * @cfg {Number} alternativePadWidth padding below box..
30405      */   
30406     alternativePadWidth : 50,
30407     
30408     selectedBrick : [],
30409     
30410     getAutoCreate : function(){
30411         
30412         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30413         
30414         var cfg = {
30415             tag: this.tag,
30416             cls: 'blog-masonary-wrapper ' + this.cls,
30417             cn : {
30418                 cls : 'mas-boxes masonary'
30419             }
30420         };
30421         
30422         return cfg;
30423     },
30424     
30425     getChildContainer: function( )
30426     {
30427         if (this.boxesEl) {
30428             return this.boxesEl;
30429         }
30430         
30431         this.boxesEl = this.el.select('.mas-boxes').first();
30432         
30433         return this.boxesEl;
30434     },
30435     
30436     
30437     initEvents : function()
30438     {
30439         var _this = this;
30440         
30441         if(this.isAutoInitial){
30442             Roo.log('hook children rendered');
30443             this.on('childrenrendered', function() {
30444                 Roo.log('children rendered');
30445                 _this.initial();
30446             } ,this);
30447         }
30448     },
30449     
30450     initial : function()
30451     {
30452         this.selectedBrick = [];
30453         
30454         this.currentSize = this.el.getBox(true);
30455         
30456         Roo.EventManager.onWindowResize(this.resize, this); 
30457
30458         if(!this.isAutoInitial){
30459             this.layout();
30460             return;
30461         }
30462         
30463         this.layout();
30464         
30465         return;
30466         //this.layout.defer(500,this);
30467         
30468     },
30469     
30470     resize : function()
30471     {
30472         var cs = this.el.getBox(true);
30473         
30474         if (
30475                 this.currentSize.width == cs.width && 
30476                 this.currentSize.x == cs.x && 
30477                 this.currentSize.height == cs.height && 
30478                 this.currentSize.y == cs.y 
30479         ) {
30480             Roo.log("no change in with or X or Y");
30481             return;
30482         }
30483         
30484         this.currentSize = cs;
30485         
30486         this.layout();
30487         
30488     },
30489     
30490     layout : function()
30491     {   
30492         this._resetLayout();
30493         
30494         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30495         
30496         this.layoutItems( isInstant );
30497       
30498         this._isLayoutInited = true;
30499         
30500         this.fireEvent('layout', this);
30501         
30502     },
30503     
30504     _resetLayout : function()
30505     {
30506         if(this.isHorizontal){
30507             this.horizontalMeasureColumns();
30508             return;
30509         }
30510         
30511         this.verticalMeasureColumns();
30512         
30513     },
30514     
30515     verticalMeasureColumns : function()
30516     {
30517         this.getContainerWidth();
30518         
30519 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30520 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30521 //            return;
30522 //        }
30523         
30524         var boxWidth = this.boxWidth + this.padWidth;
30525         
30526         if(this.containerWidth < this.boxWidth){
30527             boxWidth = this.containerWidth
30528         }
30529         
30530         var containerWidth = this.containerWidth;
30531         
30532         var cols = Math.floor(containerWidth / boxWidth);
30533         
30534         this.cols = Math.max( cols, 1 );
30535         
30536         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30537         
30538         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30539         
30540         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30541         
30542         this.colWidth = boxWidth + avail - this.padWidth;
30543         
30544         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30545         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30546     },
30547     
30548     horizontalMeasureColumns : function()
30549     {
30550         this.getContainerWidth();
30551         
30552         var boxWidth = this.boxWidth;
30553         
30554         if(this.containerWidth < boxWidth){
30555             boxWidth = this.containerWidth;
30556         }
30557         
30558         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30559         
30560         this.el.setHeight(boxWidth);
30561         
30562     },
30563     
30564     getContainerWidth : function()
30565     {
30566         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30567     },
30568     
30569     layoutItems : function( isInstant )
30570     {
30571         Roo.log(this.bricks);
30572         
30573         var items = Roo.apply([], this.bricks);
30574         
30575         if(this.isHorizontal){
30576             this._horizontalLayoutItems( items , isInstant );
30577             return;
30578         }
30579         
30580 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30581 //            this._verticalAlternativeLayoutItems( items , isInstant );
30582 //            return;
30583 //        }
30584         
30585         this._verticalLayoutItems( items , isInstant );
30586         
30587     },
30588     
30589     _verticalLayoutItems : function ( items , isInstant)
30590     {
30591         if ( !items || !items.length ) {
30592             return;
30593         }
30594         
30595         var standard = [
30596             ['xs', 'xs', 'xs', 'tall'],
30597             ['xs', 'xs', 'tall'],
30598             ['xs', 'xs', 'sm'],
30599             ['xs', 'xs', 'xs'],
30600             ['xs', 'tall'],
30601             ['xs', 'sm'],
30602             ['xs', 'xs'],
30603             ['xs'],
30604             
30605             ['sm', 'xs', 'xs'],
30606             ['sm', 'xs'],
30607             ['sm'],
30608             
30609             ['tall', 'xs', 'xs', 'xs'],
30610             ['tall', 'xs', 'xs'],
30611             ['tall', 'xs'],
30612             ['tall']
30613             
30614         ];
30615         
30616         var queue = [];
30617         
30618         var boxes = [];
30619         
30620         var box = [];
30621         
30622         Roo.each(items, function(item, k){
30623             
30624             switch (item.size) {
30625                 // these layouts take up a full box,
30626                 case 'md' :
30627                 case 'md-left' :
30628                 case 'md-right' :
30629                 case 'wide' :
30630                     
30631                     if(box.length){
30632                         boxes.push(box);
30633                         box = [];
30634                     }
30635                     
30636                     boxes.push([item]);
30637                     
30638                     break;
30639                     
30640                 case 'xs' :
30641                 case 'sm' :
30642                 case 'tall' :
30643                     
30644                     box.push(item);
30645                     
30646                     break;
30647                 default :
30648                     break;
30649                     
30650             }
30651             
30652         }, this);
30653         
30654         if(box.length){
30655             boxes.push(box);
30656             box = [];
30657         }
30658         
30659         var filterPattern = function(box, length)
30660         {
30661             if(!box.length){
30662                 return;
30663             }
30664             
30665             var match = false;
30666             
30667             var pattern = box.slice(0, length);
30668             
30669             var format = [];
30670             
30671             Roo.each(pattern, function(i){
30672                 format.push(i.size);
30673             }, this);
30674             
30675             Roo.each(standard, function(s){
30676                 
30677                 if(String(s) != String(format)){
30678                     return;
30679                 }
30680                 
30681                 match = true;
30682                 return false;
30683                 
30684             }, this);
30685             
30686             if(!match && length == 1){
30687                 return;
30688             }
30689             
30690             if(!match){
30691                 filterPattern(box, length - 1);
30692                 return;
30693             }
30694                 
30695             queue.push(pattern);
30696
30697             box = box.slice(length, box.length);
30698
30699             filterPattern(box, 4);
30700
30701             return;
30702             
30703         }
30704         
30705         Roo.each(boxes, function(box, k){
30706             
30707             if(!box.length){
30708                 return;
30709             }
30710             
30711             if(box.length == 1){
30712                 queue.push(box);
30713                 return;
30714             }
30715             
30716             filterPattern(box, 4);
30717             
30718         }, this);
30719         
30720         this._processVerticalLayoutQueue( queue, isInstant );
30721         
30722     },
30723     
30724 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30725 //    {
30726 //        if ( !items || !items.length ) {
30727 //            return;
30728 //        }
30729 //
30730 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30731 //        
30732 //    },
30733     
30734     _horizontalLayoutItems : function ( items , isInstant)
30735     {
30736         if ( !items || !items.length || items.length < 3) {
30737             return;
30738         }
30739         
30740         items.reverse();
30741         
30742         var eItems = items.slice(0, 3);
30743         
30744         items = items.slice(3, items.length);
30745         
30746         var standard = [
30747             ['xs', 'xs', 'xs', 'wide'],
30748             ['xs', 'xs', 'wide'],
30749             ['xs', 'xs', 'sm'],
30750             ['xs', 'xs', 'xs'],
30751             ['xs', 'wide'],
30752             ['xs', 'sm'],
30753             ['xs', 'xs'],
30754             ['xs'],
30755             
30756             ['sm', 'xs', 'xs'],
30757             ['sm', 'xs'],
30758             ['sm'],
30759             
30760             ['wide', 'xs', 'xs', 'xs'],
30761             ['wide', 'xs', 'xs'],
30762             ['wide', 'xs'],
30763             ['wide'],
30764             
30765             ['wide-thin']
30766         ];
30767         
30768         var queue = [];
30769         
30770         var boxes = [];
30771         
30772         var box = [];
30773         
30774         Roo.each(items, function(item, k){
30775             
30776             switch (item.size) {
30777                 case 'md' :
30778                 case 'md-left' :
30779                 case 'md-right' :
30780                 case 'tall' :
30781                     
30782                     if(box.length){
30783                         boxes.push(box);
30784                         box = [];
30785                     }
30786                     
30787                     boxes.push([item]);
30788                     
30789                     break;
30790                     
30791                 case 'xs' :
30792                 case 'sm' :
30793                 case 'wide' :
30794                 case 'wide-thin' :
30795                     
30796                     box.push(item);
30797                     
30798                     break;
30799                 default :
30800                     break;
30801                     
30802             }
30803             
30804         }, this);
30805         
30806         if(box.length){
30807             boxes.push(box);
30808             box = [];
30809         }
30810         
30811         var filterPattern = function(box, length)
30812         {
30813             if(!box.length){
30814                 return;
30815             }
30816             
30817             var match = false;
30818             
30819             var pattern = box.slice(0, length);
30820             
30821             var format = [];
30822             
30823             Roo.each(pattern, function(i){
30824                 format.push(i.size);
30825             }, this);
30826             
30827             Roo.each(standard, function(s){
30828                 
30829                 if(String(s) != String(format)){
30830                     return;
30831                 }
30832                 
30833                 match = true;
30834                 return false;
30835                 
30836             }, this);
30837             
30838             if(!match && length == 1){
30839                 return;
30840             }
30841             
30842             if(!match){
30843                 filterPattern(box, length - 1);
30844                 return;
30845             }
30846                 
30847             queue.push(pattern);
30848
30849             box = box.slice(length, box.length);
30850
30851             filterPattern(box, 4);
30852
30853             return;
30854             
30855         }
30856         
30857         Roo.each(boxes, function(box, k){
30858             
30859             if(!box.length){
30860                 return;
30861             }
30862             
30863             if(box.length == 1){
30864                 queue.push(box);
30865                 return;
30866             }
30867             
30868             filterPattern(box, 4);
30869             
30870         }, this);
30871         
30872         
30873         var prune = [];
30874         
30875         var pos = this.el.getBox(true);
30876         
30877         var minX = pos.x;
30878         
30879         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30880         
30881         var hit_end = false;
30882         
30883         Roo.each(queue, function(box){
30884             
30885             if(hit_end){
30886                 
30887                 Roo.each(box, function(b){
30888                 
30889                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30890                     b.el.hide();
30891
30892                 }, this);
30893
30894                 return;
30895             }
30896             
30897             var mx = 0;
30898             
30899             Roo.each(box, function(b){
30900                 
30901                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30902                 b.el.show();
30903
30904                 mx = Math.max(mx, b.x);
30905                 
30906             }, this);
30907             
30908             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30909             
30910             if(maxX < minX){
30911                 
30912                 Roo.each(box, function(b){
30913                 
30914                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30915                     b.el.hide();
30916                     
30917                 }, this);
30918                 
30919                 hit_end = true;
30920                 
30921                 return;
30922             }
30923             
30924             prune.push(box);
30925             
30926         }, this);
30927         
30928         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30929     },
30930     
30931     /** Sets position of item in DOM
30932     * @param {Element} item
30933     * @param {Number} x - horizontal position
30934     * @param {Number} y - vertical position
30935     * @param {Boolean} isInstant - disables transitions
30936     */
30937     _processVerticalLayoutQueue : function( queue, isInstant )
30938     {
30939         var pos = this.el.getBox(true);
30940         var x = pos.x;
30941         var y = pos.y;
30942         var maxY = [];
30943         
30944         for (var i = 0; i < this.cols; i++){
30945             maxY[i] = pos.y;
30946         }
30947         
30948         Roo.each(queue, function(box, k){
30949             
30950             var col = k % this.cols;
30951             
30952             Roo.each(box, function(b,kk){
30953                 
30954                 b.el.position('absolute');
30955                 
30956                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30957                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30958                 
30959                 if(b.size == 'md-left' || b.size == 'md-right'){
30960                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30961                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30962                 }
30963                 
30964                 b.el.setWidth(width);
30965                 b.el.setHeight(height);
30966                 // iframe?
30967                 b.el.select('iframe',true).setSize(width,height);
30968                 
30969             }, this);
30970             
30971             for (var i = 0; i < this.cols; i++){
30972                 
30973                 if(maxY[i] < maxY[col]){
30974                     col = i;
30975                     continue;
30976                 }
30977                 
30978                 col = Math.min(col, i);
30979                 
30980             }
30981             
30982             x = pos.x + col * (this.colWidth + this.padWidth);
30983             
30984             y = maxY[col];
30985             
30986             var positions = [];
30987             
30988             switch (box.length){
30989                 case 1 :
30990                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30991                     break;
30992                 case 2 :
30993                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30994                     break;
30995                 case 3 :
30996                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30997                     break;
30998                 case 4 :
30999                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31000                     break;
31001                 default :
31002                     break;
31003             }
31004             
31005             Roo.each(box, function(b,kk){
31006                 
31007                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31008                 
31009                 var sz = b.el.getSize();
31010                 
31011                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31012                 
31013             }, this);
31014             
31015         }, this);
31016         
31017         var mY = 0;
31018         
31019         for (var i = 0; i < this.cols; i++){
31020             mY = Math.max(mY, maxY[i]);
31021         }
31022         
31023         this.el.setHeight(mY - pos.y);
31024         
31025     },
31026     
31027 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31028 //    {
31029 //        var pos = this.el.getBox(true);
31030 //        var x = pos.x;
31031 //        var y = pos.y;
31032 //        var maxX = pos.right;
31033 //        
31034 //        var maxHeight = 0;
31035 //        
31036 //        Roo.each(items, function(item, k){
31037 //            
31038 //            var c = k % 2;
31039 //            
31040 //            item.el.position('absolute');
31041 //                
31042 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31043 //
31044 //            item.el.setWidth(width);
31045 //
31046 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31047 //
31048 //            item.el.setHeight(height);
31049 //            
31050 //            if(c == 0){
31051 //                item.el.setXY([x, y], isInstant ? false : true);
31052 //            } else {
31053 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31054 //            }
31055 //            
31056 //            y = y + height + this.alternativePadWidth;
31057 //            
31058 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31059 //            
31060 //        }, this);
31061 //        
31062 //        this.el.setHeight(maxHeight);
31063 //        
31064 //    },
31065     
31066     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31067     {
31068         var pos = this.el.getBox(true);
31069         
31070         var minX = pos.x;
31071         var minY = pos.y;
31072         
31073         var maxX = pos.right;
31074         
31075         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31076         
31077         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31078         
31079         Roo.each(queue, function(box, k){
31080             
31081             Roo.each(box, function(b, kk){
31082                 
31083                 b.el.position('absolute');
31084                 
31085                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31086                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31087                 
31088                 if(b.size == 'md-left' || b.size == 'md-right'){
31089                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31090                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31091                 }
31092                 
31093                 b.el.setWidth(width);
31094                 b.el.setHeight(height);
31095                 
31096             }, this);
31097             
31098             if(!box.length){
31099                 return;
31100             }
31101             
31102             var positions = [];
31103             
31104             switch (box.length){
31105                 case 1 :
31106                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31107                     break;
31108                 case 2 :
31109                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31110                     break;
31111                 case 3 :
31112                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31113                     break;
31114                 case 4 :
31115                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31116                     break;
31117                 default :
31118                     break;
31119             }
31120             
31121             Roo.each(box, function(b,kk){
31122                 
31123                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31124                 
31125                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31126                 
31127             }, this);
31128             
31129         }, this);
31130         
31131     },
31132     
31133     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31134     {
31135         Roo.each(eItems, function(b,k){
31136             
31137             b.size = (k == 0) ? 'sm' : 'xs';
31138             b.x = (k == 0) ? 2 : 1;
31139             b.y = (k == 0) ? 2 : 1;
31140             
31141             b.el.position('absolute');
31142             
31143             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31144                 
31145             b.el.setWidth(width);
31146             
31147             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31148             
31149             b.el.setHeight(height);
31150             
31151         }, this);
31152
31153         var positions = [];
31154         
31155         positions.push({
31156             x : maxX - this.unitWidth * 2 - this.gutter,
31157             y : minY
31158         });
31159         
31160         positions.push({
31161             x : maxX - this.unitWidth,
31162             y : minY + (this.unitWidth + this.gutter) * 2
31163         });
31164         
31165         positions.push({
31166             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31167             y : minY
31168         });
31169         
31170         Roo.each(eItems, function(b,k){
31171             
31172             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31173
31174         }, this);
31175         
31176     },
31177     
31178     getVerticalOneBoxColPositions : function(x, y, box)
31179     {
31180         var pos = [];
31181         
31182         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31183         
31184         if(box[0].size == 'md-left'){
31185             rand = 0;
31186         }
31187         
31188         if(box[0].size == 'md-right'){
31189             rand = 1;
31190         }
31191         
31192         pos.push({
31193             x : x + (this.unitWidth + this.gutter) * rand,
31194             y : y
31195         });
31196         
31197         return pos;
31198     },
31199     
31200     getVerticalTwoBoxColPositions : function(x, y, box)
31201     {
31202         var pos = [];
31203         
31204         if(box[0].size == 'xs'){
31205             
31206             pos.push({
31207                 x : x,
31208                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31209             });
31210
31211             pos.push({
31212                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31213                 y : y
31214             });
31215             
31216             return pos;
31217             
31218         }
31219         
31220         pos.push({
31221             x : x,
31222             y : y
31223         });
31224
31225         pos.push({
31226             x : x + (this.unitWidth + this.gutter) * 2,
31227             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31228         });
31229         
31230         return pos;
31231         
31232     },
31233     
31234     getVerticalThreeBoxColPositions : function(x, y, box)
31235     {
31236         var pos = [];
31237         
31238         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31239             
31240             pos.push({
31241                 x : x,
31242                 y : y
31243             });
31244
31245             pos.push({
31246                 x : x + (this.unitWidth + this.gutter) * 1,
31247                 y : y
31248             });
31249             
31250             pos.push({
31251                 x : x + (this.unitWidth + this.gutter) * 2,
31252                 y : y
31253             });
31254             
31255             return pos;
31256             
31257         }
31258         
31259         if(box[0].size == 'xs' && box[1].size == 'xs'){
31260             
31261             pos.push({
31262                 x : x,
31263                 y : y
31264             });
31265
31266             pos.push({
31267                 x : x,
31268                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31269             });
31270             
31271             pos.push({
31272                 x : x + (this.unitWidth + this.gutter) * 1,
31273                 y : y
31274             });
31275             
31276             return pos;
31277             
31278         }
31279         
31280         pos.push({
31281             x : x,
31282             y : y
31283         });
31284
31285         pos.push({
31286             x : x + (this.unitWidth + this.gutter) * 2,
31287             y : y
31288         });
31289
31290         pos.push({
31291             x : x + (this.unitWidth + this.gutter) * 2,
31292             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31293         });
31294             
31295         return pos;
31296         
31297     },
31298     
31299     getVerticalFourBoxColPositions : function(x, y, box)
31300     {
31301         var pos = [];
31302         
31303         if(box[0].size == 'xs'){
31304             
31305             pos.push({
31306                 x : x,
31307                 y : y
31308             });
31309
31310             pos.push({
31311                 x : x,
31312                 y : y + (this.unitHeight + this.gutter) * 1
31313             });
31314             
31315             pos.push({
31316                 x : x,
31317                 y : y + (this.unitHeight + this.gutter) * 2
31318             });
31319             
31320             pos.push({
31321                 x : x + (this.unitWidth + this.gutter) * 1,
31322                 y : y
31323             });
31324             
31325             return pos;
31326             
31327         }
31328         
31329         pos.push({
31330             x : x,
31331             y : y
31332         });
31333
31334         pos.push({
31335             x : x + (this.unitWidth + this.gutter) * 2,
31336             y : y
31337         });
31338
31339         pos.push({
31340             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31341             y : y + (this.unitHeight + this.gutter) * 1
31342         });
31343
31344         pos.push({
31345             x : x + (this.unitWidth + this.gutter) * 2,
31346             y : y + (this.unitWidth + this.gutter) * 2
31347         });
31348
31349         return pos;
31350         
31351     },
31352     
31353     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31354     {
31355         var pos = [];
31356         
31357         if(box[0].size == 'md-left'){
31358             pos.push({
31359                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31360                 y : minY
31361             });
31362             
31363             return pos;
31364         }
31365         
31366         if(box[0].size == 'md-right'){
31367             pos.push({
31368                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31369                 y : minY + (this.unitWidth + this.gutter) * 1
31370             });
31371             
31372             return pos;
31373         }
31374         
31375         var rand = Math.floor(Math.random() * (4 - box[0].y));
31376         
31377         pos.push({
31378             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31379             y : minY + (this.unitWidth + this.gutter) * rand
31380         });
31381         
31382         return pos;
31383         
31384     },
31385     
31386     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31387     {
31388         var pos = [];
31389         
31390         if(box[0].size == 'xs'){
31391             
31392             pos.push({
31393                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31394                 y : minY
31395             });
31396
31397             pos.push({
31398                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31399                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31400             });
31401             
31402             return pos;
31403             
31404         }
31405         
31406         pos.push({
31407             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31408             y : minY
31409         });
31410
31411         pos.push({
31412             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31413             y : minY + (this.unitWidth + this.gutter) * 2
31414         });
31415         
31416         return pos;
31417         
31418     },
31419     
31420     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31421     {
31422         var pos = [];
31423         
31424         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31425             
31426             pos.push({
31427                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31428                 y : minY
31429             });
31430
31431             pos.push({
31432                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31433                 y : minY + (this.unitWidth + this.gutter) * 1
31434             });
31435             
31436             pos.push({
31437                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31438                 y : minY + (this.unitWidth + this.gutter) * 2
31439             });
31440             
31441             return pos;
31442             
31443         }
31444         
31445         if(box[0].size == 'xs' && box[1].size == 'xs'){
31446             
31447             pos.push({
31448                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31449                 y : minY
31450             });
31451
31452             pos.push({
31453                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31454                 y : minY
31455             });
31456             
31457             pos.push({
31458                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31459                 y : minY + (this.unitWidth + this.gutter) * 1
31460             });
31461             
31462             return pos;
31463             
31464         }
31465         
31466         pos.push({
31467             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31468             y : minY
31469         });
31470
31471         pos.push({
31472             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31473             y : minY + (this.unitWidth + this.gutter) * 2
31474         });
31475
31476         pos.push({
31477             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31478             y : minY + (this.unitWidth + this.gutter) * 2
31479         });
31480             
31481         return pos;
31482         
31483     },
31484     
31485     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31486     {
31487         var pos = [];
31488         
31489         if(box[0].size == 'xs'){
31490             
31491             pos.push({
31492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31493                 y : minY
31494             });
31495
31496             pos.push({
31497                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31498                 y : minY
31499             });
31500             
31501             pos.push({
31502                 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),
31503                 y : minY
31504             });
31505             
31506             pos.push({
31507                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31508                 y : minY + (this.unitWidth + this.gutter) * 1
31509             });
31510             
31511             return pos;
31512             
31513         }
31514         
31515         pos.push({
31516             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31517             y : minY
31518         });
31519         
31520         pos.push({
31521             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31522             y : minY + (this.unitWidth + this.gutter) * 2
31523         });
31524         
31525         pos.push({
31526             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31527             y : minY + (this.unitWidth + this.gutter) * 2
31528         });
31529         
31530         pos.push({
31531             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),
31532             y : minY + (this.unitWidth + this.gutter) * 2
31533         });
31534
31535         return pos;
31536         
31537     },
31538     
31539     /**
31540     * remove a Masonry Brick
31541     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31542     */
31543     removeBrick : function(brick_id)
31544     {
31545         if (!brick_id) {
31546             return;
31547         }
31548         
31549         for (var i = 0; i<this.bricks.length; i++) {
31550             if (this.bricks[i].id == brick_id) {
31551                 this.bricks.splice(i,1);
31552                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31553                 this.initial();
31554             }
31555         }
31556     },
31557     
31558     /**
31559     * adds a Masonry Brick
31560     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31561     */
31562     addBrick : function(cfg)
31563     {
31564         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31565         //this.register(cn);
31566         cn.parentId = this.id;
31567         cn.onRender(this.el, null);
31568         return cn;
31569     },
31570     
31571     /**
31572     * register a Masonry Brick
31573     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31574     */
31575     
31576     register : function(brick)
31577     {
31578         this.bricks.push(brick);
31579         brick.masonryId = this.id;
31580     },
31581     
31582     /**
31583     * clear all the Masonry Brick
31584     */
31585     clearAll : function()
31586     {
31587         this.bricks = [];
31588         //this.getChildContainer().dom.innerHTML = "";
31589         this.el.dom.innerHTML = '';
31590     },
31591     
31592     getSelected : function()
31593     {
31594         if (!this.selectedBrick) {
31595             return false;
31596         }
31597         
31598         return this.selectedBrick;
31599     }
31600 });
31601
31602 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31603     
31604     groups: {},
31605      /**
31606     * register a Masonry Layout
31607     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31608     */
31609     
31610     register : function(layout)
31611     {
31612         this.groups[layout.id] = layout;
31613     },
31614     /**
31615     * fetch a  Masonry Layout based on the masonry layout ID
31616     * @param {string} the masonry layout to add
31617     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31618     */
31619     
31620     get: function(layout_id) {
31621         if (typeof(this.groups[layout_id]) == 'undefined') {
31622             return false;
31623         }
31624         return this.groups[layout_id] ;
31625     }
31626     
31627     
31628     
31629 });
31630
31631  
31632
31633  /**
31634  *
31635  * This is based on 
31636  * http://masonry.desandro.com
31637  *
31638  * The idea is to render all the bricks based on vertical width...
31639  *
31640  * The original code extends 'outlayer' - we might need to use that....
31641  * 
31642  */
31643
31644
31645 /**
31646  * @class Roo.bootstrap.LayoutMasonryAuto
31647  * @extends Roo.bootstrap.Component
31648  * Bootstrap Layout Masonry class
31649  * 
31650  * @constructor
31651  * Create a new Element
31652  * @param {Object} config The config object
31653  */
31654
31655 Roo.bootstrap.LayoutMasonryAuto = function(config){
31656     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31657 };
31658
31659 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31660     
31661       /**
31662      * @cfg {Boolean} isFitWidth  - resize the width..
31663      */   
31664     isFitWidth : false,  // options..
31665     /**
31666      * @cfg {Boolean} isOriginLeft = left align?
31667      */   
31668     isOriginLeft : true,
31669     /**
31670      * @cfg {Boolean} isOriginTop = top align?
31671      */   
31672     isOriginTop : false,
31673     /**
31674      * @cfg {Boolean} isLayoutInstant = no animation?
31675      */   
31676     isLayoutInstant : false, // needed?
31677     /**
31678      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31679      */   
31680     isResizingContainer : true,
31681     /**
31682      * @cfg {Number} columnWidth  width of the columns 
31683      */   
31684     
31685     columnWidth : 0,
31686     
31687     /**
31688      * @cfg {Number} maxCols maximum number of columns
31689      */   
31690     
31691     maxCols: 0,
31692     /**
31693      * @cfg {Number} padHeight padding below box..
31694      */   
31695     
31696     padHeight : 10, 
31697     
31698     /**
31699      * @cfg {Boolean} isAutoInitial defalut true
31700      */   
31701     
31702     isAutoInitial : true, 
31703     
31704     // private?
31705     gutter : 0,
31706     
31707     containerWidth: 0,
31708     initialColumnWidth : 0,
31709     currentSize : null,
31710     
31711     colYs : null, // array.
31712     maxY : 0,
31713     padWidth: 10,
31714     
31715     
31716     tag: 'div',
31717     cls: '',
31718     bricks: null, //CompositeElement
31719     cols : 0, // array?
31720     // element : null, // wrapped now this.el
31721     _isLayoutInited : null, 
31722     
31723     
31724     getAutoCreate : function(){
31725         
31726         var cfg = {
31727             tag: this.tag,
31728             cls: 'blog-masonary-wrapper ' + this.cls,
31729             cn : {
31730                 cls : 'mas-boxes masonary'
31731             }
31732         };
31733         
31734         return cfg;
31735     },
31736     
31737     getChildContainer: function( )
31738     {
31739         if (this.boxesEl) {
31740             return this.boxesEl;
31741         }
31742         
31743         this.boxesEl = this.el.select('.mas-boxes').first();
31744         
31745         return this.boxesEl;
31746     },
31747     
31748     
31749     initEvents : function()
31750     {
31751         var _this = this;
31752         
31753         if(this.isAutoInitial){
31754             Roo.log('hook children rendered');
31755             this.on('childrenrendered', function() {
31756                 Roo.log('children rendered');
31757                 _this.initial();
31758             } ,this);
31759         }
31760         
31761     },
31762     
31763     initial : function()
31764     {
31765         this.reloadItems();
31766
31767         this.currentSize = this.el.getBox(true);
31768
31769         /// was window resize... - let's see if this works..
31770         Roo.EventManager.onWindowResize(this.resize, this); 
31771
31772         if(!this.isAutoInitial){
31773             this.layout();
31774             return;
31775         }
31776         
31777         this.layout.defer(500,this);
31778     },
31779     
31780     reloadItems: function()
31781     {
31782         this.bricks = this.el.select('.masonry-brick', true);
31783         
31784         this.bricks.each(function(b) {
31785             //Roo.log(b.getSize());
31786             if (!b.attr('originalwidth')) {
31787                 b.attr('originalwidth',  b.getSize().width);
31788             }
31789             
31790         });
31791         
31792         Roo.log(this.bricks.elements.length);
31793     },
31794     
31795     resize : function()
31796     {
31797         Roo.log('resize');
31798         var cs = this.el.getBox(true);
31799         
31800         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31801             Roo.log("no change in with or X");
31802             return;
31803         }
31804         this.currentSize = cs;
31805         this.layout();
31806     },
31807     
31808     layout : function()
31809     {
31810          Roo.log('layout');
31811         this._resetLayout();
31812         //this._manageStamps();
31813       
31814         // don't animate first layout
31815         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31816         this.layoutItems( isInstant );
31817       
31818         // flag for initalized
31819         this._isLayoutInited = true;
31820     },
31821     
31822     layoutItems : function( isInstant )
31823     {
31824         //var items = this._getItemsForLayout( this.items );
31825         // original code supports filtering layout items.. we just ignore it..
31826         
31827         this._layoutItems( this.bricks , isInstant );
31828       
31829         this._postLayout();
31830     },
31831     _layoutItems : function ( items , isInstant)
31832     {
31833        //this.fireEvent( 'layout', this, items );
31834     
31835
31836         if ( !items || !items.elements.length ) {
31837           // no items, emit event with empty array
31838             return;
31839         }
31840
31841         var queue = [];
31842         items.each(function(item) {
31843             Roo.log("layout item");
31844             Roo.log(item);
31845             // get x/y object from method
31846             var position = this._getItemLayoutPosition( item );
31847             // enqueue
31848             position.item = item;
31849             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31850             queue.push( position );
31851         }, this);
31852       
31853         this._processLayoutQueue( queue );
31854     },
31855     /** Sets position of item in DOM
31856     * @param {Element} item
31857     * @param {Number} x - horizontal position
31858     * @param {Number} y - vertical position
31859     * @param {Boolean} isInstant - disables transitions
31860     */
31861     _processLayoutQueue : function( queue )
31862     {
31863         for ( var i=0, len = queue.length; i < len; i++ ) {
31864             var obj = queue[i];
31865             obj.item.position('absolute');
31866             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31867         }
31868     },
31869       
31870     
31871     /**
31872     * Any logic you want to do after each layout,
31873     * i.e. size the container
31874     */
31875     _postLayout : function()
31876     {
31877         this.resizeContainer();
31878     },
31879     
31880     resizeContainer : function()
31881     {
31882         if ( !this.isResizingContainer ) {
31883             return;
31884         }
31885         var size = this._getContainerSize();
31886         if ( size ) {
31887             this.el.setSize(size.width,size.height);
31888             this.boxesEl.setSize(size.width,size.height);
31889         }
31890     },
31891     
31892     
31893     
31894     _resetLayout : function()
31895     {
31896         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31897         this.colWidth = this.el.getWidth();
31898         //this.gutter = this.el.getWidth(); 
31899         
31900         this.measureColumns();
31901
31902         // reset column Y
31903         var i = this.cols;
31904         this.colYs = [];
31905         while (i--) {
31906             this.colYs.push( 0 );
31907         }
31908     
31909         this.maxY = 0;
31910     },
31911
31912     measureColumns : function()
31913     {
31914         this.getContainerWidth();
31915       // if columnWidth is 0, default to outerWidth of first item
31916         if ( !this.columnWidth ) {
31917             var firstItem = this.bricks.first();
31918             Roo.log(firstItem);
31919             this.columnWidth  = this.containerWidth;
31920             if (firstItem && firstItem.attr('originalwidth') ) {
31921                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31922             }
31923             // columnWidth fall back to item of first element
31924             Roo.log("set column width?");
31925                         this.initialColumnWidth = this.columnWidth  ;
31926
31927             // if first elem has no width, default to size of container
31928             
31929         }
31930         
31931         
31932         if (this.initialColumnWidth) {
31933             this.columnWidth = this.initialColumnWidth;
31934         }
31935         
31936         
31937             
31938         // column width is fixed at the top - however if container width get's smaller we should
31939         // reduce it...
31940         
31941         // this bit calcs how man columns..
31942             
31943         var columnWidth = this.columnWidth += this.gutter;
31944       
31945         // calculate columns
31946         var containerWidth = this.containerWidth + this.gutter;
31947         
31948         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31949         // fix rounding errors, typically with gutters
31950         var excess = columnWidth - containerWidth % columnWidth;
31951         
31952         
31953         // if overshoot is less than a pixel, round up, otherwise floor it
31954         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31955         cols = Math[ mathMethod ]( cols );
31956         this.cols = Math.max( cols, 1 );
31957         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31958         
31959          // padding positioning..
31960         var totalColWidth = this.cols * this.columnWidth;
31961         var padavail = this.containerWidth - totalColWidth;
31962         // so for 2 columns - we need 3 'pads'
31963         
31964         var padNeeded = (1+this.cols) * this.padWidth;
31965         
31966         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31967         
31968         this.columnWidth += padExtra
31969         //this.padWidth = Math.floor(padavail /  ( this.cols));
31970         
31971         // adjust colum width so that padding is fixed??
31972         
31973         // we have 3 columns ... total = width * 3
31974         // we have X left over... that should be used by 
31975         
31976         //if (this.expandC) {
31977             
31978         //}
31979         
31980         
31981         
31982     },
31983     
31984     getContainerWidth : function()
31985     {
31986        /* // container is parent if fit width
31987         var container = this.isFitWidth ? this.element.parentNode : this.element;
31988         // check that this.size and size are there
31989         // IE8 triggers resize on body size change, so they might not be
31990         
31991         var size = getSize( container );  //FIXME
31992         this.containerWidth = size && size.innerWidth; //FIXME
31993         */
31994          
31995         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31996         
31997     },
31998     
31999     _getItemLayoutPosition : function( item )  // what is item?
32000     {
32001         // we resize the item to our columnWidth..
32002       
32003         item.setWidth(this.columnWidth);
32004         item.autoBoxAdjust  = false;
32005         
32006         var sz = item.getSize();
32007  
32008         // how many columns does this brick span
32009         var remainder = this.containerWidth % this.columnWidth;
32010         
32011         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32012         // round if off by 1 pixel, otherwise use ceil
32013         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32014         colSpan = Math.min( colSpan, this.cols );
32015         
32016         // normally this should be '1' as we dont' currently allow multi width columns..
32017         
32018         var colGroup = this._getColGroup( colSpan );
32019         // get the minimum Y value from the columns
32020         var minimumY = Math.min.apply( Math, colGroup );
32021         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32022         
32023         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32024          
32025         // position the brick
32026         var position = {
32027             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32028             y: this.currentSize.y + minimumY + this.padHeight
32029         };
32030         
32031         Roo.log(position);
32032         // apply setHeight to necessary columns
32033         var setHeight = minimumY + sz.height + this.padHeight;
32034         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32035         
32036         var setSpan = this.cols + 1 - colGroup.length;
32037         for ( var i = 0; i < setSpan; i++ ) {
32038           this.colYs[ shortColIndex + i ] = setHeight ;
32039         }
32040       
32041         return position;
32042     },
32043     
32044     /**
32045      * @param {Number} colSpan - number of columns the element spans
32046      * @returns {Array} colGroup
32047      */
32048     _getColGroup : function( colSpan )
32049     {
32050         if ( colSpan < 2 ) {
32051           // if brick spans only one column, use all the column Ys
32052           return this.colYs;
32053         }
32054       
32055         var colGroup = [];
32056         // how many different places could this brick fit horizontally
32057         var groupCount = this.cols + 1 - colSpan;
32058         // for each group potential horizontal position
32059         for ( var i = 0; i < groupCount; i++ ) {
32060           // make an array of colY values for that one group
32061           var groupColYs = this.colYs.slice( i, i + colSpan );
32062           // and get the max value of the array
32063           colGroup[i] = Math.max.apply( Math, groupColYs );
32064         }
32065         return colGroup;
32066     },
32067     /*
32068     _manageStamp : function( stamp )
32069     {
32070         var stampSize =  stamp.getSize();
32071         var offset = stamp.getBox();
32072         // get the columns that this stamp affects
32073         var firstX = this.isOriginLeft ? offset.x : offset.right;
32074         var lastX = firstX + stampSize.width;
32075         var firstCol = Math.floor( firstX / this.columnWidth );
32076         firstCol = Math.max( 0, firstCol );
32077         
32078         var lastCol = Math.floor( lastX / this.columnWidth );
32079         // lastCol should not go over if multiple of columnWidth #425
32080         lastCol -= lastX % this.columnWidth ? 0 : 1;
32081         lastCol = Math.min( this.cols - 1, lastCol );
32082         
32083         // set colYs to bottom of the stamp
32084         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32085             stampSize.height;
32086             
32087         for ( var i = firstCol; i <= lastCol; i++ ) {
32088           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32089         }
32090     },
32091     */
32092     
32093     _getContainerSize : function()
32094     {
32095         this.maxY = Math.max.apply( Math, this.colYs );
32096         var size = {
32097             height: this.maxY
32098         };
32099       
32100         if ( this.isFitWidth ) {
32101             size.width = this._getContainerFitWidth();
32102         }
32103       
32104         return size;
32105     },
32106     
32107     _getContainerFitWidth : function()
32108     {
32109         var unusedCols = 0;
32110         // count unused columns
32111         var i = this.cols;
32112         while ( --i ) {
32113           if ( this.colYs[i] !== 0 ) {
32114             break;
32115           }
32116           unusedCols++;
32117         }
32118         // fit container to columns that have been used
32119         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32120     },
32121     
32122     needsResizeLayout : function()
32123     {
32124         var previousWidth = this.containerWidth;
32125         this.getContainerWidth();
32126         return previousWidth !== this.containerWidth;
32127     }
32128  
32129 });
32130
32131  
32132
32133  /*
32134  * - LGPL
32135  *
32136  * element
32137  * 
32138  */
32139
32140 /**
32141  * @class Roo.bootstrap.MasonryBrick
32142  * @extends Roo.bootstrap.Component
32143  * Bootstrap MasonryBrick class
32144  * 
32145  * @constructor
32146  * Create a new MasonryBrick
32147  * @param {Object} config The config object
32148  */
32149
32150 Roo.bootstrap.MasonryBrick = function(config){
32151     
32152     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32153     
32154     Roo.bootstrap.MasonryBrick.register(this);
32155     
32156     this.addEvents({
32157         // raw events
32158         /**
32159          * @event click
32160          * When a MasonryBrick is clcik
32161          * @param {Roo.bootstrap.MasonryBrick} this
32162          * @param {Roo.EventObject} e
32163          */
32164         "click" : true
32165     });
32166 };
32167
32168 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32169     
32170     /**
32171      * @cfg {String} title
32172      */   
32173     title : '',
32174     /**
32175      * @cfg {String} html
32176      */   
32177     html : '',
32178     /**
32179      * @cfg {String} bgimage
32180      */   
32181     bgimage : '',
32182     /**
32183      * @cfg {String} videourl
32184      */   
32185     videourl : '',
32186     /**
32187      * @cfg {String} cls
32188      */   
32189     cls : '',
32190     /**
32191      * @cfg {String} href
32192      */   
32193     href : '',
32194     /**
32195      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32196      */   
32197     size : 'xs',
32198     
32199     /**
32200      * @cfg {String} placetitle (center|bottom)
32201      */   
32202     placetitle : '',
32203     
32204     /**
32205      * @cfg {Boolean} isFitContainer defalut true
32206      */   
32207     isFitContainer : true, 
32208     
32209     /**
32210      * @cfg {Boolean} preventDefault defalut false
32211      */   
32212     preventDefault : false, 
32213     
32214     /**
32215      * @cfg {Boolean} inverse defalut false
32216      */   
32217     maskInverse : false, 
32218     
32219     getAutoCreate : function()
32220     {
32221         if(!this.isFitContainer){
32222             return this.getSplitAutoCreate();
32223         }
32224         
32225         var cls = 'masonry-brick masonry-brick-full';
32226         
32227         if(this.href.length){
32228             cls += ' masonry-brick-link';
32229         }
32230         
32231         if(this.bgimage.length){
32232             cls += ' masonry-brick-image';
32233         }
32234         
32235         if(this.maskInverse){
32236             cls += ' mask-inverse';
32237         }
32238         
32239         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32240             cls += ' enable-mask';
32241         }
32242         
32243         if(this.size){
32244             cls += ' masonry-' + this.size + '-brick';
32245         }
32246         
32247         if(this.placetitle.length){
32248             
32249             switch (this.placetitle) {
32250                 case 'center' :
32251                     cls += ' masonry-center-title';
32252                     break;
32253                 case 'bottom' :
32254                     cls += ' masonry-bottom-title';
32255                     break;
32256                 default:
32257                     break;
32258             }
32259             
32260         } else {
32261             if(!this.html.length && !this.bgimage.length){
32262                 cls += ' masonry-center-title';
32263             }
32264
32265             if(!this.html.length && this.bgimage.length){
32266                 cls += ' masonry-bottom-title';
32267             }
32268         }
32269         
32270         if(this.cls){
32271             cls += ' ' + this.cls;
32272         }
32273         
32274         var cfg = {
32275             tag: (this.href.length) ? 'a' : 'div',
32276             cls: cls,
32277             cn: [
32278                 {
32279                     tag: 'div',
32280                     cls: 'masonry-brick-mask'
32281                 },
32282                 {
32283                     tag: 'div',
32284                     cls: 'masonry-brick-paragraph',
32285                     cn: []
32286                 }
32287             ]
32288         };
32289         
32290         if(this.href.length){
32291             cfg.href = this.href;
32292         }
32293         
32294         var cn = cfg.cn[1].cn;
32295         
32296         if(this.title.length){
32297             cn.push({
32298                 tag: 'h4',
32299                 cls: 'masonry-brick-title',
32300                 html: this.title
32301             });
32302         }
32303         
32304         if(this.html.length){
32305             cn.push({
32306                 tag: 'p',
32307                 cls: 'masonry-brick-text',
32308                 html: this.html
32309             });
32310         }
32311         
32312         if (!this.title.length && !this.html.length) {
32313             cfg.cn[1].cls += ' hide';
32314         }
32315         
32316         if(this.bgimage.length){
32317             cfg.cn.push({
32318                 tag: 'img',
32319                 cls: 'masonry-brick-image-view',
32320                 src: this.bgimage
32321             });
32322         }
32323         
32324         if(this.videourl.length){
32325             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32326             // youtube support only?
32327             cfg.cn.push({
32328                 tag: 'iframe',
32329                 cls: 'masonry-brick-image-view',
32330                 src: vurl,
32331                 frameborder : 0,
32332                 allowfullscreen : true
32333             });
32334         }
32335         
32336         return cfg;
32337         
32338     },
32339     
32340     getSplitAutoCreate : function()
32341     {
32342         var cls = 'masonry-brick masonry-brick-split';
32343         
32344         if(this.href.length){
32345             cls += ' masonry-brick-link';
32346         }
32347         
32348         if(this.bgimage.length){
32349             cls += ' masonry-brick-image';
32350         }
32351         
32352         if(this.size){
32353             cls += ' masonry-' + this.size + '-brick';
32354         }
32355         
32356         switch (this.placetitle) {
32357             case 'center' :
32358                 cls += ' masonry-center-title';
32359                 break;
32360             case 'bottom' :
32361                 cls += ' masonry-bottom-title';
32362                 break;
32363             default:
32364                 if(!this.bgimage.length){
32365                     cls += ' masonry-center-title';
32366                 }
32367
32368                 if(this.bgimage.length){
32369                     cls += ' masonry-bottom-title';
32370                 }
32371                 break;
32372         }
32373         
32374         if(this.cls){
32375             cls += ' ' + this.cls;
32376         }
32377         
32378         var cfg = {
32379             tag: (this.href.length) ? 'a' : 'div',
32380             cls: cls,
32381             cn: [
32382                 {
32383                     tag: 'div',
32384                     cls: 'masonry-brick-split-head',
32385                     cn: [
32386                         {
32387                             tag: 'div',
32388                             cls: 'masonry-brick-paragraph',
32389                             cn: []
32390                         }
32391                     ]
32392                 },
32393                 {
32394                     tag: 'div',
32395                     cls: 'masonry-brick-split-body',
32396                     cn: []
32397                 }
32398             ]
32399         };
32400         
32401         if(this.href.length){
32402             cfg.href = this.href;
32403         }
32404         
32405         if(this.title.length){
32406             cfg.cn[0].cn[0].cn.push({
32407                 tag: 'h4',
32408                 cls: 'masonry-brick-title',
32409                 html: this.title
32410             });
32411         }
32412         
32413         if(this.html.length){
32414             cfg.cn[1].cn.push({
32415                 tag: 'p',
32416                 cls: 'masonry-brick-text',
32417                 html: this.html
32418             });
32419         }
32420
32421         if(this.bgimage.length){
32422             cfg.cn[0].cn.push({
32423                 tag: 'img',
32424                 cls: 'masonry-brick-image-view',
32425                 src: this.bgimage
32426             });
32427         }
32428         
32429         if(this.videourl.length){
32430             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32431             // youtube support only?
32432             cfg.cn[0].cn.cn.push({
32433                 tag: 'iframe',
32434                 cls: 'masonry-brick-image-view',
32435                 src: vurl,
32436                 frameborder : 0,
32437                 allowfullscreen : true
32438             });
32439         }
32440         
32441         return cfg;
32442     },
32443     
32444     initEvents: function() 
32445     {
32446         switch (this.size) {
32447             case 'xs' :
32448                 this.x = 1;
32449                 this.y = 1;
32450                 break;
32451             case 'sm' :
32452                 this.x = 2;
32453                 this.y = 2;
32454                 break;
32455             case 'md' :
32456             case 'md-left' :
32457             case 'md-right' :
32458                 this.x = 3;
32459                 this.y = 3;
32460                 break;
32461             case 'tall' :
32462                 this.x = 2;
32463                 this.y = 3;
32464                 break;
32465             case 'wide' :
32466                 this.x = 3;
32467                 this.y = 2;
32468                 break;
32469             case 'wide-thin' :
32470                 this.x = 3;
32471                 this.y = 1;
32472                 break;
32473                         
32474             default :
32475                 break;
32476         }
32477         
32478         if(Roo.isTouch){
32479             this.el.on('touchstart', this.onTouchStart, this);
32480             this.el.on('touchmove', this.onTouchMove, this);
32481             this.el.on('touchend', this.onTouchEnd, this);
32482             this.el.on('contextmenu', this.onContextMenu, this);
32483         } else {
32484             this.el.on('mouseenter'  ,this.enter, this);
32485             this.el.on('mouseleave', this.leave, this);
32486             this.el.on('click', this.onClick, this);
32487         }
32488         
32489         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32490             this.parent().bricks.push(this);   
32491         }
32492         
32493     },
32494     
32495     onClick: function(e, el)
32496     {
32497         var time = this.endTimer - this.startTimer;
32498         // Roo.log(e.preventDefault());
32499         if(Roo.isTouch){
32500             if(time > 1000){
32501                 e.preventDefault();
32502                 return;
32503             }
32504         }
32505         
32506         if(!this.preventDefault){
32507             return;
32508         }
32509         
32510         e.preventDefault();
32511         
32512         if (this.activcClass != '') {
32513             this.selectBrick();
32514         }
32515         
32516         this.fireEvent('click', this);
32517     },
32518     
32519     enter: function(e, el)
32520     {
32521         e.preventDefault();
32522         
32523         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32524             return;
32525         }
32526         
32527         if(this.bgimage.length && this.html.length){
32528             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32529         }
32530     },
32531     
32532     leave: function(e, el)
32533     {
32534         e.preventDefault();
32535         
32536         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32537             return;
32538         }
32539         
32540         if(this.bgimage.length && this.html.length){
32541             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32542         }
32543     },
32544     
32545     onTouchStart: function(e, el)
32546     {
32547 //        e.preventDefault();
32548         
32549         this.touchmoved = false;
32550         
32551         if(!this.isFitContainer){
32552             return;
32553         }
32554         
32555         if(!this.bgimage.length || !this.html.length){
32556             return;
32557         }
32558         
32559         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32560         
32561         this.timer = new Date().getTime();
32562         
32563     },
32564     
32565     onTouchMove: function(e, el)
32566     {
32567         this.touchmoved = true;
32568     },
32569     
32570     onContextMenu : function(e,el)
32571     {
32572         e.preventDefault();
32573         e.stopPropagation();
32574         return false;
32575     },
32576     
32577     onTouchEnd: function(e, el)
32578     {
32579 //        e.preventDefault();
32580         
32581         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32582         
32583             this.leave(e,el);
32584             
32585             return;
32586         }
32587         
32588         if(!this.bgimage.length || !this.html.length){
32589             
32590             if(this.href.length){
32591                 window.location.href = this.href;
32592             }
32593             
32594             return;
32595         }
32596         
32597         if(!this.isFitContainer){
32598             return;
32599         }
32600         
32601         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32602         
32603         window.location.href = this.href;
32604     },
32605     
32606     //selection on single brick only
32607     selectBrick : function() {
32608         
32609         if (!this.parentId) {
32610             return;
32611         }
32612         
32613         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32614         var index = m.selectedBrick.indexOf(this.id);
32615         
32616         if ( index > -1) {
32617             m.selectedBrick.splice(index,1);
32618             this.el.removeClass(this.activeClass);
32619             return;
32620         }
32621         
32622         for(var i = 0; i < m.selectedBrick.length; i++) {
32623             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32624             b.el.removeClass(b.activeClass);
32625         }
32626         
32627         m.selectedBrick = [];
32628         
32629         m.selectedBrick.push(this.id);
32630         this.el.addClass(this.activeClass);
32631         return;
32632     }
32633     
32634 });
32635
32636 Roo.apply(Roo.bootstrap.MasonryBrick, {
32637     
32638     //groups: {},
32639     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32640      /**
32641     * register a Masonry Brick
32642     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32643     */
32644     
32645     register : function(brick)
32646     {
32647         //this.groups[brick.id] = brick;
32648         this.groups.add(brick.id, brick);
32649     },
32650     /**
32651     * fetch a  masonry brick based on the masonry brick ID
32652     * @param {string} the masonry brick to add
32653     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32654     */
32655     
32656     get: function(brick_id) 
32657     {
32658         // if (typeof(this.groups[brick_id]) == 'undefined') {
32659         //     return false;
32660         // }
32661         // return this.groups[brick_id] ;
32662         
32663         if(this.groups.key(brick_id)) {
32664             return this.groups.key(brick_id);
32665         }
32666         
32667         return false;
32668     }
32669     
32670     
32671     
32672 });
32673
32674  /*
32675  * - LGPL
32676  *
32677  * element
32678  * 
32679  */
32680
32681 /**
32682  * @class Roo.bootstrap.Brick
32683  * @extends Roo.bootstrap.Component
32684  * Bootstrap Brick class
32685  * 
32686  * @constructor
32687  * Create a new Brick
32688  * @param {Object} config The config object
32689  */
32690
32691 Roo.bootstrap.Brick = function(config){
32692     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32693     
32694     this.addEvents({
32695         // raw events
32696         /**
32697          * @event click
32698          * When a Brick is click
32699          * @param {Roo.bootstrap.Brick} this
32700          * @param {Roo.EventObject} e
32701          */
32702         "click" : true
32703     });
32704 };
32705
32706 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32707     
32708     /**
32709      * @cfg {String} title
32710      */   
32711     title : '',
32712     /**
32713      * @cfg {String} html
32714      */   
32715     html : '',
32716     /**
32717      * @cfg {String} bgimage
32718      */   
32719     bgimage : '',
32720     /**
32721      * @cfg {String} cls
32722      */   
32723     cls : '',
32724     /**
32725      * @cfg {String} href
32726      */   
32727     href : '',
32728     /**
32729      * @cfg {String} video
32730      */   
32731     video : '',
32732     /**
32733      * @cfg {Boolean} square
32734      */   
32735     square : true,
32736     
32737     getAutoCreate : function()
32738     {
32739         var cls = 'roo-brick';
32740         
32741         if(this.href.length){
32742             cls += ' roo-brick-link';
32743         }
32744         
32745         if(this.bgimage.length){
32746             cls += ' roo-brick-image';
32747         }
32748         
32749         if(!this.html.length && !this.bgimage.length){
32750             cls += ' roo-brick-center-title';
32751         }
32752         
32753         if(!this.html.length && this.bgimage.length){
32754             cls += ' roo-brick-bottom-title';
32755         }
32756         
32757         if(this.cls){
32758             cls += ' ' + this.cls;
32759         }
32760         
32761         var cfg = {
32762             tag: (this.href.length) ? 'a' : 'div',
32763             cls: cls,
32764             cn: [
32765                 {
32766                     tag: 'div',
32767                     cls: 'roo-brick-paragraph',
32768                     cn: []
32769                 }
32770             ]
32771         };
32772         
32773         if(this.href.length){
32774             cfg.href = this.href;
32775         }
32776         
32777         var cn = cfg.cn[0].cn;
32778         
32779         if(this.title.length){
32780             cn.push({
32781                 tag: 'h4',
32782                 cls: 'roo-brick-title',
32783                 html: this.title
32784             });
32785         }
32786         
32787         if(this.html.length){
32788             cn.push({
32789                 tag: 'p',
32790                 cls: 'roo-brick-text',
32791                 html: this.html
32792             });
32793         } else {
32794             cn.cls += ' hide';
32795         }
32796         
32797         if(this.bgimage.length){
32798             cfg.cn.push({
32799                 tag: 'img',
32800                 cls: 'roo-brick-image-view',
32801                 src: this.bgimage
32802             });
32803         }
32804         
32805         return cfg;
32806     },
32807     
32808     initEvents: function() 
32809     {
32810         if(this.title.length || this.html.length){
32811             this.el.on('mouseenter'  ,this.enter, this);
32812             this.el.on('mouseleave', this.leave, this);
32813         }
32814         
32815         Roo.EventManager.onWindowResize(this.resize, this); 
32816         
32817         if(this.bgimage.length){
32818             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32819             this.imageEl.on('load', this.onImageLoad, this);
32820             return;
32821         }
32822         
32823         this.resize();
32824     },
32825     
32826     onImageLoad : function()
32827     {
32828         this.resize();
32829     },
32830     
32831     resize : function()
32832     {
32833         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32834         
32835         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32836         
32837         if(this.bgimage.length){
32838             var image = this.el.select('.roo-brick-image-view', true).first();
32839             
32840             image.setWidth(paragraph.getWidth());
32841             
32842             if(this.square){
32843                 image.setHeight(paragraph.getWidth());
32844             }
32845             
32846             this.el.setHeight(image.getHeight());
32847             paragraph.setHeight(image.getHeight());
32848             
32849         }
32850         
32851     },
32852     
32853     enter: function(e, el)
32854     {
32855         e.preventDefault();
32856         
32857         if(this.bgimage.length){
32858             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32859             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32860         }
32861     },
32862     
32863     leave: function(e, el)
32864     {
32865         e.preventDefault();
32866         
32867         if(this.bgimage.length){
32868             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32869             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32870         }
32871     }
32872     
32873 });
32874
32875  
32876
32877  /*
32878  * - LGPL
32879  *
32880  * Input
32881  * 
32882  */
32883
32884 /**
32885  * @class Roo.bootstrap.NumberField
32886  * @extends Roo.bootstrap.Input
32887  * Bootstrap NumberField class
32888  * 
32889  * 
32890  * 
32891  * 
32892  * @constructor
32893  * Create a new NumberField
32894  * @param {Object} config The config object
32895  */
32896
32897 Roo.bootstrap.NumberField = function(config){
32898     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32899 };
32900
32901 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32902     
32903     /**
32904      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32905      */
32906     allowDecimals : true,
32907     /**
32908      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32909      */
32910     decimalSeparator : ".",
32911     /**
32912      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32913      */
32914     decimalPrecision : 2,
32915     /**
32916      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32917      */
32918     allowNegative : true,
32919     /**
32920      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32921      */
32922     minValue : Number.NEGATIVE_INFINITY,
32923     /**
32924      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32925      */
32926     maxValue : Number.MAX_VALUE,
32927     /**
32928      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32929      */
32930     minText : "The minimum value for this field is {0}",
32931     /**
32932      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32933      */
32934     maxText : "The maximum value for this field is {0}",
32935     /**
32936      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32937      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32938      */
32939     nanText : "{0} is not a valid number",
32940     /**
32941      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32942      */
32943     castInt : true,
32944
32945     // private
32946     initEvents : function()
32947     {   
32948         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32949         
32950         var allowed = "0123456789";
32951         
32952         if(this.allowDecimals){
32953             allowed += this.decimalSeparator;
32954         }
32955         
32956         if(this.allowNegative){
32957             allowed += "-";
32958         }
32959         
32960         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32961         
32962         var keyPress = function(e){
32963             
32964             var k = e.getKey();
32965             
32966             var c = e.getCharCode();
32967             
32968             if(
32969                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32970                     allowed.indexOf(String.fromCharCode(c)) === -1
32971             ){
32972                 e.stopEvent();
32973                 return;
32974             }
32975             
32976             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32977                 return;
32978             }
32979             
32980             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32981                 e.stopEvent();
32982             }
32983         };
32984         
32985         this.el.on("keypress", keyPress, this);
32986     },
32987     
32988     validateValue : function(value)
32989     {
32990         
32991         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32992             return false;
32993         }
32994         
32995         var num = this.parseValue(value);
32996         
32997         if(isNaN(num)){
32998             this.markInvalid(String.format(this.nanText, value));
32999             return false;
33000         }
33001         
33002         if(num < this.minValue){
33003             this.markInvalid(String.format(this.minText, this.minValue));
33004             return false;
33005         }
33006         
33007         if(num > this.maxValue){
33008             this.markInvalid(String.format(this.maxText, this.maxValue));
33009             return false;
33010         }
33011         
33012         return true;
33013     },
33014
33015     getValue : function()
33016     {
33017         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33018     },
33019
33020     parseValue : function(value)
33021     {
33022         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33023         return isNaN(value) ? '' : value;
33024     },
33025
33026     fixPrecision : function(value)
33027     {
33028         var nan = isNaN(value);
33029         
33030         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33031             return nan ? '' : value;
33032         }
33033         return parseFloat(value).toFixed(this.decimalPrecision);
33034     },
33035
33036     setValue : function(v)
33037     {
33038         v = this.fixPrecision(v);
33039         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33040     },
33041
33042     decimalPrecisionFcn : function(v)
33043     {
33044         return Math.floor(v);
33045     },
33046
33047     beforeBlur : function()
33048     {
33049         if(!this.castInt){
33050             return;
33051         }
33052         
33053         var v = this.parseValue(this.getRawValue());
33054         if(v){
33055             this.setValue(v);
33056         }
33057     }
33058     
33059 });
33060
33061  
33062
33063 /*
33064 * Licence: LGPL
33065 */
33066
33067 /**
33068  * @class Roo.bootstrap.DocumentSlider
33069  * @extends Roo.bootstrap.Component
33070  * Bootstrap DocumentSlider class
33071  * 
33072  * @constructor
33073  * Create a new DocumentViewer
33074  * @param {Object} config The config object
33075  */
33076
33077 Roo.bootstrap.DocumentSlider = function(config){
33078     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33079     
33080     this.files = [];
33081     
33082     this.addEvents({
33083         /**
33084          * @event initial
33085          * Fire after initEvent
33086          * @param {Roo.bootstrap.DocumentSlider} this
33087          */
33088         "initial" : true,
33089         /**
33090          * @event update
33091          * Fire after update
33092          * @param {Roo.bootstrap.DocumentSlider} this
33093          */
33094         "update" : true,
33095         /**
33096          * @event click
33097          * Fire after click
33098          * @param {Roo.bootstrap.DocumentSlider} this
33099          */
33100         "click" : true
33101     });
33102 };
33103
33104 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33105     
33106     files : false,
33107     
33108     indicator : 0,
33109     
33110     getAutoCreate : function()
33111     {
33112         var cfg = {
33113             tag : 'div',
33114             cls : 'roo-document-slider',
33115             cn : [
33116                 {
33117                     tag : 'div',
33118                     cls : 'roo-document-slider-header',
33119                     cn : [
33120                         {
33121                             tag : 'div',
33122                             cls : 'roo-document-slider-header-title'
33123                         }
33124                     ]
33125                 },
33126                 {
33127                     tag : 'div',
33128                     cls : 'roo-document-slider-body',
33129                     cn : [
33130                         {
33131                             tag : 'div',
33132                             cls : 'roo-document-slider-prev',
33133                             cn : [
33134                                 {
33135                                     tag : 'i',
33136                                     cls : 'fa fa-chevron-left'
33137                                 }
33138                             ]
33139                         },
33140                         {
33141                             tag : 'div',
33142                             cls : 'roo-document-slider-thumb',
33143                             cn : [
33144                                 {
33145                                     tag : 'img',
33146                                     cls : 'roo-document-slider-image'
33147                                 }
33148                             ]
33149                         },
33150                         {
33151                             tag : 'div',
33152                             cls : 'roo-document-slider-next',
33153                             cn : [
33154                                 {
33155                                     tag : 'i',
33156                                     cls : 'fa fa-chevron-right'
33157                                 }
33158                             ]
33159                         }
33160                     ]
33161                 }
33162             ]
33163         };
33164         
33165         return cfg;
33166     },
33167     
33168     initEvents : function()
33169     {
33170         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33171         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33172         
33173         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33174         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33175         
33176         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33178         
33179         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33181         
33182         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33184         
33185         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33186         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33187         
33188         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33189         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33190         
33191         this.thumbEl.on('click', this.onClick, this);
33192         
33193         this.prevIndicator.on('click', this.prev, this);
33194         
33195         this.nextIndicator.on('click', this.next, this);
33196         
33197     },
33198     
33199     initial : function()
33200     {
33201         if(this.files.length){
33202             this.indicator = 1;
33203             this.update()
33204         }
33205         
33206         this.fireEvent('initial', this);
33207     },
33208     
33209     update : function()
33210     {
33211         this.imageEl.attr('src', this.files[this.indicator - 1]);
33212         
33213         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33214         
33215         this.prevIndicator.show();
33216         
33217         if(this.indicator == 1){
33218             this.prevIndicator.hide();
33219         }
33220         
33221         this.nextIndicator.show();
33222         
33223         if(this.indicator == this.files.length){
33224             this.nextIndicator.hide();
33225         }
33226         
33227         this.thumbEl.scrollTo('top');
33228         
33229         this.fireEvent('update', this);
33230     },
33231     
33232     onClick : function(e)
33233     {
33234         e.preventDefault();
33235         
33236         this.fireEvent('click', this);
33237     },
33238     
33239     prev : function(e)
33240     {
33241         e.preventDefault();
33242         
33243         this.indicator = Math.max(1, this.indicator - 1);
33244         
33245         this.update();
33246     },
33247     
33248     next : function(e)
33249     {
33250         e.preventDefault();
33251         
33252         this.indicator = Math.min(this.files.length, this.indicator + 1);
33253         
33254         this.update();
33255     }
33256 });
33257 /*
33258  * - LGPL
33259  *
33260  * RadioSet
33261  *
33262  *
33263  */
33264
33265 /**
33266  * @class Roo.bootstrap.RadioSet
33267  * @extends Roo.bootstrap.Input
33268  * Bootstrap RadioSet class
33269  * @cfg {String} indicatorpos (left|right) default left
33270  * @cfg {Boolean} inline (true|false) inline the element (default true)
33271  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33272  * @constructor
33273  * Create a new RadioSet
33274  * @param {Object} config The config object
33275  */
33276
33277 Roo.bootstrap.RadioSet = function(config){
33278     
33279     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33280     
33281     this.radioes = [];
33282     
33283     Roo.bootstrap.RadioSet.register(this);
33284     
33285     this.addEvents({
33286         /**
33287         * @event check
33288         * Fires when the element is checked or unchecked.
33289         * @param {Roo.bootstrap.RadioSet} this This radio
33290         * @param {Roo.bootstrap.Radio} item The checked item
33291         */
33292        check : true
33293     });
33294     
33295 };
33296
33297 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33298
33299     radioes : false,
33300     
33301     inline : true,
33302     
33303     weight : '',
33304     
33305     indicatorpos : 'left',
33306     
33307     getAutoCreate : function()
33308     {
33309         var label = {
33310             tag : 'label',
33311             cls : 'roo-radio-set-label',
33312             cn : [
33313                 {
33314                     tag : 'span',
33315                     html : this.fieldLabel
33316                 }
33317             ]
33318         };
33319         
33320         if(this.indicatorpos == 'left'){
33321             label.cn.unshift({
33322                 tag : 'i',
33323                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33324                 tooltip : 'This field is required'
33325             });
33326         } else {
33327             label.cn.push({
33328                 tag : 'i',
33329                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33330                 tooltip : 'This field is required'
33331             });
33332         }
33333         
33334         var items = {
33335             tag : 'div',
33336             cls : 'roo-radio-set-items'
33337         };
33338         
33339         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33340         
33341         if (align === 'left' && this.fieldLabel.length) {
33342             
33343             items = {
33344                 cls : "roo-radio-set-right", 
33345                 cn: [
33346                     items
33347                 ]
33348             };
33349             
33350             if(this.labelWidth > 12){
33351                 label.style = "width: " + this.labelWidth + 'px';
33352             }
33353             
33354             if(this.labelWidth < 13 && this.labelmd == 0){
33355                 this.labelmd = this.labelWidth;
33356             }
33357             
33358             if(this.labellg > 0){
33359                 label.cls += ' col-lg-' + this.labellg;
33360                 items.cls += ' col-lg-' + (12 - this.labellg);
33361             }
33362             
33363             if(this.labelmd > 0){
33364                 label.cls += ' col-md-' + this.labelmd;
33365                 items.cls += ' col-md-' + (12 - this.labelmd);
33366             }
33367             
33368             if(this.labelsm > 0){
33369                 label.cls += ' col-sm-' + this.labelsm;
33370                 items.cls += ' col-sm-' + (12 - this.labelsm);
33371             }
33372             
33373             if(this.labelxs > 0){
33374                 label.cls += ' col-xs-' + this.labelxs;
33375                 items.cls += ' col-xs-' + (12 - this.labelxs);
33376             }
33377         }
33378         
33379         var cfg = {
33380             tag : 'div',
33381             cls : 'roo-radio-set',
33382             cn : [
33383                 {
33384                     tag : 'input',
33385                     cls : 'roo-radio-set-input',
33386                     type : 'hidden',
33387                     name : this.name,
33388                     value : this.value ? this.value :  ''
33389                 },
33390                 label,
33391                 items
33392             ]
33393         };
33394         
33395         if(this.weight.length){
33396             cfg.cls += ' roo-radio-' + this.weight;
33397         }
33398         
33399         if(this.inline) {
33400             cfg.cls += ' roo-radio-set-inline';
33401         }
33402         
33403         var settings=this;
33404         ['xs','sm','md','lg'].map(function(size){
33405             if (settings[size]) {
33406                 cfg.cls += ' col-' + size + '-' + settings[size];
33407             }
33408         });
33409         
33410         return cfg;
33411         
33412     },
33413
33414     initEvents : function()
33415     {
33416         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33417         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33418         
33419         if(!this.fieldLabel.length){
33420             this.labelEl.hide();
33421         }
33422         
33423         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33424         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33425         
33426         this.indicatorEl().addClass('invisible');
33427         
33428         this.originalValue = this.getValue();
33429         
33430     },
33431     
33432     inputEl: function ()
33433     {
33434         return this.el.select('.roo-radio-set-input', true).first();
33435     },
33436     
33437     getChildContainer : function()
33438     {
33439         return this.itemsEl;
33440     },
33441     
33442     register : function(item)
33443     {
33444         this.radioes.push(item);
33445         
33446     },
33447     
33448     validate : function()
33449     {   
33450         var valid = false;
33451         
33452         Roo.each(this.radioes, function(i){
33453             if(!i.checked){
33454                 return;
33455             }
33456             
33457             valid = true;
33458             return false;
33459         });
33460         
33461         if(this.allowBlank) {
33462             return true;
33463         }
33464         
33465         if(this.disabled || valid){
33466             this.markValid();
33467             return true;
33468         }
33469         
33470         this.markInvalid();
33471         return false;
33472         
33473     },
33474     
33475     markValid : function()
33476     {
33477         if(this.labelEl.isVisible(true)){
33478             this.indicatorEl().removeClass('visible');
33479             this.indicatorEl().addClass('invisible');
33480         }
33481         
33482         this.el.removeClass([this.invalidClass, this.validClass]);
33483         this.el.addClass(this.validClass);
33484         
33485         this.fireEvent('valid', this);
33486     },
33487     
33488     markInvalid : function(msg)
33489     {
33490         if(this.allowBlank || this.disabled){
33491             return;
33492         }
33493         
33494         if(this.labelEl.isVisible(true)){
33495             this.indicatorEl().removeClass('invisible');
33496             this.indicatorEl().addClass('visible');
33497         }
33498         
33499         this.el.removeClass([this.invalidClass, this.validClass]);
33500         this.el.addClass(this.invalidClass);
33501         
33502         this.fireEvent('invalid', this, msg);
33503         
33504     },
33505     
33506     setValue : function(v, suppressEvent)
33507     {   
33508         if(this.value === v){
33509             return;
33510         }
33511         
33512         this.value = v;
33513         
33514         if(this.rendered){
33515             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33516         }
33517         
33518         Roo.each(this.radioes, function(i){
33519             
33520             i.checked = false;
33521             i.el.removeClass('checked');
33522             
33523             if(i.value === v || i.value.toString() === v.toString()){
33524                 i.checked = true;
33525                 i.el.addClass('checked');
33526                 
33527                 if(suppressEvent !== true){
33528                     this.fireEvent('check', this, i);
33529                 }
33530             }
33531             
33532         }, this);
33533         
33534         this.validate();
33535     },
33536     
33537     clearInvalid : function(){
33538         
33539         if(!this.el || this.preventMark){
33540             return;
33541         }
33542         
33543         this.el.removeClass([this.invalidClass]);
33544         
33545         this.fireEvent('valid', this);
33546     }
33547     
33548 });
33549
33550 Roo.apply(Roo.bootstrap.RadioSet, {
33551     
33552     groups: {},
33553     
33554     register : function(set)
33555     {
33556         this.groups[set.name] = set;
33557     },
33558     
33559     get: function(name) 
33560     {
33561         if (typeof(this.groups[name]) == 'undefined') {
33562             return false;
33563         }
33564         
33565         return this.groups[name] ;
33566     }
33567     
33568 });
33569 /*
33570  * Based on:
33571  * Ext JS Library 1.1.1
33572  * Copyright(c) 2006-2007, Ext JS, LLC.
33573  *
33574  * Originally Released Under LGPL - original licence link has changed is not relivant.
33575  *
33576  * Fork - LGPL
33577  * <script type="text/javascript">
33578  */
33579
33580
33581 /**
33582  * @class Roo.bootstrap.SplitBar
33583  * @extends Roo.util.Observable
33584  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33585  * <br><br>
33586  * Usage:
33587  * <pre><code>
33588 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33589                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33590 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33591 split.minSize = 100;
33592 split.maxSize = 600;
33593 split.animate = true;
33594 split.on('moved', splitterMoved);
33595 </code></pre>
33596  * @constructor
33597  * Create a new SplitBar
33598  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33599  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33600  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33601  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33602                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33603                         position of the SplitBar).
33604  */
33605 Roo.bootstrap.SplitBar = function(cfg){
33606     
33607     /** @private */
33608     
33609     //{
33610     //  dragElement : elm
33611     //  resizingElement: el,
33612         // optional..
33613     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33614     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33615         // existingProxy ???
33616     //}
33617     
33618     this.el = Roo.get(cfg.dragElement, true);
33619     this.el.dom.unselectable = "on";
33620     /** @private */
33621     this.resizingEl = Roo.get(cfg.resizingElement, true);
33622
33623     /**
33624      * @private
33625      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33626      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33627      * @type Number
33628      */
33629     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33630     
33631     /**
33632      * The minimum size of the resizing element. (Defaults to 0)
33633      * @type Number
33634      */
33635     this.minSize = 0;
33636     
33637     /**
33638      * The maximum size of the resizing element. (Defaults to 2000)
33639      * @type Number
33640      */
33641     this.maxSize = 2000;
33642     
33643     /**
33644      * Whether to animate the transition to the new size
33645      * @type Boolean
33646      */
33647     this.animate = false;
33648     
33649     /**
33650      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33651      * @type Boolean
33652      */
33653     this.useShim = false;
33654     
33655     /** @private */
33656     this.shim = null;
33657     
33658     if(!cfg.existingProxy){
33659         /** @private */
33660         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33661     }else{
33662         this.proxy = Roo.get(cfg.existingProxy).dom;
33663     }
33664     /** @private */
33665     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33666     
33667     /** @private */
33668     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33669     
33670     /** @private */
33671     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33672     
33673     /** @private */
33674     this.dragSpecs = {};
33675     
33676     /**
33677      * @private The adapter to use to positon and resize elements
33678      */
33679     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33680     this.adapter.init(this);
33681     
33682     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33683         /** @private */
33684         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33685         this.el.addClass("roo-splitbar-h");
33686     }else{
33687         /** @private */
33688         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33689         this.el.addClass("roo-splitbar-v");
33690     }
33691     
33692     this.addEvents({
33693         /**
33694          * @event resize
33695          * Fires when the splitter is moved (alias for {@link #event-moved})
33696          * @param {Roo.bootstrap.SplitBar} this
33697          * @param {Number} newSize the new width or height
33698          */
33699         "resize" : true,
33700         /**
33701          * @event moved
33702          * Fires when the splitter is moved
33703          * @param {Roo.bootstrap.SplitBar} this
33704          * @param {Number} newSize the new width or height
33705          */
33706         "moved" : true,
33707         /**
33708          * @event beforeresize
33709          * Fires before the splitter is dragged
33710          * @param {Roo.bootstrap.SplitBar} this
33711          */
33712         "beforeresize" : true,
33713
33714         "beforeapply" : true
33715     });
33716
33717     Roo.util.Observable.call(this);
33718 };
33719
33720 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33721     onStartProxyDrag : function(x, y){
33722         this.fireEvent("beforeresize", this);
33723         if(!this.overlay){
33724             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33725             o.unselectable();
33726             o.enableDisplayMode("block");
33727             // all splitbars share the same overlay
33728             Roo.bootstrap.SplitBar.prototype.overlay = o;
33729         }
33730         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33731         this.overlay.show();
33732         Roo.get(this.proxy).setDisplayed("block");
33733         var size = this.adapter.getElementSize(this);
33734         this.activeMinSize = this.getMinimumSize();;
33735         this.activeMaxSize = this.getMaximumSize();;
33736         var c1 = size - this.activeMinSize;
33737         var c2 = Math.max(this.activeMaxSize - size, 0);
33738         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33739             this.dd.resetConstraints();
33740             this.dd.setXConstraint(
33741                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33742                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33743             );
33744             this.dd.setYConstraint(0, 0);
33745         }else{
33746             this.dd.resetConstraints();
33747             this.dd.setXConstraint(0, 0);
33748             this.dd.setYConstraint(
33749                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33750                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33751             );
33752          }
33753         this.dragSpecs.startSize = size;
33754         this.dragSpecs.startPoint = [x, y];
33755         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33756     },
33757     
33758     /** 
33759      * @private Called after the drag operation by the DDProxy
33760      */
33761     onEndProxyDrag : function(e){
33762         Roo.get(this.proxy).setDisplayed(false);
33763         var endPoint = Roo.lib.Event.getXY(e);
33764         if(this.overlay){
33765             this.overlay.hide();
33766         }
33767         var newSize;
33768         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33769             newSize = this.dragSpecs.startSize + 
33770                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33771                     endPoint[0] - this.dragSpecs.startPoint[0] :
33772                     this.dragSpecs.startPoint[0] - endPoint[0]
33773                 );
33774         }else{
33775             newSize = this.dragSpecs.startSize + 
33776                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33777                     endPoint[1] - this.dragSpecs.startPoint[1] :
33778                     this.dragSpecs.startPoint[1] - endPoint[1]
33779                 );
33780         }
33781         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33782         if(newSize != this.dragSpecs.startSize){
33783             if(this.fireEvent('beforeapply', this, newSize) !== false){
33784                 this.adapter.setElementSize(this, newSize);
33785                 this.fireEvent("moved", this, newSize);
33786                 this.fireEvent("resize", this, newSize);
33787             }
33788         }
33789     },
33790     
33791     /**
33792      * Get the adapter this SplitBar uses
33793      * @return The adapter object
33794      */
33795     getAdapter : function(){
33796         return this.adapter;
33797     },
33798     
33799     /**
33800      * Set the adapter this SplitBar uses
33801      * @param {Object} adapter A SplitBar adapter object
33802      */
33803     setAdapter : function(adapter){
33804         this.adapter = adapter;
33805         this.adapter.init(this);
33806     },
33807     
33808     /**
33809      * Gets the minimum size for the resizing element
33810      * @return {Number} The minimum size
33811      */
33812     getMinimumSize : function(){
33813         return this.minSize;
33814     },
33815     
33816     /**
33817      * Sets the minimum size for the resizing element
33818      * @param {Number} minSize The minimum size
33819      */
33820     setMinimumSize : function(minSize){
33821         this.minSize = minSize;
33822     },
33823     
33824     /**
33825      * Gets the maximum size for the resizing element
33826      * @return {Number} The maximum size
33827      */
33828     getMaximumSize : function(){
33829         return this.maxSize;
33830     },
33831     
33832     /**
33833      * Sets the maximum size for the resizing element
33834      * @param {Number} maxSize The maximum size
33835      */
33836     setMaximumSize : function(maxSize){
33837         this.maxSize = maxSize;
33838     },
33839     
33840     /**
33841      * Sets the initialize size for the resizing element
33842      * @param {Number} size The initial size
33843      */
33844     setCurrentSize : function(size){
33845         var oldAnimate = this.animate;
33846         this.animate = false;
33847         this.adapter.setElementSize(this, size);
33848         this.animate = oldAnimate;
33849     },
33850     
33851     /**
33852      * Destroy this splitbar. 
33853      * @param {Boolean} removeEl True to remove the element
33854      */
33855     destroy : function(removeEl){
33856         if(this.shim){
33857             this.shim.remove();
33858         }
33859         this.dd.unreg();
33860         this.proxy.parentNode.removeChild(this.proxy);
33861         if(removeEl){
33862             this.el.remove();
33863         }
33864     }
33865 });
33866
33867 /**
33868  * @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.
33869  */
33870 Roo.bootstrap.SplitBar.createProxy = function(dir){
33871     var proxy = new Roo.Element(document.createElement("div"));
33872     proxy.unselectable();
33873     var cls = 'roo-splitbar-proxy';
33874     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33875     document.body.appendChild(proxy.dom);
33876     return proxy.dom;
33877 };
33878
33879 /** 
33880  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33881  * Default Adapter. It assumes the splitter and resizing element are not positioned
33882  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33883  */
33884 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33885 };
33886
33887 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33888     // do nothing for now
33889     init : function(s){
33890     
33891     },
33892     /**
33893      * Called before drag operations to get the current size of the resizing element. 
33894      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33895      */
33896      getElementSize : function(s){
33897         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33898             return s.resizingEl.getWidth();
33899         }else{
33900             return s.resizingEl.getHeight();
33901         }
33902     },
33903     
33904     /**
33905      * Called after drag operations to set the size of the resizing element.
33906      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33907      * @param {Number} newSize The new size to set
33908      * @param {Function} onComplete A function to be invoked when resizing is complete
33909      */
33910     setElementSize : function(s, newSize, onComplete){
33911         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33912             if(!s.animate){
33913                 s.resizingEl.setWidth(newSize);
33914                 if(onComplete){
33915                     onComplete(s, newSize);
33916                 }
33917             }else{
33918                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33919             }
33920         }else{
33921             
33922             if(!s.animate){
33923                 s.resizingEl.setHeight(newSize);
33924                 if(onComplete){
33925                     onComplete(s, newSize);
33926                 }
33927             }else{
33928                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33929             }
33930         }
33931     }
33932 };
33933
33934 /** 
33935  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33936  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33937  * Adapter that  moves the splitter element to align with the resized sizing element. 
33938  * Used with an absolute positioned SplitBar.
33939  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33940  * document.body, make sure you assign an id to the body element.
33941  */
33942 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33943     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33944     this.container = Roo.get(container);
33945 };
33946
33947 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33948     init : function(s){
33949         this.basic.init(s);
33950     },
33951     
33952     getElementSize : function(s){
33953         return this.basic.getElementSize(s);
33954     },
33955     
33956     setElementSize : function(s, newSize, onComplete){
33957         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33958     },
33959     
33960     moveSplitter : function(s){
33961         var yes = Roo.bootstrap.SplitBar;
33962         switch(s.placement){
33963             case yes.LEFT:
33964                 s.el.setX(s.resizingEl.getRight());
33965                 break;
33966             case yes.RIGHT:
33967                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33968                 break;
33969             case yes.TOP:
33970                 s.el.setY(s.resizingEl.getBottom());
33971                 break;
33972             case yes.BOTTOM:
33973                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33974                 break;
33975         }
33976     }
33977 };
33978
33979 /**
33980  * Orientation constant - Create a vertical SplitBar
33981  * @static
33982  * @type Number
33983  */
33984 Roo.bootstrap.SplitBar.VERTICAL = 1;
33985
33986 /**
33987  * Orientation constant - Create a horizontal SplitBar
33988  * @static
33989  * @type Number
33990  */
33991 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33992
33993 /**
33994  * Placement constant - The resizing element is to the left of the splitter element
33995  * @static
33996  * @type Number
33997  */
33998 Roo.bootstrap.SplitBar.LEFT = 1;
33999
34000 /**
34001  * Placement constant - The resizing element is to the right of the splitter element
34002  * @static
34003  * @type Number
34004  */
34005 Roo.bootstrap.SplitBar.RIGHT = 2;
34006
34007 /**
34008  * Placement constant - The resizing element is positioned above the splitter element
34009  * @static
34010  * @type Number
34011  */
34012 Roo.bootstrap.SplitBar.TOP = 3;
34013
34014 /**
34015  * Placement constant - The resizing element is positioned under splitter element
34016  * @static
34017  * @type Number
34018  */
34019 Roo.bootstrap.SplitBar.BOTTOM = 4;
34020 Roo.namespace("Roo.bootstrap.layout");/*
34021  * Based on:
34022  * Ext JS Library 1.1.1
34023  * Copyright(c) 2006-2007, Ext JS, LLC.
34024  *
34025  * Originally Released Under LGPL - original licence link has changed is not relivant.
34026  *
34027  * Fork - LGPL
34028  * <script type="text/javascript">
34029  */
34030
34031 /**
34032  * @class Roo.bootstrap.layout.Manager
34033  * @extends Roo.bootstrap.Component
34034  * Base class for layout managers.
34035  */
34036 Roo.bootstrap.layout.Manager = function(config)
34037 {
34038     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34039
34040
34041
34042
34043
34044     /** false to disable window resize monitoring @type Boolean */
34045     this.monitorWindowResize = true;
34046     this.regions = {};
34047     this.addEvents({
34048         /**
34049          * @event layout
34050          * Fires when a layout is performed.
34051          * @param {Roo.LayoutManager} this
34052          */
34053         "layout" : true,
34054         /**
34055          * @event regionresized
34056          * Fires when the user resizes a region.
34057          * @param {Roo.LayoutRegion} region The resized region
34058          * @param {Number} newSize The new size (width for east/west, height for north/south)
34059          */
34060         "regionresized" : true,
34061         /**
34062          * @event regioncollapsed
34063          * Fires when a region is collapsed.
34064          * @param {Roo.LayoutRegion} region The collapsed region
34065          */
34066         "regioncollapsed" : true,
34067         /**
34068          * @event regionexpanded
34069          * Fires when a region is expanded.
34070          * @param {Roo.LayoutRegion} region The expanded region
34071          */
34072         "regionexpanded" : true
34073     });
34074     this.updating = false;
34075
34076     if (config.el) {
34077         this.el = Roo.get(config.el);
34078         this.initEvents();
34079     }
34080
34081 };
34082
34083 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34084
34085
34086     regions : null,
34087
34088     monitorWindowResize : true,
34089
34090
34091     updating : false,
34092
34093
34094     onRender : function(ct, position)
34095     {
34096         if(!this.el){
34097             this.el = Roo.get(ct);
34098             this.initEvents();
34099         }
34100         //this.fireEvent('render',this);
34101     },
34102
34103
34104     initEvents: function()
34105     {
34106
34107
34108         // ie scrollbar fix
34109         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34110             document.body.scroll = "no";
34111         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34112             this.el.position('relative');
34113         }
34114         this.id = this.el.id;
34115         this.el.addClass("roo-layout-container");
34116         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34117         if(this.el.dom != document.body ) {
34118             this.el.on('resize', this.layout,this);
34119             this.el.on('show', this.layout,this);
34120         }
34121
34122     },
34123
34124     /**
34125      * Returns true if this layout is currently being updated
34126      * @return {Boolean}
34127      */
34128     isUpdating : function(){
34129         return this.updating;
34130     },
34131
34132     /**
34133      * Suspend the LayoutManager from doing auto-layouts while
34134      * making multiple add or remove calls
34135      */
34136     beginUpdate : function(){
34137         this.updating = true;
34138     },
34139
34140     /**
34141      * Restore auto-layouts and optionally disable the manager from performing a layout
34142      * @param {Boolean} noLayout true to disable a layout update
34143      */
34144     endUpdate : function(noLayout){
34145         this.updating = false;
34146         if(!noLayout){
34147             this.layout();
34148         }
34149     },
34150
34151     layout: function(){
34152         // abstract...
34153     },
34154
34155     onRegionResized : function(region, newSize){
34156         this.fireEvent("regionresized", region, newSize);
34157         this.layout();
34158     },
34159
34160     onRegionCollapsed : function(region){
34161         this.fireEvent("regioncollapsed", region);
34162     },
34163
34164     onRegionExpanded : function(region){
34165         this.fireEvent("regionexpanded", region);
34166     },
34167
34168     /**
34169      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34170      * performs box-model adjustments.
34171      * @return {Object} The size as an object {width: (the width), height: (the height)}
34172      */
34173     getViewSize : function()
34174     {
34175         var size;
34176         if(this.el.dom != document.body){
34177             size = this.el.getSize();
34178         }else{
34179             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34180         }
34181         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34182         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34183         return size;
34184     },
34185
34186     /**
34187      * Returns the Element this layout is bound to.
34188      * @return {Roo.Element}
34189      */
34190     getEl : function(){
34191         return this.el;
34192     },
34193
34194     /**
34195      * Returns the specified region.
34196      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34197      * @return {Roo.LayoutRegion}
34198      */
34199     getRegion : function(target){
34200         return this.regions[target.toLowerCase()];
34201     },
34202
34203     onWindowResize : function(){
34204         if(this.monitorWindowResize){
34205             this.layout();
34206         }
34207     }
34208 });
34209 /*
34210  * Based on:
34211  * Ext JS Library 1.1.1
34212  * Copyright(c) 2006-2007, Ext JS, LLC.
34213  *
34214  * Originally Released Under LGPL - original licence link has changed is not relivant.
34215  *
34216  * Fork - LGPL
34217  * <script type="text/javascript">
34218  */
34219 /**
34220  * @class Roo.bootstrap.layout.Border
34221  * @extends Roo.bootstrap.layout.Manager
34222  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34223  * please see: examples/bootstrap/nested.html<br><br>
34224  
34225 <b>The container the layout is rendered into can be either the body element or any other element.
34226 If it is not the body element, the container needs to either be an absolute positioned element,
34227 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34228 the container size if it is not the body element.</b>
34229
34230 * @constructor
34231 * Create a new Border
34232 * @param {Object} config Configuration options
34233  */
34234 Roo.bootstrap.layout.Border = function(config){
34235     config = config || {};
34236     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34237     
34238     
34239     
34240     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34241         if(config[region]){
34242             config[region].region = region;
34243             this.addRegion(config[region]);
34244         }
34245     },this);
34246     
34247 };
34248
34249 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34250
34251 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34252     /**
34253      * Creates and adds a new region if it doesn't already exist.
34254      * @param {String} target The target region key (north, south, east, west or center).
34255      * @param {Object} config The regions config object
34256      * @return {BorderLayoutRegion} The new region
34257      */
34258     addRegion : function(config)
34259     {
34260         if(!this.regions[config.region]){
34261             var r = this.factory(config);
34262             this.bindRegion(r);
34263         }
34264         return this.regions[config.region];
34265     },
34266
34267     // private (kinda)
34268     bindRegion : function(r){
34269         this.regions[r.config.region] = r;
34270         
34271         r.on("visibilitychange",    this.layout, this);
34272         r.on("paneladded",          this.layout, this);
34273         r.on("panelremoved",        this.layout, this);
34274         r.on("invalidated",         this.layout, this);
34275         r.on("resized",             this.onRegionResized, this);
34276         r.on("collapsed",           this.onRegionCollapsed, this);
34277         r.on("expanded",            this.onRegionExpanded, this);
34278     },
34279
34280     /**
34281      * Performs a layout update.
34282      */
34283     layout : function()
34284     {
34285         if(this.updating) {
34286             return;
34287         }
34288         
34289         // render all the rebions if they have not been done alreayd?
34290         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34291             if(this.regions[region] && !this.regions[region].bodyEl){
34292                 this.regions[region].onRender(this.el)
34293             }
34294         },this);
34295         
34296         var size = this.getViewSize();
34297         var w = size.width;
34298         var h = size.height;
34299         var centerW = w;
34300         var centerH = h;
34301         var centerY = 0;
34302         var centerX = 0;
34303         //var x = 0, y = 0;
34304
34305         var rs = this.regions;
34306         var north = rs["north"];
34307         var south = rs["south"]; 
34308         var west = rs["west"];
34309         var east = rs["east"];
34310         var center = rs["center"];
34311         //if(this.hideOnLayout){ // not supported anymore
34312             //c.el.setStyle("display", "none");
34313         //}
34314         if(north && north.isVisible()){
34315             var b = north.getBox();
34316             var m = north.getMargins();
34317             b.width = w - (m.left+m.right);
34318             b.x = m.left;
34319             b.y = m.top;
34320             centerY = b.height + b.y + m.bottom;
34321             centerH -= centerY;
34322             north.updateBox(this.safeBox(b));
34323         }
34324         if(south && south.isVisible()){
34325             var b = south.getBox();
34326             var m = south.getMargins();
34327             b.width = w - (m.left+m.right);
34328             b.x = m.left;
34329             var totalHeight = (b.height + m.top + m.bottom);
34330             b.y = h - totalHeight + m.top;
34331             centerH -= totalHeight;
34332             south.updateBox(this.safeBox(b));
34333         }
34334         if(west && west.isVisible()){
34335             var b = west.getBox();
34336             var m = west.getMargins();
34337             b.height = centerH - (m.top+m.bottom);
34338             b.x = m.left;
34339             b.y = centerY + m.top;
34340             var totalWidth = (b.width + m.left + m.right);
34341             centerX += totalWidth;
34342             centerW -= totalWidth;
34343             west.updateBox(this.safeBox(b));
34344         }
34345         if(east && east.isVisible()){
34346             var b = east.getBox();
34347             var m = east.getMargins();
34348             b.height = centerH - (m.top+m.bottom);
34349             var totalWidth = (b.width + m.left + m.right);
34350             b.x = w - totalWidth + m.left;
34351             b.y = centerY + m.top;
34352             centerW -= totalWidth;
34353             east.updateBox(this.safeBox(b));
34354         }
34355         if(center){
34356             var m = center.getMargins();
34357             var centerBox = {
34358                 x: centerX + m.left,
34359                 y: centerY + m.top,
34360                 width: centerW - (m.left+m.right),
34361                 height: centerH - (m.top+m.bottom)
34362             };
34363             //if(this.hideOnLayout){
34364                 //center.el.setStyle("display", "block");
34365             //}
34366             center.updateBox(this.safeBox(centerBox));
34367         }
34368         this.el.repaint();
34369         this.fireEvent("layout", this);
34370     },
34371
34372     // private
34373     safeBox : function(box){
34374         box.width = Math.max(0, box.width);
34375         box.height = Math.max(0, box.height);
34376         return box;
34377     },
34378
34379     /**
34380      * Adds a ContentPanel (or subclass) to this layout.
34381      * @param {String} target The target region key (north, south, east, west or center).
34382      * @param {Roo.ContentPanel} panel The panel to add
34383      * @return {Roo.ContentPanel} The added panel
34384      */
34385     add : function(target, panel){
34386          
34387         target = target.toLowerCase();
34388         return this.regions[target].add(panel);
34389     },
34390
34391     /**
34392      * Remove a ContentPanel (or subclass) to this layout.
34393      * @param {String} target The target region key (north, south, east, west or center).
34394      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34395      * @return {Roo.ContentPanel} The removed panel
34396      */
34397     remove : function(target, panel){
34398         target = target.toLowerCase();
34399         return this.regions[target].remove(panel);
34400     },
34401
34402     /**
34403      * Searches all regions for a panel with the specified id
34404      * @param {String} panelId
34405      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34406      */
34407     findPanel : function(panelId){
34408         var rs = this.regions;
34409         for(var target in rs){
34410             if(typeof rs[target] != "function"){
34411                 var p = rs[target].getPanel(panelId);
34412                 if(p){
34413                     return p;
34414                 }
34415             }
34416         }
34417         return null;
34418     },
34419
34420     /**
34421      * Searches all regions for a panel with the specified id and activates (shows) it.
34422      * @param {String/ContentPanel} panelId The panels id or the panel itself
34423      * @return {Roo.ContentPanel} The shown panel or null
34424      */
34425     showPanel : function(panelId) {
34426       var rs = this.regions;
34427       for(var target in rs){
34428          var r = rs[target];
34429          if(typeof r != "function"){
34430             if(r.hasPanel(panelId)){
34431                return r.showPanel(panelId);
34432             }
34433          }
34434       }
34435       return null;
34436    },
34437
34438    /**
34439      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34440      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34441      */
34442    /*
34443     restoreState : function(provider){
34444         if(!provider){
34445             provider = Roo.state.Manager;
34446         }
34447         var sm = new Roo.LayoutStateManager();
34448         sm.init(this, provider);
34449     },
34450 */
34451  
34452  
34453     /**
34454      * Adds a xtype elements to the layout.
34455      * <pre><code>
34456
34457 layout.addxtype({
34458        xtype : 'ContentPanel',
34459        region: 'west',
34460        items: [ .... ]
34461    }
34462 );
34463
34464 layout.addxtype({
34465         xtype : 'NestedLayoutPanel',
34466         region: 'west',
34467         layout: {
34468            center: { },
34469            west: { }   
34470         },
34471         items : [ ... list of content panels or nested layout panels.. ]
34472    }
34473 );
34474 </code></pre>
34475      * @param {Object} cfg Xtype definition of item to add.
34476      */
34477     addxtype : function(cfg)
34478     {
34479         // basically accepts a pannel...
34480         // can accept a layout region..!?!?
34481         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34482         
34483         
34484         // theory?  children can only be panels??
34485         
34486         //if (!cfg.xtype.match(/Panel$/)) {
34487         //    return false;
34488         //}
34489         var ret = false;
34490         
34491         if (typeof(cfg.region) == 'undefined') {
34492             Roo.log("Failed to add Panel, region was not set");
34493             Roo.log(cfg);
34494             return false;
34495         }
34496         var region = cfg.region;
34497         delete cfg.region;
34498         
34499           
34500         var xitems = [];
34501         if (cfg.items) {
34502             xitems = cfg.items;
34503             delete cfg.items;
34504         }
34505         var nb = false;
34506         
34507         switch(cfg.xtype) 
34508         {
34509             case 'Content':  // ContentPanel (el, cfg)
34510             case 'Scroll':  // ContentPanel (el, cfg)
34511             case 'View': 
34512                 cfg.autoCreate = true;
34513                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34514                 //} else {
34515                 //    var el = this.el.createChild();
34516                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34517                 //}
34518                 
34519                 this.add(region, ret);
34520                 break;
34521             
34522             /*
34523             case 'TreePanel': // our new panel!
34524                 cfg.el = this.el.createChild();
34525                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34526                 this.add(region, ret);
34527                 break;
34528             */
34529             
34530             case 'Nest': 
34531                 // create a new Layout (which is  a Border Layout...
34532                 
34533                 var clayout = cfg.layout;
34534                 clayout.el  = this.el.createChild();
34535                 clayout.items   = clayout.items  || [];
34536                 
34537                 delete cfg.layout;
34538                 
34539                 // replace this exitems with the clayout ones..
34540                 xitems = clayout.items;
34541                  
34542                 // force background off if it's in center...
34543                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34544                     cfg.background = false;
34545                 }
34546                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34547                 
34548                 
34549                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34550                 //console.log('adding nested layout panel '  + cfg.toSource());
34551                 this.add(region, ret);
34552                 nb = {}; /// find first...
34553                 break;
34554             
34555             case 'Grid':
34556                 
34557                 // needs grid and region
34558                 
34559                 //var el = this.getRegion(region).el.createChild();
34560                 /*
34561                  *var el = this.el.createChild();
34562                 // create the grid first...
34563                 cfg.grid.container = el;
34564                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34565                 */
34566                 
34567                 if (region == 'center' && this.active ) {
34568                     cfg.background = false;
34569                 }
34570                 
34571                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34572                 
34573                 this.add(region, ret);
34574                 /*
34575                 if (cfg.background) {
34576                     // render grid on panel activation (if panel background)
34577                     ret.on('activate', function(gp) {
34578                         if (!gp.grid.rendered) {
34579                     //        gp.grid.render(el);
34580                         }
34581                     });
34582                 } else {
34583                   //  cfg.grid.render(el);
34584                 }
34585                 */
34586                 break;
34587            
34588            
34589             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34590                 // it was the old xcomponent building that caused this before.
34591                 // espeically if border is the top element in the tree.
34592                 ret = this;
34593                 break; 
34594                 
34595                     
34596                 
34597                 
34598                 
34599             default:
34600                 /*
34601                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34602                     
34603                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34604                     this.add(region, ret);
34605                 } else {
34606                 */
34607                     Roo.log(cfg);
34608                     throw "Can not add '" + cfg.xtype + "' to Border";
34609                     return null;
34610              
34611                                 
34612              
34613         }
34614         this.beginUpdate();
34615         // add children..
34616         var region = '';
34617         var abn = {};
34618         Roo.each(xitems, function(i)  {
34619             region = nb && i.region ? i.region : false;
34620             
34621             var add = ret.addxtype(i);
34622            
34623             if (region) {
34624                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34625                 if (!i.background) {
34626                     abn[region] = nb[region] ;
34627                 }
34628             }
34629             
34630         });
34631         this.endUpdate();
34632
34633         // make the last non-background panel active..
34634         //if (nb) { Roo.log(abn); }
34635         if (nb) {
34636             
34637             for(var r in abn) {
34638                 region = this.getRegion(r);
34639                 if (region) {
34640                     // tried using nb[r], but it does not work..
34641                      
34642                     region.showPanel(abn[r]);
34643                    
34644                 }
34645             }
34646         }
34647         return ret;
34648         
34649     },
34650     
34651     
34652 // private
34653     factory : function(cfg)
34654     {
34655         
34656         var validRegions = Roo.bootstrap.layout.Border.regions;
34657
34658         var target = cfg.region;
34659         cfg.mgr = this;
34660         
34661         var r = Roo.bootstrap.layout;
34662         Roo.log(target);
34663         switch(target){
34664             case "north":
34665                 return new r.North(cfg);
34666             case "south":
34667                 return new r.South(cfg);
34668             case "east":
34669                 return new r.East(cfg);
34670             case "west":
34671                 return new r.West(cfg);
34672             case "center":
34673                 return new r.Center(cfg);
34674         }
34675         throw 'Layout region "'+target+'" not supported.';
34676     }
34677     
34678     
34679 });
34680  /*
34681  * Based on:
34682  * Ext JS Library 1.1.1
34683  * Copyright(c) 2006-2007, Ext JS, LLC.
34684  *
34685  * Originally Released Under LGPL - original licence link has changed is not relivant.
34686  *
34687  * Fork - LGPL
34688  * <script type="text/javascript">
34689  */
34690  
34691 /**
34692  * @class Roo.bootstrap.layout.Basic
34693  * @extends Roo.util.Observable
34694  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34695  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34696  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34697  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34698  * @cfg {string}   region  the region that it inhabits..
34699  * @cfg {bool}   skipConfig skip config?
34700  * 
34701
34702  */
34703 Roo.bootstrap.layout.Basic = function(config){
34704     
34705     this.mgr = config.mgr;
34706     
34707     this.position = config.region;
34708     
34709     var skipConfig = config.skipConfig;
34710     
34711     this.events = {
34712         /**
34713          * @scope Roo.BasicLayoutRegion
34714          */
34715         
34716         /**
34717          * @event beforeremove
34718          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34719          * @param {Roo.LayoutRegion} this
34720          * @param {Roo.ContentPanel} panel The panel
34721          * @param {Object} e The cancel event object
34722          */
34723         "beforeremove" : true,
34724         /**
34725          * @event invalidated
34726          * Fires when the layout for this region is changed.
34727          * @param {Roo.LayoutRegion} this
34728          */
34729         "invalidated" : true,
34730         /**
34731          * @event visibilitychange
34732          * Fires when this region is shown or hidden 
34733          * @param {Roo.LayoutRegion} this
34734          * @param {Boolean} visibility true or false
34735          */
34736         "visibilitychange" : true,
34737         /**
34738          * @event paneladded
34739          * Fires when a panel is added. 
34740          * @param {Roo.LayoutRegion} this
34741          * @param {Roo.ContentPanel} panel The panel
34742          */
34743         "paneladded" : true,
34744         /**
34745          * @event panelremoved
34746          * Fires when a panel is removed. 
34747          * @param {Roo.LayoutRegion} this
34748          * @param {Roo.ContentPanel} panel The panel
34749          */
34750         "panelremoved" : true,
34751         /**
34752          * @event beforecollapse
34753          * Fires when this region before collapse.
34754          * @param {Roo.LayoutRegion} this
34755          */
34756         "beforecollapse" : true,
34757         /**
34758          * @event collapsed
34759          * Fires when this region is collapsed.
34760          * @param {Roo.LayoutRegion} this
34761          */
34762         "collapsed" : true,
34763         /**
34764          * @event expanded
34765          * Fires when this region is expanded.
34766          * @param {Roo.LayoutRegion} this
34767          */
34768         "expanded" : true,
34769         /**
34770          * @event slideshow
34771          * Fires when this region is slid into view.
34772          * @param {Roo.LayoutRegion} this
34773          */
34774         "slideshow" : true,
34775         /**
34776          * @event slidehide
34777          * Fires when this region slides out of view. 
34778          * @param {Roo.LayoutRegion} this
34779          */
34780         "slidehide" : true,
34781         /**
34782          * @event panelactivated
34783          * Fires when a panel is activated. 
34784          * @param {Roo.LayoutRegion} this
34785          * @param {Roo.ContentPanel} panel The activated panel
34786          */
34787         "panelactivated" : true,
34788         /**
34789          * @event resized
34790          * Fires when the user resizes this region. 
34791          * @param {Roo.LayoutRegion} this
34792          * @param {Number} newSize The new size (width for east/west, height for north/south)
34793          */
34794         "resized" : true
34795     };
34796     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34797     this.panels = new Roo.util.MixedCollection();
34798     this.panels.getKey = this.getPanelId.createDelegate(this);
34799     this.box = null;
34800     this.activePanel = null;
34801     // ensure listeners are added...
34802     
34803     if (config.listeners || config.events) {
34804         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34805             listeners : config.listeners || {},
34806             events : config.events || {}
34807         });
34808     }
34809     
34810     if(skipConfig !== true){
34811         this.applyConfig(config);
34812     }
34813 };
34814
34815 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34816 {
34817     getPanelId : function(p){
34818         return p.getId();
34819     },
34820     
34821     applyConfig : function(config){
34822         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34823         this.config = config;
34824         
34825     },
34826     
34827     /**
34828      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34829      * the width, for horizontal (north, south) the height.
34830      * @param {Number} newSize The new width or height
34831      */
34832     resizeTo : function(newSize){
34833         var el = this.el ? this.el :
34834                  (this.activePanel ? this.activePanel.getEl() : null);
34835         if(el){
34836             switch(this.position){
34837                 case "east":
34838                 case "west":
34839                     el.setWidth(newSize);
34840                     this.fireEvent("resized", this, newSize);
34841                 break;
34842                 case "north":
34843                 case "south":
34844                     el.setHeight(newSize);
34845                     this.fireEvent("resized", this, newSize);
34846                 break;                
34847             }
34848         }
34849     },
34850     
34851     getBox : function(){
34852         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34853     },
34854     
34855     getMargins : function(){
34856         return this.margins;
34857     },
34858     
34859     updateBox : function(box){
34860         this.box = box;
34861         var el = this.activePanel.getEl();
34862         el.dom.style.left = box.x + "px";
34863         el.dom.style.top = box.y + "px";
34864         this.activePanel.setSize(box.width, box.height);
34865     },
34866     
34867     /**
34868      * Returns the container element for this region.
34869      * @return {Roo.Element}
34870      */
34871     getEl : function(){
34872         return this.activePanel;
34873     },
34874     
34875     /**
34876      * Returns true if this region is currently visible.
34877      * @return {Boolean}
34878      */
34879     isVisible : function(){
34880         return this.activePanel ? true : false;
34881     },
34882     
34883     setActivePanel : function(panel){
34884         panel = this.getPanel(panel);
34885         if(this.activePanel && this.activePanel != panel){
34886             this.activePanel.setActiveState(false);
34887             this.activePanel.getEl().setLeftTop(-10000,-10000);
34888         }
34889         this.activePanel = panel;
34890         panel.setActiveState(true);
34891         if(this.box){
34892             panel.setSize(this.box.width, this.box.height);
34893         }
34894         this.fireEvent("panelactivated", this, panel);
34895         this.fireEvent("invalidated");
34896     },
34897     
34898     /**
34899      * Show the specified panel.
34900      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34901      * @return {Roo.ContentPanel} The shown panel or null
34902      */
34903     showPanel : function(panel){
34904         panel = this.getPanel(panel);
34905         if(panel){
34906             this.setActivePanel(panel);
34907         }
34908         return panel;
34909     },
34910     
34911     /**
34912      * Get the active panel for this region.
34913      * @return {Roo.ContentPanel} The active panel or null
34914      */
34915     getActivePanel : function(){
34916         return this.activePanel;
34917     },
34918     
34919     /**
34920      * Add the passed ContentPanel(s)
34921      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34922      * @return {Roo.ContentPanel} The panel added (if only one was added)
34923      */
34924     add : function(panel){
34925         if(arguments.length > 1){
34926             for(var i = 0, len = arguments.length; i < len; i++) {
34927                 this.add(arguments[i]);
34928             }
34929             return null;
34930         }
34931         if(this.hasPanel(panel)){
34932             this.showPanel(panel);
34933             return panel;
34934         }
34935         var el = panel.getEl();
34936         if(el.dom.parentNode != this.mgr.el.dom){
34937             this.mgr.el.dom.appendChild(el.dom);
34938         }
34939         if(panel.setRegion){
34940             panel.setRegion(this);
34941         }
34942         this.panels.add(panel);
34943         el.setStyle("position", "absolute");
34944         if(!panel.background){
34945             this.setActivePanel(panel);
34946             if(this.config.initialSize && this.panels.getCount()==1){
34947                 this.resizeTo(this.config.initialSize);
34948             }
34949         }
34950         this.fireEvent("paneladded", this, panel);
34951         return panel;
34952     },
34953     
34954     /**
34955      * Returns true if the panel is in this region.
34956      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34957      * @return {Boolean}
34958      */
34959     hasPanel : function(panel){
34960         if(typeof panel == "object"){ // must be panel obj
34961             panel = panel.getId();
34962         }
34963         return this.getPanel(panel) ? true : false;
34964     },
34965     
34966     /**
34967      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34968      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34969      * @param {Boolean} preservePanel Overrides the config preservePanel option
34970      * @return {Roo.ContentPanel} The panel that was removed
34971      */
34972     remove : function(panel, preservePanel){
34973         panel = this.getPanel(panel);
34974         if(!panel){
34975             return null;
34976         }
34977         var e = {};
34978         this.fireEvent("beforeremove", this, panel, e);
34979         if(e.cancel === true){
34980             return null;
34981         }
34982         var panelId = panel.getId();
34983         this.panels.removeKey(panelId);
34984         return panel;
34985     },
34986     
34987     /**
34988      * Returns the panel specified or null if it's not in this region.
34989      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34990      * @return {Roo.ContentPanel}
34991      */
34992     getPanel : function(id){
34993         if(typeof id == "object"){ // must be panel obj
34994             return id;
34995         }
34996         return this.panels.get(id);
34997     },
34998     
34999     /**
35000      * Returns this regions position (north/south/east/west/center).
35001      * @return {String} 
35002      */
35003     getPosition: function(){
35004         return this.position;    
35005     }
35006 });/*
35007  * Based on:
35008  * Ext JS Library 1.1.1
35009  * Copyright(c) 2006-2007, Ext JS, LLC.
35010  *
35011  * Originally Released Under LGPL - original licence link has changed is not relivant.
35012  *
35013  * Fork - LGPL
35014  * <script type="text/javascript">
35015  */
35016  
35017 /**
35018  * @class Roo.bootstrap.layout.Region
35019  * @extends Roo.bootstrap.layout.Basic
35020  * This class represents a region in a layout manager.
35021  
35022  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35023  * @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})
35024  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35025  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35026  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35027  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35028  * @cfg {String}    title           The title for the region (overrides panel titles)
35029  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35030  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35031  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35032  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35033  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35034  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35035  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35036  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35037  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35038  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35039
35040  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35041  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35042  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35043  * @cfg {Number}    width           For East/West panels
35044  * @cfg {Number}    height          For North/South panels
35045  * @cfg {Boolean}   split           To show the splitter
35046  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35047  * 
35048  * @cfg {string}   cls             Extra CSS classes to add to region
35049  * 
35050  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35051  * @cfg {string}   region  the region that it inhabits..
35052  *
35053
35054  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35055  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35056
35057  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35058  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35059  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35060  */
35061 Roo.bootstrap.layout.Region = function(config)
35062 {
35063     this.applyConfig(config);
35064
35065     var mgr = config.mgr;
35066     var pos = config.region;
35067     config.skipConfig = true;
35068     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35069     
35070     if (mgr.el) {
35071         this.onRender(mgr.el);   
35072     }
35073      
35074     this.visible = true;
35075     this.collapsed = false;
35076     this.unrendered_panels = [];
35077 };
35078
35079 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35080
35081     position: '', // set by wrapper (eg. north/south etc..)
35082     unrendered_panels : null,  // unrendered panels.
35083     createBody : function(){
35084         /** This region's body element 
35085         * @type Roo.Element */
35086         this.bodyEl = this.el.createChild({
35087                 tag: "div",
35088                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35089         });
35090     },
35091
35092     onRender: function(ctr, pos)
35093     {
35094         var dh = Roo.DomHelper;
35095         /** This region's container element 
35096         * @type Roo.Element */
35097         this.el = dh.append(ctr.dom, {
35098                 tag: "div",
35099                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35100             }, true);
35101         /** This region's title element 
35102         * @type Roo.Element */
35103     
35104         this.titleEl = dh.append(this.el.dom,
35105             {
35106                     tag: "div",
35107                     unselectable: "on",
35108                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35109                     children:[
35110                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35111                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35112                     ]}, true);
35113         
35114         this.titleEl.enableDisplayMode();
35115         /** This region's title text element 
35116         * @type HTMLElement */
35117         this.titleTextEl = this.titleEl.dom.firstChild;
35118         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35119         /*
35120         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35121         this.closeBtn.enableDisplayMode();
35122         this.closeBtn.on("click", this.closeClicked, this);
35123         this.closeBtn.hide();
35124     */
35125         this.createBody(this.config);
35126         if(this.config.hideWhenEmpty){
35127             this.hide();
35128             this.on("paneladded", this.validateVisibility, this);
35129             this.on("panelremoved", this.validateVisibility, this);
35130         }
35131         if(this.autoScroll){
35132             this.bodyEl.setStyle("overflow", "auto");
35133         }else{
35134             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35135         }
35136         //if(c.titlebar !== false){
35137             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35138                 this.titleEl.hide();
35139             }else{
35140                 this.titleEl.show();
35141                 if(this.config.title){
35142                     this.titleTextEl.innerHTML = this.config.title;
35143                 }
35144             }
35145         //}
35146         if(this.config.collapsed){
35147             this.collapse(true);
35148         }
35149         if(this.config.hidden){
35150             this.hide();
35151         }
35152         
35153         if (this.unrendered_panels && this.unrendered_panels.length) {
35154             for (var i =0;i< this.unrendered_panels.length; i++) {
35155                 this.add(this.unrendered_panels[i]);
35156             }
35157             this.unrendered_panels = null;
35158             
35159         }
35160         
35161     },
35162     
35163     applyConfig : function(c)
35164     {
35165         /*
35166          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35167             var dh = Roo.DomHelper;
35168             if(c.titlebar !== false){
35169                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35170                 this.collapseBtn.on("click", this.collapse, this);
35171                 this.collapseBtn.enableDisplayMode();
35172                 /*
35173                 if(c.showPin === true || this.showPin){
35174                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35175                     this.stickBtn.enableDisplayMode();
35176                     this.stickBtn.on("click", this.expand, this);
35177                     this.stickBtn.hide();
35178                 }
35179                 
35180             }
35181             */
35182             /** This region's collapsed element
35183             * @type Roo.Element */
35184             /*
35185              *
35186             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35187                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35188             ]}, true);
35189             
35190             if(c.floatable !== false){
35191                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35192                this.collapsedEl.on("click", this.collapseClick, this);
35193             }
35194
35195             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35196                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35197                    id: "message", unselectable: "on", style:{"float":"left"}});
35198                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35199              }
35200             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35201             this.expandBtn.on("click", this.expand, this);
35202             
35203         }
35204         
35205         if(this.collapseBtn){
35206             this.collapseBtn.setVisible(c.collapsible == true);
35207         }
35208         
35209         this.cmargins = c.cmargins || this.cmargins ||
35210                          (this.position == "west" || this.position == "east" ?
35211                              {top: 0, left: 2, right:2, bottom: 0} :
35212                              {top: 2, left: 0, right:0, bottom: 2});
35213         */
35214         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35215         
35216         
35217         this.bottomTabs = c.tabPosition != "top";
35218         
35219         this.autoScroll = c.autoScroll || false;
35220         
35221         
35222        
35223         
35224         this.duration = c.duration || .30;
35225         this.slideDuration = c.slideDuration || .45;
35226         this.config = c;
35227        
35228     },
35229     /**
35230      * Returns true if this region is currently visible.
35231      * @return {Boolean}
35232      */
35233     isVisible : function(){
35234         return this.visible;
35235     },
35236
35237     /**
35238      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35239      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35240      */
35241     //setCollapsedTitle : function(title){
35242     //    title = title || "&#160;";
35243      //   if(this.collapsedTitleTextEl){
35244       //      this.collapsedTitleTextEl.innerHTML = title;
35245        // }
35246     //},
35247
35248     getBox : function(){
35249         var b;
35250       //  if(!this.collapsed){
35251             b = this.el.getBox(false, true);
35252        // }else{
35253           //  b = this.collapsedEl.getBox(false, true);
35254         //}
35255         return b;
35256     },
35257
35258     getMargins : function(){
35259         return this.margins;
35260         //return this.collapsed ? this.cmargins : this.margins;
35261     },
35262 /*
35263     highlight : function(){
35264         this.el.addClass("x-layout-panel-dragover");
35265     },
35266
35267     unhighlight : function(){
35268         this.el.removeClass("x-layout-panel-dragover");
35269     },
35270 */
35271     updateBox : function(box)
35272     {
35273         if (!this.bodyEl) {
35274             return; // not rendered yet..
35275         }
35276         
35277         this.box = box;
35278         if(!this.collapsed){
35279             this.el.dom.style.left = box.x + "px";
35280             this.el.dom.style.top = box.y + "px";
35281             this.updateBody(box.width, box.height);
35282         }else{
35283             this.collapsedEl.dom.style.left = box.x + "px";
35284             this.collapsedEl.dom.style.top = box.y + "px";
35285             this.collapsedEl.setSize(box.width, box.height);
35286         }
35287         if(this.tabs){
35288             this.tabs.autoSizeTabs();
35289         }
35290     },
35291
35292     updateBody : function(w, h)
35293     {
35294         if(w !== null){
35295             this.el.setWidth(w);
35296             w -= this.el.getBorderWidth("rl");
35297             if(this.config.adjustments){
35298                 w += this.config.adjustments[0];
35299             }
35300         }
35301         if(h !== null && h > 0){
35302             this.el.setHeight(h);
35303             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35304             h -= this.el.getBorderWidth("tb");
35305             if(this.config.adjustments){
35306                 h += this.config.adjustments[1];
35307             }
35308             this.bodyEl.setHeight(h);
35309             if(this.tabs){
35310                 h = this.tabs.syncHeight(h);
35311             }
35312         }
35313         if(this.panelSize){
35314             w = w !== null ? w : this.panelSize.width;
35315             h = h !== null ? h : this.panelSize.height;
35316         }
35317         if(this.activePanel){
35318             var el = this.activePanel.getEl();
35319             w = w !== null ? w : el.getWidth();
35320             h = h !== null ? h : el.getHeight();
35321             this.panelSize = {width: w, height: h};
35322             this.activePanel.setSize(w, h);
35323         }
35324         if(Roo.isIE && this.tabs){
35325             this.tabs.el.repaint();
35326         }
35327     },
35328
35329     /**
35330      * Returns the container element for this region.
35331      * @return {Roo.Element}
35332      */
35333     getEl : function(){
35334         return this.el;
35335     },
35336
35337     /**
35338      * Hides this region.
35339      */
35340     hide : function(){
35341         //if(!this.collapsed){
35342             this.el.dom.style.left = "-2000px";
35343             this.el.hide();
35344         //}else{
35345          //   this.collapsedEl.dom.style.left = "-2000px";
35346          //   this.collapsedEl.hide();
35347        // }
35348         this.visible = false;
35349         this.fireEvent("visibilitychange", this, false);
35350     },
35351
35352     /**
35353      * Shows this region if it was previously hidden.
35354      */
35355     show : function(){
35356         //if(!this.collapsed){
35357             this.el.show();
35358         //}else{
35359         //    this.collapsedEl.show();
35360        // }
35361         this.visible = true;
35362         this.fireEvent("visibilitychange", this, true);
35363     },
35364 /*
35365     closeClicked : function(){
35366         if(this.activePanel){
35367             this.remove(this.activePanel);
35368         }
35369     },
35370
35371     collapseClick : function(e){
35372         if(this.isSlid){
35373            e.stopPropagation();
35374            this.slideIn();
35375         }else{
35376            e.stopPropagation();
35377            this.slideOut();
35378         }
35379     },
35380 */
35381     /**
35382      * Collapses this region.
35383      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35384      */
35385     /*
35386     collapse : function(skipAnim, skipCheck = false){
35387         if(this.collapsed) {
35388             return;
35389         }
35390         
35391         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35392             
35393             this.collapsed = true;
35394             if(this.split){
35395                 this.split.el.hide();
35396             }
35397             if(this.config.animate && skipAnim !== true){
35398                 this.fireEvent("invalidated", this);
35399                 this.animateCollapse();
35400             }else{
35401                 this.el.setLocation(-20000,-20000);
35402                 this.el.hide();
35403                 this.collapsedEl.show();
35404                 this.fireEvent("collapsed", this);
35405                 this.fireEvent("invalidated", this);
35406             }
35407         }
35408         
35409     },
35410 */
35411     animateCollapse : function(){
35412         // overridden
35413     },
35414
35415     /**
35416      * Expands this region if it was previously collapsed.
35417      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35418      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35419      */
35420     /*
35421     expand : function(e, skipAnim){
35422         if(e) {
35423             e.stopPropagation();
35424         }
35425         if(!this.collapsed || this.el.hasActiveFx()) {
35426             return;
35427         }
35428         if(this.isSlid){
35429             this.afterSlideIn();
35430             skipAnim = true;
35431         }
35432         this.collapsed = false;
35433         if(this.config.animate && skipAnim !== true){
35434             this.animateExpand();
35435         }else{
35436             this.el.show();
35437             if(this.split){
35438                 this.split.el.show();
35439             }
35440             this.collapsedEl.setLocation(-2000,-2000);
35441             this.collapsedEl.hide();
35442             this.fireEvent("invalidated", this);
35443             this.fireEvent("expanded", this);
35444         }
35445     },
35446 */
35447     animateExpand : function(){
35448         // overridden
35449     },
35450
35451     initTabs : function()
35452     {
35453         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35454         
35455         var ts = new Roo.bootstrap.panel.Tabs({
35456                 el: this.bodyEl.dom,
35457                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35458                 disableTooltips: this.config.disableTabTips,
35459                 toolbar : this.config.toolbar
35460             });
35461         
35462         if(this.config.hideTabs){
35463             ts.stripWrap.setDisplayed(false);
35464         }
35465         this.tabs = ts;
35466         ts.resizeTabs = this.config.resizeTabs === true;
35467         ts.minTabWidth = this.config.minTabWidth || 40;
35468         ts.maxTabWidth = this.config.maxTabWidth || 250;
35469         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35470         ts.monitorResize = false;
35471         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35472         ts.bodyEl.addClass('roo-layout-tabs-body');
35473         this.panels.each(this.initPanelAsTab, this);
35474     },
35475
35476     initPanelAsTab : function(panel){
35477         var ti = this.tabs.addTab(
35478             panel.getEl().id,
35479             panel.getTitle(),
35480             null,
35481             this.config.closeOnTab && panel.isClosable(),
35482             panel.tpl
35483         );
35484         if(panel.tabTip !== undefined){
35485             ti.setTooltip(panel.tabTip);
35486         }
35487         ti.on("activate", function(){
35488               this.setActivePanel(panel);
35489         }, this);
35490         
35491         if(this.config.closeOnTab){
35492             ti.on("beforeclose", function(t, e){
35493                 e.cancel = true;
35494                 this.remove(panel);
35495             }, this);
35496         }
35497         
35498         panel.tabItem = ti;
35499         
35500         return ti;
35501     },
35502
35503     updatePanelTitle : function(panel, title)
35504     {
35505         if(this.activePanel == panel){
35506             this.updateTitle(title);
35507         }
35508         if(this.tabs){
35509             var ti = this.tabs.getTab(panel.getEl().id);
35510             ti.setText(title);
35511             if(panel.tabTip !== undefined){
35512                 ti.setTooltip(panel.tabTip);
35513             }
35514         }
35515     },
35516
35517     updateTitle : function(title){
35518         if(this.titleTextEl && !this.config.title){
35519             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35520         }
35521     },
35522
35523     setActivePanel : function(panel)
35524     {
35525         panel = this.getPanel(panel);
35526         if(this.activePanel && this.activePanel != panel){
35527             if(this.activePanel.setActiveState(false) === false){
35528                 return;
35529             }
35530         }
35531         this.activePanel = panel;
35532         panel.setActiveState(true);
35533         if(this.panelSize){
35534             panel.setSize(this.panelSize.width, this.panelSize.height);
35535         }
35536         if(this.closeBtn){
35537             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35538         }
35539         this.updateTitle(panel.getTitle());
35540         if(this.tabs){
35541             this.fireEvent("invalidated", this);
35542         }
35543         this.fireEvent("panelactivated", this, panel);
35544     },
35545
35546     /**
35547      * Shows the specified panel.
35548      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35549      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35550      */
35551     showPanel : function(panel)
35552     {
35553         panel = this.getPanel(panel);
35554         if(panel){
35555             if(this.tabs){
35556                 var tab = this.tabs.getTab(panel.getEl().id);
35557                 if(tab.isHidden()){
35558                     this.tabs.unhideTab(tab.id);
35559                 }
35560                 tab.activate();
35561             }else{
35562                 this.setActivePanel(panel);
35563             }
35564         }
35565         return panel;
35566     },
35567
35568     /**
35569      * Get the active panel for this region.
35570      * @return {Roo.ContentPanel} The active panel or null
35571      */
35572     getActivePanel : function(){
35573         return this.activePanel;
35574     },
35575
35576     validateVisibility : function(){
35577         if(this.panels.getCount() < 1){
35578             this.updateTitle("&#160;");
35579             this.closeBtn.hide();
35580             this.hide();
35581         }else{
35582             if(!this.isVisible()){
35583                 this.show();
35584             }
35585         }
35586     },
35587
35588     /**
35589      * Adds the passed ContentPanel(s) to this region.
35590      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35591      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35592      */
35593     add : function(panel)
35594     {
35595         if(arguments.length > 1){
35596             for(var i = 0, len = arguments.length; i < len; i++) {
35597                 this.add(arguments[i]);
35598             }
35599             return null;
35600         }
35601         
35602         // if we have not been rendered yet, then we can not really do much of this..
35603         if (!this.bodyEl) {
35604             this.unrendered_panels.push(panel);
35605             return panel;
35606         }
35607         
35608         
35609         
35610         
35611         if(this.hasPanel(panel)){
35612             this.showPanel(panel);
35613             return panel;
35614         }
35615         panel.setRegion(this);
35616         this.panels.add(panel);
35617        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35618             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35619             // and hide them... ???
35620             this.bodyEl.dom.appendChild(panel.getEl().dom);
35621             if(panel.background !== true){
35622                 this.setActivePanel(panel);
35623             }
35624             this.fireEvent("paneladded", this, panel);
35625             return panel;
35626         }
35627         */
35628         if(!this.tabs){
35629             this.initTabs();
35630         }else{
35631             this.initPanelAsTab(panel);
35632         }
35633         
35634         
35635         if(panel.background !== true){
35636             this.tabs.activate(panel.getEl().id);
35637         }
35638         this.fireEvent("paneladded", this, panel);
35639         return panel;
35640     },
35641
35642     /**
35643      * Hides the tab for the specified panel.
35644      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35645      */
35646     hidePanel : function(panel){
35647         if(this.tabs && (panel = this.getPanel(panel))){
35648             this.tabs.hideTab(panel.getEl().id);
35649         }
35650     },
35651
35652     /**
35653      * Unhides the tab for a previously hidden panel.
35654      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35655      */
35656     unhidePanel : function(panel){
35657         if(this.tabs && (panel = this.getPanel(panel))){
35658             this.tabs.unhideTab(panel.getEl().id);
35659         }
35660     },
35661
35662     clearPanels : function(){
35663         while(this.panels.getCount() > 0){
35664              this.remove(this.panels.first());
35665         }
35666     },
35667
35668     /**
35669      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35670      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35671      * @param {Boolean} preservePanel Overrides the config preservePanel option
35672      * @return {Roo.ContentPanel} The panel that was removed
35673      */
35674     remove : function(panel, preservePanel)
35675     {
35676         panel = this.getPanel(panel);
35677         if(!panel){
35678             return null;
35679         }
35680         var e = {};
35681         this.fireEvent("beforeremove", this, panel, e);
35682         if(e.cancel === true){
35683             return null;
35684         }
35685         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35686         var panelId = panel.getId();
35687         this.panels.removeKey(panelId);
35688         if(preservePanel){
35689             document.body.appendChild(panel.getEl().dom);
35690         }
35691         if(this.tabs){
35692             this.tabs.removeTab(panel.getEl().id);
35693         }else if (!preservePanel){
35694             this.bodyEl.dom.removeChild(panel.getEl().dom);
35695         }
35696         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35697             var p = this.panels.first();
35698             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35699             tempEl.appendChild(p.getEl().dom);
35700             this.bodyEl.update("");
35701             this.bodyEl.dom.appendChild(p.getEl().dom);
35702             tempEl = null;
35703             this.updateTitle(p.getTitle());
35704             this.tabs = null;
35705             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35706             this.setActivePanel(p);
35707         }
35708         panel.setRegion(null);
35709         if(this.activePanel == panel){
35710             this.activePanel = null;
35711         }
35712         if(this.config.autoDestroy !== false && preservePanel !== true){
35713             try{panel.destroy();}catch(e){}
35714         }
35715         this.fireEvent("panelremoved", this, panel);
35716         return panel;
35717     },
35718
35719     /**
35720      * Returns the TabPanel component used by this region
35721      * @return {Roo.TabPanel}
35722      */
35723     getTabs : function(){
35724         return this.tabs;
35725     },
35726
35727     createTool : function(parentEl, className){
35728         var btn = Roo.DomHelper.append(parentEl, {
35729             tag: "div",
35730             cls: "x-layout-tools-button",
35731             children: [ {
35732                 tag: "div",
35733                 cls: "roo-layout-tools-button-inner " + className,
35734                 html: "&#160;"
35735             }]
35736         }, true);
35737         btn.addClassOnOver("roo-layout-tools-button-over");
35738         return btn;
35739     }
35740 });/*
35741  * Based on:
35742  * Ext JS Library 1.1.1
35743  * Copyright(c) 2006-2007, Ext JS, LLC.
35744  *
35745  * Originally Released Under LGPL - original licence link has changed is not relivant.
35746  *
35747  * Fork - LGPL
35748  * <script type="text/javascript">
35749  */
35750  
35751
35752
35753 /**
35754  * @class Roo.SplitLayoutRegion
35755  * @extends Roo.LayoutRegion
35756  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35757  */
35758 Roo.bootstrap.layout.Split = function(config){
35759     this.cursor = config.cursor;
35760     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35761 };
35762
35763 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35764 {
35765     splitTip : "Drag to resize.",
35766     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35767     useSplitTips : false,
35768
35769     applyConfig : function(config){
35770         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35771     },
35772     
35773     onRender : function(ctr,pos) {
35774         
35775         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35776         if(!this.config.split){
35777             return;
35778         }
35779         if(!this.split){
35780             
35781             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35782                             tag: "div",
35783                             id: this.el.id + "-split",
35784                             cls: "roo-layout-split roo-layout-split-"+this.position,
35785                             html: "&#160;"
35786             });
35787             /** The SplitBar for this region 
35788             * @type Roo.SplitBar */
35789             // does not exist yet...
35790             Roo.log([this.position, this.orientation]);
35791             
35792             this.split = new Roo.bootstrap.SplitBar({
35793                 dragElement : splitEl,
35794                 resizingElement: this.el,
35795                 orientation : this.orientation
35796             });
35797             
35798             this.split.on("moved", this.onSplitMove, this);
35799             this.split.useShim = this.config.useShim === true;
35800             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35801             if(this.useSplitTips){
35802                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35803             }
35804             //if(config.collapsible){
35805             //    this.split.el.on("dblclick", this.collapse,  this);
35806             //}
35807         }
35808         if(typeof this.config.minSize != "undefined"){
35809             this.split.minSize = this.config.minSize;
35810         }
35811         if(typeof this.config.maxSize != "undefined"){
35812             this.split.maxSize = this.config.maxSize;
35813         }
35814         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35815             this.hideSplitter();
35816         }
35817         
35818     },
35819
35820     getHMaxSize : function(){
35821          var cmax = this.config.maxSize || 10000;
35822          var center = this.mgr.getRegion("center");
35823          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35824     },
35825
35826     getVMaxSize : function(){
35827          var cmax = this.config.maxSize || 10000;
35828          var center = this.mgr.getRegion("center");
35829          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35830     },
35831
35832     onSplitMove : function(split, newSize){
35833         this.fireEvent("resized", this, newSize);
35834     },
35835     
35836     /** 
35837      * Returns the {@link Roo.SplitBar} for this region.
35838      * @return {Roo.SplitBar}
35839      */
35840     getSplitBar : function(){
35841         return this.split;
35842     },
35843     
35844     hide : function(){
35845         this.hideSplitter();
35846         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35847     },
35848
35849     hideSplitter : function(){
35850         if(this.split){
35851             this.split.el.setLocation(-2000,-2000);
35852             this.split.el.hide();
35853         }
35854     },
35855
35856     show : function(){
35857         if(this.split){
35858             this.split.el.show();
35859         }
35860         Roo.bootstrap.layout.Split.superclass.show.call(this);
35861     },
35862     
35863     beforeSlide: function(){
35864         if(Roo.isGecko){// firefox overflow auto bug workaround
35865             this.bodyEl.clip();
35866             if(this.tabs) {
35867                 this.tabs.bodyEl.clip();
35868             }
35869             if(this.activePanel){
35870                 this.activePanel.getEl().clip();
35871                 
35872                 if(this.activePanel.beforeSlide){
35873                     this.activePanel.beforeSlide();
35874                 }
35875             }
35876         }
35877     },
35878     
35879     afterSlide : function(){
35880         if(Roo.isGecko){// firefox overflow auto bug workaround
35881             this.bodyEl.unclip();
35882             if(this.tabs) {
35883                 this.tabs.bodyEl.unclip();
35884             }
35885             if(this.activePanel){
35886                 this.activePanel.getEl().unclip();
35887                 if(this.activePanel.afterSlide){
35888                     this.activePanel.afterSlide();
35889                 }
35890             }
35891         }
35892     },
35893
35894     initAutoHide : function(){
35895         if(this.autoHide !== false){
35896             if(!this.autoHideHd){
35897                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35898                 this.autoHideHd = {
35899                     "mouseout": function(e){
35900                         if(!e.within(this.el, true)){
35901                             st.delay(500);
35902                         }
35903                     },
35904                     "mouseover" : function(e){
35905                         st.cancel();
35906                     },
35907                     scope : this
35908                 };
35909             }
35910             this.el.on(this.autoHideHd);
35911         }
35912     },
35913
35914     clearAutoHide : function(){
35915         if(this.autoHide !== false){
35916             this.el.un("mouseout", this.autoHideHd.mouseout);
35917             this.el.un("mouseover", this.autoHideHd.mouseover);
35918         }
35919     },
35920
35921     clearMonitor : function(){
35922         Roo.get(document).un("click", this.slideInIf, this);
35923     },
35924
35925     // these names are backwards but not changed for compat
35926     slideOut : function(){
35927         if(this.isSlid || this.el.hasActiveFx()){
35928             return;
35929         }
35930         this.isSlid = true;
35931         if(this.collapseBtn){
35932             this.collapseBtn.hide();
35933         }
35934         this.closeBtnState = this.closeBtn.getStyle('display');
35935         this.closeBtn.hide();
35936         if(this.stickBtn){
35937             this.stickBtn.show();
35938         }
35939         this.el.show();
35940         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35941         this.beforeSlide();
35942         this.el.setStyle("z-index", 10001);
35943         this.el.slideIn(this.getSlideAnchor(), {
35944             callback: function(){
35945                 this.afterSlide();
35946                 this.initAutoHide();
35947                 Roo.get(document).on("click", this.slideInIf, this);
35948                 this.fireEvent("slideshow", this);
35949             },
35950             scope: this,
35951             block: true
35952         });
35953     },
35954
35955     afterSlideIn : function(){
35956         this.clearAutoHide();
35957         this.isSlid = false;
35958         this.clearMonitor();
35959         this.el.setStyle("z-index", "");
35960         if(this.collapseBtn){
35961             this.collapseBtn.show();
35962         }
35963         this.closeBtn.setStyle('display', this.closeBtnState);
35964         if(this.stickBtn){
35965             this.stickBtn.hide();
35966         }
35967         this.fireEvent("slidehide", this);
35968     },
35969
35970     slideIn : function(cb){
35971         if(!this.isSlid || this.el.hasActiveFx()){
35972             Roo.callback(cb);
35973             return;
35974         }
35975         this.isSlid = false;
35976         this.beforeSlide();
35977         this.el.slideOut(this.getSlideAnchor(), {
35978             callback: function(){
35979                 this.el.setLeftTop(-10000, -10000);
35980                 this.afterSlide();
35981                 this.afterSlideIn();
35982                 Roo.callback(cb);
35983             },
35984             scope: this,
35985             block: true
35986         });
35987     },
35988     
35989     slideInIf : function(e){
35990         if(!e.within(this.el)){
35991             this.slideIn();
35992         }
35993     },
35994
35995     animateCollapse : function(){
35996         this.beforeSlide();
35997         this.el.setStyle("z-index", 20000);
35998         var anchor = this.getSlideAnchor();
35999         this.el.slideOut(anchor, {
36000             callback : function(){
36001                 this.el.setStyle("z-index", "");
36002                 this.collapsedEl.slideIn(anchor, {duration:.3});
36003                 this.afterSlide();
36004                 this.el.setLocation(-10000,-10000);
36005                 this.el.hide();
36006                 this.fireEvent("collapsed", this);
36007             },
36008             scope: this,
36009             block: true
36010         });
36011     },
36012
36013     animateExpand : function(){
36014         this.beforeSlide();
36015         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36016         this.el.setStyle("z-index", 20000);
36017         this.collapsedEl.hide({
36018             duration:.1
36019         });
36020         this.el.slideIn(this.getSlideAnchor(), {
36021             callback : function(){
36022                 this.el.setStyle("z-index", "");
36023                 this.afterSlide();
36024                 if(this.split){
36025                     this.split.el.show();
36026                 }
36027                 this.fireEvent("invalidated", this);
36028                 this.fireEvent("expanded", this);
36029             },
36030             scope: this,
36031             block: true
36032         });
36033     },
36034
36035     anchors : {
36036         "west" : "left",
36037         "east" : "right",
36038         "north" : "top",
36039         "south" : "bottom"
36040     },
36041
36042     sanchors : {
36043         "west" : "l",
36044         "east" : "r",
36045         "north" : "t",
36046         "south" : "b"
36047     },
36048
36049     canchors : {
36050         "west" : "tl-tr",
36051         "east" : "tr-tl",
36052         "north" : "tl-bl",
36053         "south" : "bl-tl"
36054     },
36055
36056     getAnchor : function(){
36057         return this.anchors[this.position];
36058     },
36059
36060     getCollapseAnchor : function(){
36061         return this.canchors[this.position];
36062     },
36063
36064     getSlideAnchor : function(){
36065         return this.sanchors[this.position];
36066     },
36067
36068     getAlignAdj : function(){
36069         var cm = this.cmargins;
36070         switch(this.position){
36071             case "west":
36072                 return [0, 0];
36073             break;
36074             case "east":
36075                 return [0, 0];
36076             break;
36077             case "north":
36078                 return [0, 0];
36079             break;
36080             case "south":
36081                 return [0, 0];
36082             break;
36083         }
36084     },
36085
36086     getExpandAdj : function(){
36087         var c = this.collapsedEl, cm = this.cmargins;
36088         switch(this.position){
36089             case "west":
36090                 return [-(cm.right+c.getWidth()+cm.left), 0];
36091             break;
36092             case "east":
36093                 return [cm.right+c.getWidth()+cm.left, 0];
36094             break;
36095             case "north":
36096                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36097             break;
36098             case "south":
36099                 return [0, cm.top+cm.bottom+c.getHeight()];
36100             break;
36101         }
36102     }
36103 });/*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113 /*
36114  * These classes are private internal classes
36115  */
36116 Roo.bootstrap.layout.Center = function(config){
36117     config.region = "center";
36118     Roo.bootstrap.layout.Region.call(this, config);
36119     this.visible = true;
36120     this.minWidth = config.minWidth || 20;
36121     this.minHeight = config.minHeight || 20;
36122 };
36123
36124 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36125     hide : function(){
36126         // center panel can't be hidden
36127     },
36128     
36129     show : function(){
36130         // center panel can't be hidden
36131     },
36132     
36133     getMinWidth: function(){
36134         return this.minWidth;
36135     },
36136     
36137     getMinHeight: function(){
36138         return this.minHeight;
36139     }
36140 });
36141
36142
36143
36144
36145  
36146
36147
36148
36149
36150
36151 Roo.bootstrap.layout.North = function(config)
36152 {
36153     config.region = 'north';
36154     config.cursor = 'n-resize';
36155     
36156     Roo.bootstrap.layout.Split.call(this, config);
36157     
36158     
36159     if(this.split){
36160         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36161         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36162         this.split.el.addClass("roo-layout-split-v");
36163     }
36164     var size = config.initialSize || config.height;
36165     if(typeof size != "undefined"){
36166         this.el.setHeight(size);
36167     }
36168 };
36169 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36170 {
36171     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36172     
36173     
36174     
36175     getBox : function(){
36176         if(this.collapsed){
36177             return this.collapsedEl.getBox();
36178         }
36179         var box = this.el.getBox();
36180         if(this.split){
36181             box.height += this.split.el.getHeight();
36182         }
36183         return box;
36184     },
36185     
36186     updateBox : function(box){
36187         if(this.split && !this.collapsed){
36188             box.height -= this.split.el.getHeight();
36189             this.split.el.setLeft(box.x);
36190             this.split.el.setTop(box.y+box.height);
36191             this.split.el.setWidth(box.width);
36192         }
36193         if(this.collapsed){
36194             this.updateBody(box.width, null);
36195         }
36196         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36197     }
36198 });
36199
36200
36201
36202
36203
36204 Roo.bootstrap.layout.South = function(config){
36205     config.region = 'south';
36206     config.cursor = 's-resize';
36207     Roo.bootstrap.layout.Split.call(this, config);
36208     if(this.split){
36209         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36210         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36211         this.split.el.addClass("roo-layout-split-v");
36212     }
36213     var size = config.initialSize || config.height;
36214     if(typeof size != "undefined"){
36215         this.el.setHeight(size);
36216     }
36217 };
36218
36219 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36220     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36221     getBox : function(){
36222         if(this.collapsed){
36223             return this.collapsedEl.getBox();
36224         }
36225         var box = this.el.getBox();
36226         if(this.split){
36227             var sh = this.split.el.getHeight();
36228             box.height += sh;
36229             box.y -= sh;
36230         }
36231         return box;
36232     },
36233     
36234     updateBox : function(box){
36235         if(this.split && !this.collapsed){
36236             var sh = this.split.el.getHeight();
36237             box.height -= sh;
36238             box.y += sh;
36239             this.split.el.setLeft(box.x);
36240             this.split.el.setTop(box.y-sh);
36241             this.split.el.setWidth(box.width);
36242         }
36243         if(this.collapsed){
36244             this.updateBody(box.width, null);
36245         }
36246         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36247     }
36248 });
36249
36250 Roo.bootstrap.layout.East = function(config){
36251     config.region = "east";
36252     config.cursor = "e-resize";
36253     Roo.bootstrap.layout.Split.call(this, config);
36254     if(this.split){
36255         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36256         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36257         this.split.el.addClass("roo-layout-split-h");
36258     }
36259     var size = config.initialSize || config.width;
36260     if(typeof size != "undefined"){
36261         this.el.setWidth(size);
36262     }
36263 };
36264 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36265     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36266     getBox : function(){
36267         if(this.collapsed){
36268             return this.collapsedEl.getBox();
36269         }
36270         var box = this.el.getBox();
36271         if(this.split){
36272             var sw = this.split.el.getWidth();
36273             box.width += sw;
36274             box.x -= sw;
36275         }
36276         return box;
36277     },
36278
36279     updateBox : function(box){
36280         if(this.split && !this.collapsed){
36281             var sw = this.split.el.getWidth();
36282             box.width -= sw;
36283             this.split.el.setLeft(box.x);
36284             this.split.el.setTop(box.y);
36285             this.split.el.setHeight(box.height);
36286             box.x += sw;
36287         }
36288         if(this.collapsed){
36289             this.updateBody(null, box.height);
36290         }
36291         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36292     }
36293 });
36294
36295 Roo.bootstrap.layout.West = function(config){
36296     config.region = "west";
36297     config.cursor = "w-resize";
36298     
36299     Roo.bootstrap.layout.Split.call(this, config);
36300     if(this.split){
36301         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36302         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36303         this.split.el.addClass("roo-layout-split-h");
36304     }
36305     
36306 };
36307 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36308     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36309     
36310     onRender: function(ctr, pos)
36311     {
36312         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36313         var size = this.config.initialSize || this.config.width;
36314         if(typeof size != "undefined"){
36315             this.el.setWidth(size);
36316         }
36317     },
36318     
36319     getBox : function(){
36320         if(this.collapsed){
36321             return this.collapsedEl.getBox();
36322         }
36323         var box = this.el.getBox();
36324         if(this.split){
36325             box.width += this.split.el.getWidth();
36326         }
36327         return box;
36328     },
36329     
36330     updateBox : function(box){
36331         if(this.split && !this.collapsed){
36332             var sw = this.split.el.getWidth();
36333             box.width -= sw;
36334             this.split.el.setLeft(box.x+box.width);
36335             this.split.el.setTop(box.y);
36336             this.split.el.setHeight(box.height);
36337         }
36338         if(this.collapsed){
36339             this.updateBody(null, box.height);
36340         }
36341         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36342     }
36343 });
36344 Roo.namespace("Roo.bootstrap.panel");/*
36345  * Based on:
36346  * Ext JS Library 1.1.1
36347  * Copyright(c) 2006-2007, Ext JS, LLC.
36348  *
36349  * Originally Released Under LGPL - original licence link has changed is not relivant.
36350  *
36351  * Fork - LGPL
36352  * <script type="text/javascript">
36353  */
36354 /**
36355  * @class Roo.ContentPanel
36356  * @extends Roo.util.Observable
36357  * A basic ContentPanel element.
36358  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36359  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36360  * @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
36361  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36362  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36363  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36364  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36365  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36366  * @cfg {String} title          The title for this panel
36367  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36368  * @cfg {String} url            Calls {@link #setUrl} with this value
36369  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36370  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36371  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36372  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36373  * @cfg {Boolean} badges render the badges
36374
36375  * @constructor
36376  * Create a new ContentPanel.
36377  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36378  * @param {String/Object} config A string to set only the title or a config object
36379  * @param {String} content (optional) Set the HTML content for this panel
36380  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36381  */
36382 Roo.bootstrap.panel.Content = function( config){
36383     
36384     this.tpl = config.tpl || false;
36385     
36386     var el = config.el;
36387     var content = config.content;
36388
36389     if(config.autoCreate){ // xtype is available if this is called from factory
36390         el = Roo.id();
36391     }
36392     this.el = Roo.get(el);
36393     if(!this.el && config && config.autoCreate){
36394         if(typeof config.autoCreate == "object"){
36395             if(!config.autoCreate.id){
36396                 config.autoCreate.id = config.id||el;
36397             }
36398             this.el = Roo.DomHelper.append(document.body,
36399                         config.autoCreate, true);
36400         }else{
36401             var elcfg =  {   tag: "div",
36402                             cls: "roo-layout-inactive-content",
36403                             id: config.id||el
36404                             };
36405             if (config.html) {
36406                 elcfg.html = config.html;
36407                 
36408             }
36409                         
36410             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36411         }
36412     } 
36413     this.closable = false;
36414     this.loaded = false;
36415     this.active = false;
36416    
36417       
36418     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36419         
36420         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36421         
36422         this.wrapEl = this.el; //this.el.wrap();
36423         var ti = [];
36424         if (config.toolbar.items) {
36425             ti = config.toolbar.items ;
36426             delete config.toolbar.items ;
36427         }
36428         
36429         var nitems = [];
36430         this.toolbar.render(this.wrapEl, 'before');
36431         for(var i =0;i < ti.length;i++) {
36432           //  Roo.log(['add child', items[i]]);
36433             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36434         }
36435         this.toolbar.items = nitems;
36436         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36437         delete config.toolbar;
36438         
36439     }
36440     /*
36441     // xtype created footer. - not sure if will work as we normally have to render first..
36442     if (this.footer && !this.footer.el && this.footer.xtype) {
36443         if (!this.wrapEl) {
36444             this.wrapEl = this.el.wrap();
36445         }
36446     
36447         this.footer.container = this.wrapEl.createChild();
36448          
36449         this.footer = Roo.factory(this.footer, Roo);
36450         
36451     }
36452     */
36453     
36454      if(typeof config == "string"){
36455         this.title = config;
36456     }else{
36457         Roo.apply(this, config);
36458     }
36459     
36460     if(this.resizeEl){
36461         this.resizeEl = Roo.get(this.resizeEl, true);
36462     }else{
36463         this.resizeEl = this.el;
36464     }
36465     // handle view.xtype
36466     
36467  
36468     
36469     
36470     this.addEvents({
36471         /**
36472          * @event activate
36473          * Fires when this panel is activated. 
36474          * @param {Roo.ContentPanel} this
36475          */
36476         "activate" : true,
36477         /**
36478          * @event deactivate
36479          * Fires when this panel is activated. 
36480          * @param {Roo.ContentPanel} this
36481          */
36482         "deactivate" : true,
36483
36484         /**
36485          * @event resize
36486          * Fires when this panel is resized if fitToFrame is true.
36487          * @param {Roo.ContentPanel} this
36488          * @param {Number} width The width after any component adjustments
36489          * @param {Number} height The height after any component adjustments
36490          */
36491         "resize" : true,
36492         
36493          /**
36494          * @event render
36495          * Fires when this tab is created
36496          * @param {Roo.ContentPanel} this
36497          */
36498         "render" : true
36499         
36500         
36501         
36502     });
36503     
36504
36505     
36506     
36507     if(this.autoScroll){
36508         this.resizeEl.setStyle("overflow", "auto");
36509     } else {
36510         // fix randome scrolling
36511         //this.el.on('scroll', function() {
36512         //    Roo.log('fix random scolling');
36513         //    this.scrollTo('top',0); 
36514         //});
36515     }
36516     content = content || this.content;
36517     if(content){
36518         this.setContent(content);
36519     }
36520     if(config && config.url){
36521         this.setUrl(this.url, this.params, this.loadOnce);
36522     }
36523     
36524     
36525     
36526     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36527     
36528     if (this.view && typeof(this.view.xtype) != 'undefined') {
36529         this.view.el = this.el.appendChild(document.createElement("div"));
36530         this.view = Roo.factory(this.view); 
36531         this.view.render  &&  this.view.render(false, '');  
36532     }
36533     
36534     
36535     this.fireEvent('render', this);
36536 };
36537
36538 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36539     
36540     tabTip : '',
36541     
36542     setRegion : function(region){
36543         this.region = region;
36544         this.setActiveClass(region && !this.background);
36545     },
36546     
36547     
36548     setActiveClass: function(state)
36549     {
36550         if(state){
36551            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36552            this.el.setStyle('position','relative');
36553         }else{
36554            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36555            this.el.setStyle('position', 'absolute');
36556         } 
36557     },
36558     
36559     /**
36560      * Returns the toolbar for this Panel if one was configured. 
36561      * @return {Roo.Toolbar} 
36562      */
36563     getToolbar : function(){
36564         return this.toolbar;
36565     },
36566     
36567     setActiveState : function(active)
36568     {
36569         this.active = active;
36570         this.setActiveClass(active);
36571         if(!active){
36572             if(this.fireEvent("deactivate", this) === false){
36573                 return false;
36574             }
36575             return true;
36576         }
36577         this.fireEvent("activate", this);
36578         return true;
36579     },
36580     /**
36581      * Updates this panel's element
36582      * @param {String} content The new content
36583      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36584     */
36585     setContent : function(content, loadScripts){
36586         this.el.update(content, loadScripts);
36587     },
36588
36589     ignoreResize : function(w, h){
36590         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36591             return true;
36592         }else{
36593             this.lastSize = {width: w, height: h};
36594             return false;
36595         }
36596     },
36597     /**
36598      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36599      * @return {Roo.UpdateManager} The UpdateManager
36600      */
36601     getUpdateManager : function(){
36602         return this.el.getUpdateManager();
36603     },
36604      /**
36605      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36606      * @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:
36607 <pre><code>
36608 panel.load({
36609     url: "your-url.php",
36610     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36611     callback: yourFunction,
36612     scope: yourObject, //(optional scope)
36613     discardUrl: false,
36614     nocache: false,
36615     text: "Loading...",
36616     timeout: 30,
36617     scripts: false
36618 });
36619 </code></pre>
36620      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36621      * 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.
36622      * @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}
36623      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36624      * @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.
36625      * @return {Roo.ContentPanel} this
36626      */
36627     load : function(){
36628         var um = this.el.getUpdateManager();
36629         um.update.apply(um, arguments);
36630         return this;
36631     },
36632
36633
36634     /**
36635      * 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.
36636      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36637      * @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)
36638      * @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)
36639      * @return {Roo.UpdateManager} The UpdateManager
36640      */
36641     setUrl : function(url, params, loadOnce){
36642         if(this.refreshDelegate){
36643             this.removeListener("activate", this.refreshDelegate);
36644         }
36645         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36646         this.on("activate", this.refreshDelegate);
36647         return this.el.getUpdateManager();
36648     },
36649     
36650     _handleRefresh : function(url, params, loadOnce){
36651         if(!loadOnce || !this.loaded){
36652             var updater = this.el.getUpdateManager();
36653             updater.update(url, params, this._setLoaded.createDelegate(this));
36654         }
36655     },
36656     
36657     _setLoaded : function(){
36658         this.loaded = true;
36659     }, 
36660     
36661     /**
36662      * Returns this panel's id
36663      * @return {String} 
36664      */
36665     getId : function(){
36666         return this.el.id;
36667     },
36668     
36669     /** 
36670      * Returns this panel's element - used by regiosn to add.
36671      * @return {Roo.Element} 
36672      */
36673     getEl : function(){
36674         return this.wrapEl || this.el;
36675     },
36676     
36677    
36678     
36679     adjustForComponents : function(width, height)
36680     {
36681         //Roo.log('adjustForComponents ');
36682         if(this.resizeEl != this.el){
36683             width -= this.el.getFrameWidth('lr');
36684             height -= this.el.getFrameWidth('tb');
36685         }
36686         if(this.toolbar){
36687             var te = this.toolbar.getEl();
36688             te.setWidth(width);
36689             height -= te.getHeight();
36690         }
36691         if(this.footer){
36692             var te = this.footer.getEl();
36693             te.setWidth(width);
36694             height -= te.getHeight();
36695         }
36696         
36697         
36698         if(this.adjustments){
36699             width += this.adjustments[0];
36700             height += this.adjustments[1];
36701         }
36702         return {"width": width, "height": height};
36703     },
36704     
36705     setSize : function(width, height){
36706         if(this.fitToFrame && !this.ignoreResize(width, height)){
36707             if(this.fitContainer && this.resizeEl != this.el){
36708                 this.el.setSize(width, height);
36709             }
36710             var size = this.adjustForComponents(width, height);
36711             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36712             this.fireEvent('resize', this, size.width, size.height);
36713         }
36714     },
36715     
36716     /**
36717      * Returns this panel's title
36718      * @return {String} 
36719      */
36720     getTitle : function(){
36721         
36722         if (typeof(this.title) != 'object') {
36723             return this.title;
36724         }
36725         
36726         var t = '';
36727         for (var k in this.title) {
36728             if (!this.title.hasOwnProperty(k)) {
36729                 continue;
36730             }
36731             
36732             if (k.indexOf('-') >= 0) {
36733                 var s = k.split('-');
36734                 for (var i = 0; i<s.length; i++) {
36735                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36736                 }
36737             } else {
36738                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36739             }
36740         }
36741         return t;
36742     },
36743     
36744     /**
36745      * Set this panel's title
36746      * @param {String} title
36747      */
36748     setTitle : function(title){
36749         this.title = title;
36750         if(this.region){
36751             this.region.updatePanelTitle(this, title);
36752         }
36753     },
36754     
36755     /**
36756      * Returns true is this panel was configured to be closable
36757      * @return {Boolean} 
36758      */
36759     isClosable : function(){
36760         return this.closable;
36761     },
36762     
36763     beforeSlide : function(){
36764         this.el.clip();
36765         this.resizeEl.clip();
36766     },
36767     
36768     afterSlide : function(){
36769         this.el.unclip();
36770         this.resizeEl.unclip();
36771     },
36772     
36773     /**
36774      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36775      *   Will fail silently if the {@link #setUrl} method has not been called.
36776      *   This does not activate the panel, just updates its content.
36777      */
36778     refresh : function(){
36779         if(this.refreshDelegate){
36780            this.loaded = false;
36781            this.refreshDelegate();
36782         }
36783     },
36784     
36785     /**
36786      * Destroys this panel
36787      */
36788     destroy : function(){
36789         this.el.removeAllListeners();
36790         var tempEl = document.createElement("span");
36791         tempEl.appendChild(this.el.dom);
36792         tempEl.innerHTML = "";
36793         this.el.remove();
36794         this.el = null;
36795     },
36796     
36797     /**
36798      * form - if the content panel contains a form - this is a reference to it.
36799      * @type {Roo.form.Form}
36800      */
36801     form : false,
36802     /**
36803      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36804      *    This contains a reference to it.
36805      * @type {Roo.View}
36806      */
36807     view : false,
36808     
36809       /**
36810      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36811      * <pre><code>
36812
36813 layout.addxtype({
36814        xtype : 'Form',
36815        items: [ .... ]
36816    }
36817 );
36818
36819 </code></pre>
36820      * @param {Object} cfg Xtype definition of item to add.
36821      */
36822     
36823     
36824     getChildContainer: function () {
36825         return this.getEl();
36826     }
36827     
36828     
36829     /*
36830         var  ret = new Roo.factory(cfg);
36831         return ret;
36832         
36833         
36834         // add form..
36835         if (cfg.xtype.match(/^Form$/)) {
36836             
36837             var el;
36838             //if (this.footer) {
36839             //    el = this.footer.container.insertSibling(false, 'before');
36840             //} else {
36841                 el = this.el.createChild();
36842             //}
36843
36844             this.form = new  Roo.form.Form(cfg);
36845             
36846             
36847             if ( this.form.allItems.length) {
36848                 this.form.render(el.dom);
36849             }
36850             return this.form;
36851         }
36852         // should only have one of theses..
36853         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36854             // views.. should not be just added - used named prop 'view''
36855             
36856             cfg.el = this.el.appendChild(document.createElement("div"));
36857             // factory?
36858             
36859             var ret = new Roo.factory(cfg);
36860              
36861              ret.render && ret.render(false, ''); // render blank..
36862             this.view = ret;
36863             return ret;
36864         }
36865         return false;
36866     }
36867     \*/
36868 });
36869  
36870 /**
36871  * @class Roo.bootstrap.panel.Grid
36872  * @extends Roo.bootstrap.panel.Content
36873  * @constructor
36874  * Create a new GridPanel.
36875  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36876  * @param {Object} config A the config object
36877   
36878  */
36879
36880
36881
36882 Roo.bootstrap.panel.Grid = function(config)
36883 {
36884     
36885       
36886     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36887         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36888
36889     config.el = this.wrapper;
36890     //this.el = this.wrapper;
36891     
36892       if (config.container) {
36893         // ctor'ed from a Border/panel.grid
36894         
36895         
36896         this.wrapper.setStyle("overflow", "hidden");
36897         this.wrapper.addClass('roo-grid-container');
36898
36899     }
36900     
36901     
36902     if(config.toolbar){
36903         var tool_el = this.wrapper.createChild();    
36904         this.toolbar = Roo.factory(config.toolbar);
36905         var ti = [];
36906         if (config.toolbar.items) {
36907             ti = config.toolbar.items ;
36908             delete config.toolbar.items ;
36909         }
36910         
36911         var nitems = [];
36912         this.toolbar.render(tool_el);
36913         for(var i =0;i < ti.length;i++) {
36914           //  Roo.log(['add child', items[i]]);
36915             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36916         }
36917         this.toolbar.items = nitems;
36918         
36919         delete config.toolbar;
36920     }
36921     
36922     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36923     config.grid.scrollBody = true;;
36924     config.grid.monitorWindowResize = false; // turn off autosizing
36925     config.grid.autoHeight = false;
36926     config.grid.autoWidth = false;
36927     
36928     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36929     
36930     if (config.background) {
36931         // render grid on panel activation (if panel background)
36932         this.on('activate', function(gp) {
36933             if (!gp.grid.rendered) {
36934                 gp.grid.render(this.wrapper);
36935                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36936             }
36937         });
36938             
36939     } else {
36940         this.grid.render(this.wrapper);
36941         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36942
36943     }
36944     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36945     // ??? needed ??? config.el = this.wrapper;
36946     
36947     
36948     
36949   
36950     // xtype created footer. - not sure if will work as we normally have to render first..
36951     if (this.footer && !this.footer.el && this.footer.xtype) {
36952         
36953         var ctr = this.grid.getView().getFooterPanel(true);
36954         this.footer.dataSource = this.grid.dataSource;
36955         this.footer = Roo.factory(this.footer, Roo);
36956         this.footer.render(ctr);
36957         
36958     }
36959     
36960     
36961     
36962     
36963      
36964 };
36965
36966 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36967     getId : function(){
36968         return this.grid.id;
36969     },
36970     
36971     /**
36972      * Returns the grid for this panel
36973      * @return {Roo.bootstrap.Table} 
36974      */
36975     getGrid : function(){
36976         return this.grid;    
36977     },
36978     
36979     setSize : function(width, height){
36980         if(!this.ignoreResize(width, height)){
36981             var grid = this.grid;
36982             var size = this.adjustForComponents(width, height);
36983             var gridel = grid.getGridEl();
36984             gridel.setSize(size.width, size.height);
36985             /*
36986             var thd = grid.getGridEl().select('thead',true).first();
36987             var tbd = grid.getGridEl().select('tbody', true).first();
36988             if (tbd) {
36989                 tbd.setSize(width, height - thd.getHeight());
36990             }
36991             */
36992             grid.autoSize();
36993         }
36994     },
36995      
36996     
36997     
36998     beforeSlide : function(){
36999         this.grid.getView().scroller.clip();
37000     },
37001     
37002     afterSlide : function(){
37003         this.grid.getView().scroller.unclip();
37004     },
37005     
37006     destroy : function(){
37007         this.grid.destroy();
37008         delete this.grid;
37009         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37010     }
37011 });
37012
37013 /**
37014  * @class Roo.bootstrap.panel.Nest
37015  * @extends Roo.bootstrap.panel.Content
37016  * @constructor
37017  * Create a new Panel, that can contain a layout.Border.
37018  * 
37019  * 
37020  * @param {Roo.BorderLayout} layout The layout for this panel
37021  * @param {String/Object} config A string to set only the title or a config object
37022  */
37023 Roo.bootstrap.panel.Nest = function(config)
37024 {
37025     // construct with only one argument..
37026     /* FIXME - implement nicer consturctors
37027     if (layout.layout) {
37028         config = layout;
37029         layout = config.layout;
37030         delete config.layout;
37031     }
37032     if (layout.xtype && !layout.getEl) {
37033         // then layout needs constructing..
37034         layout = Roo.factory(layout, Roo);
37035     }
37036     */
37037     
37038     config.el =  config.layout.getEl();
37039     
37040     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37041     
37042     config.layout.monitorWindowResize = false; // turn off autosizing
37043     this.layout = config.layout;
37044     this.layout.getEl().addClass("roo-layout-nested-layout");
37045     
37046     
37047     
37048     
37049 };
37050
37051 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37052
37053     setSize : function(width, height){
37054         if(!this.ignoreResize(width, height)){
37055             var size = this.adjustForComponents(width, height);
37056             var el = this.layout.getEl();
37057             if (size.height < 1) {
37058                 el.setWidth(size.width);   
37059             } else {
37060                 el.setSize(size.width, size.height);
37061             }
37062             var touch = el.dom.offsetWidth;
37063             this.layout.layout();
37064             // ie requires a double layout on the first pass
37065             if(Roo.isIE && !this.initialized){
37066                 this.initialized = true;
37067                 this.layout.layout();
37068             }
37069         }
37070     },
37071     
37072     // activate all subpanels if not currently active..
37073     
37074     setActiveState : function(active){
37075         this.active = active;
37076         this.setActiveClass(active);
37077         
37078         if(!active){
37079             this.fireEvent("deactivate", this);
37080             return;
37081         }
37082         
37083         this.fireEvent("activate", this);
37084         // not sure if this should happen before or after..
37085         if (!this.layout) {
37086             return; // should not happen..
37087         }
37088         var reg = false;
37089         for (var r in this.layout.regions) {
37090             reg = this.layout.getRegion(r);
37091             if (reg.getActivePanel()) {
37092                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37093                 reg.setActivePanel(reg.getActivePanel());
37094                 continue;
37095             }
37096             if (!reg.panels.length) {
37097                 continue;
37098             }
37099             reg.showPanel(reg.getPanel(0));
37100         }
37101         
37102         
37103         
37104         
37105     },
37106     
37107     /**
37108      * Returns the nested BorderLayout for this panel
37109      * @return {Roo.BorderLayout} 
37110      */
37111     getLayout : function(){
37112         return this.layout;
37113     },
37114     
37115      /**
37116      * Adds a xtype elements to the layout of the nested panel
37117      * <pre><code>
37118
37119 panel.addxtype({
37120        xtype : 'ContentPanel',
37121        region: 'west',
37122        items: [ .... ]
37123    }
37124 );
37125
37126 panel.addxtype({
37127         xtype : 'NestedLayoutPanel',
37128         region: 'west',
37129         layout: {
37130            center: { },
37131            west: { }   
37132         },
37133         items : [ ... list of content panels or nested layout panels.. ]
37134    }
37135 );
37136 </code></pre>
37137      * @param {Object} cfg Xtype definition of item to add.
37138      */
37139     addxtype : function(cfg) {
37140         return this.layout.addxtype(cfg);
37141     
37142     }
37143 });        /*
37144  * Based on:
37145  * Ext JS Library 1.1.1
37146  * Copyright(c) 2006-2007, Ext JS, LLC.
37147  *
37148  * Originally Released Under LGPL - original licence link has changed is not relivant.
37149  *
37150  * Fork - LGPL
37151  * <script type="text/javascript">
37152  */
37153 /**
37154  * @class Roo.TabPanel
37155  * @extends Roo.util.Observable
37156  * A lightweight tab container.
37157  * <br><br>
37158  * Usage:
37159  * <pre><code>
37160 // basic tabs 1, built from existing content
37161 var tabs = new Roo.TabPanel("tabs1");
37162 tabs.addTab("script", "View Script");
37163 tabs.addTab("markup", "View Markup");
37164 tabs.activate("script");
37165
37166 // more advanced tabs, built from javascript
37167 var jtabs = new Roo.TabPanel("jtabs");
37168 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37169
37170 // set up the UpdateManager
37171 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37172 var updater = tab2.getUpdateManager();
37173 updater.setDefaultUrl("ajax1.htm");
37174 tab2.on('activate', updater.refresh, updater, true);
37175
37176 // Use setUrl for Ajax loading
37177 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37178 tab3.setUrl("ajax2.htm", null, true);
37179
37180 // Disabled tab
37181 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37182 tab4.disable();
37183
37184 jtabs.activate("jtabs-1");
37185  * </code></pre>
37186  * @constructor
37187  * Create a new TabPanel.
37188  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37189  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37190  */
37191 Roo.bootstrap.panel.Tabs = function(config){
37192     /**
37193     * The container element for this TabPanel.
37194     * @type Roo.Element
37195     */
37196     this.el = Roo.get(config.el);
37197     delete config.el;
37198     if(config){
37199         if(typeof config == "boolean"){
37200             this.tabPosition = config ? "bottom" : "top";
37201         }else{
37202             Roo.apply(this, config);
37203         }
37204     }
37205     
37206     if(this.tabPosition == "bottom"){
37207         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37208         this.el.addClass("roo-tabs-bottom");
37209     }
37210     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37211     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37212     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37213     if(Roo.isIE){
37214         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37215     }
37216     if(this.tabPosition != "bottom"){
37217         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37218          * @type Roo.Element
37219          */
37220         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37221         this.el.addClass("roo-tabs-top");
37222     }
37223     this.items = [];
37224
37225     this.bodyEl.setStyle("position", "relative");
37226
37227     this.active = null;
37228     this.activateDelegate = this.activate.createDelegate(this);
37229
37230     this.addEvents({
37231         /**
37232          * @event tabchange
37233          * Fires when the active tab changes
37234          * @param {Roo.TabPanel} this
37235          * @param {Roo.TabPanelItem} activePanel The new active tab
37236          */
37237         "tabchange": true,
37238         /**
37239          * @event beforetabchange
37240          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37241          * @param {Roo.TabPanel} this
37242          * @param {Object} e Set cancel to true on this object to cancel the tab change
37243          * @param {Roo.TabPanelItem} tab The tab being changed to
37244          */
37245         "beforetabchange" : true
37246     });
37247
37248     Roo.EventManager.onWindowResize(this.onResize, this);
37249     this.cpad = this.el.getPadding("lr");
37250     this.hiddenCount = 0;
37251
37252
37253     // toolbar on the tabbar support...
37254     if (this.toolbar) {
37255         alert("no toolbar support yet");
37256         this.toolbar  = false;
37257         /*
37258         var tcfg = this.toolbar;
37259         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37260         this.toolbar = new Roo.Toolbar(tcfg);
37261         if (Roo.isSafari) {
37262             var tbl = tcfg.container.child('table', true);
37263             tbl.setAttribute('width', '100%');
37264         }
37265         */
37266         
37267     }
37268    
37269
37270
37271     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37272 };
37273
37274 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37275     /*
37276      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37277      */
37278     tabPosition : "top",
37279     /*
37280      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37281      */
37282     currentTabWidth : 0,
37283     /*
37284      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37285      */
37286     minTabWidth : 40,
37287     /*
37288      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37289      */
37290     maxTabWidth : 250,
37291     /*
37292      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37293      */
37294     preferredTabWidth : 175,
37295     /*
37296      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37297      */
37298     resizeTabs : false,
37299     /*
37300      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37301      */
37302     monitorResize : true,
37303     /*
37304      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37305      */
37306     toolbar : false,
37307
37308     /**
37309      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37310      * @param {String} id The id of the div to use <b>or create</b>
37311      * @param {String} text The text for the tab
37312      * @param {String} content (optional) Content to put in the TabPanelItem body
37313      * @param {Boolean} closable (optional) True to create a close icon on the tab
37314      * @return {Roo.TabPanelItem} The created TabPanelItem
37315      */
37316     addTab : function(id, text, content, closable, tpl)
37317     {
37318         var item = new Roo.bootstrap.panel.TabItem({
37319             panel: this,
37320             id : id,
37321             text : text,
37322             closable : closable,
37323             tpl : tpl
37324         });
37325         this.addTabItem(item);
37326         if(content){
37327             item.setContent(content);
37328         }
37329         return item;
37330     },
37331
37332     /**
37333      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37334      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37335      * @return {Roo.TabPanelItem}
37336      */
37337     getTab : function(id){
37338         return this.items[id];
37339     },
37340
37341     /**
37342      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37343      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37344      */
37345     hideTab : function(id){
37346         var t = this.items[id];
37347         if(!t.isHidden()){
37348            t.setHidden(true);
37349            this.hiddenCount++;
37350            this.autoSizeTabs();
37351         }
37352     },
37353
37354     /**
37355      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37356      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37357      */
37358     unhideTab : function(id){
37359         var t = this.items[id];
37360         if(t.isHidden()){
37361            t.setHidden(false);
37362            this.hiddenCount--;
37363            this.autoSizeTabs();
37364         }
37365     },
37366
37367     /**
37368      * Adds an existing {@link Roo.TabPanelItem}.
37369      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37370      */
37371     addTabItem : function(item){
37372         this.items[item.id] = item;
37373         this.items.push(item);
37374       //  if(this.resizeTabs){
37375     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37376   //         this.autoSizeTabs();
37377 //        }else{
37378 //            item.autoSize();
37379        // }
37380     },
37381
37382     /**
37383      * Removes a {@link Roo.TabPanelItem}.
37384      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37385      */
37386     removeTab : function(id){
37387         var items = this.items;
37388         var tab = items[id];
37389         if(!tab) { return; }
37390         var index = items.indexOf(tab);
37391         if(this.active == tab && items.length > 1){
37392             var newTab = this.getNextAvailable(index);
37393             if(newTab) {
37394                 newTab.activate();
37395             }
37396         }
37397         this.stripEl.dom.removeChild(tab.pnode.dom);
37398         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37399             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37400         }
37401         items.splice(index, 1);
37402         delete this.items[tab.id];
37403         tab.fireEvent("close", tab);
37404         tab.purgeListeners();
37405         this.autoSizeTabs();
37406     },
37407
37408     getNextAvailable : function(start){
37409         var items = this.items;
37410         var index = start;
37411         // look for a next tab that will slide over to
37412         // replace the one being removed
37413         while(index < items.length){
37414             var item = items[++index];
37415             if(item && !item.isHidden()){
37416                 return item;
37417             }
37418         }
37419         // if one isn't found select the previous tab (on the left)
37420         index = start;
37421         while(index >= 0){
37422             var item = items[--index];
37423             if(item && !item.isHidden()){
37424                 return item;
37425             }
37426         }
37427         return null;
37428     },
37429
37430     /**
37431      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37432      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37433      */
37434     disableTab : function(id){
37435         var tab = this.items[id];
37436         if(tab && this.active != tab){
37437             tab.disable();
37438         }
37439     },
37440
37441     /**
37442      * Enables a {@link Roo.TabPanelItem} that is disabled.
37443      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37444      */
37445     enableTab : function(id){
37446         var tab = this.items[id];
37447         tab.enable();
37448     },
37449
37450     /**
37451      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37452      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37453      * @return {Roo.TabPanelItem} The TabPanelItem.
37454      */
37455     activate : function(id){
37456         var tab = this.items[id];
37457         if(!tab){
37458             return null;
37459         }
37460         if(tab == this.active || tab.disabled){
37461             return tab;
37462         }
37463         var e = {};
37464         this.fireEvent("beforetabchange", this, e, tab);
37465         if(e.cancel !== true && !tab.disabled){
37466             if(this.active){
37467                 this.active.hide();
37468             }
37469             this.active = this.items[id];
37470             this.active.show();
37471             this.fireEvent("tabchange", this, this.active);
37472         }
37473         return tab;
37474     },
37475
37476     /**
37477      * Gets the active {@link Roo.TabPanelItem}.
37478      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37479      */
37480     getActiveTab : function(){
37481         return this.active;
37482     },
37483
37484     /**
37485      * Updates the tab body element to fit the height of the container element
37486      * for overflow scrolling
37487      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37488      */
37489     syncHeight : function(targetHeight){
37490         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37491         var bm = this.bodyEl.getMargins();
37492         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37493         this.bodyEl.setHeight(newHeight);
37494         return newHeight;
37495     },
37496
37497     onResize : function(){
37498         if(this.monitorResize){
37499             this.autoSizeTabs();
37500         }
37501     },
37502
37503     /**
37504      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37505      */
37506     beginUpdate : function(){
37507         this.updating = true;
37508     },
37509
37510     /**
37511      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37512      */
37513     endUpdate : function(){
37514         this.updating = false;
37515         this.autoSizeTabs();
37516     },
37517
37518     /**
37519      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37520      */
37521     autoSizeTabs : function(){
37522         var count = this.items.length;
37523         var vcount = count - this.hiddenCount;
37524         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37525             return;
37526         }
37527         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37528         var availWidth = Math.floor(w / vcount);
37529         var b = this.stripBody;
37530         if(b.getWidth() > w){
37531             var tabs = this.items;
37532             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37533             if(availWidth < this.minTabWidth){
37534                 /*if(!this.sleft){    // incomplete scrolling code
37535                     this.createScrollButtons();
37536                 }
37537                 this.showScroll();
37538                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37539             }
37540         }else{
37541             if(this.currentTabWidth < this.preferredTabWidth){
37542                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37543             }
37544         }
37545     },
37546
37547     /**
37548      * Returns the number of tabs in this TabPanel.
37549      * @return {Number}
37550      */
37551      getCount : function(){
37552          return this.items.length;
37553      },
37554
37555     /**
37556      * Resizes all the tabs to the passed width
37557      * @param {Number} The new width
37558      */
37559     setTabWidth : function(width){
37560         this.currentTabWidth = width;
37561         for(var i = 0, len = this.items.length; i < len; i++) {
37562                 if(!this.items[i].isHidden()) {
37563                 this.items[i].setWidth(width);
37564             }
37565         }
37566     },
37567
37568     /**
37569      * Destroys this TabPanel
37570      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37571      */
37572     destroy : function(removeEl){
37573         Roo.EventManager.removeResizeListener(this.onResize, this);
37574         for(var i = 0, len = this.items.length; i < len; i++){
37575             this.items[i].purgeListeners();
37576         }
37577         if(removeEl === true){
37578             this.el.update("");
37579             this.el.remove();
37580         }
37581     },
37582     
37583     createStrip : function(container)
37584     {
37585         var strip = document.createElement("nav");
37586         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37587         container.appendChild(strip);
37588         return strip;
37589     },
37590     
37591     createStripList : function(strip)
37592     {
37593         // div wrapper for retard IE
37594         // returns the "tr" element.
37595         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37596         //'<div class="x-tabs-strip-wrap">'+
37597           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37598           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37599         return strip.firstChild; //.firstChild.firstChild.firstChild;
37600     },
37601     createBody : function(container)
37602     {
37603         var body = document.createElement("div");
37604         Roo.id(body, "tab-body");
37605         //Roo.fly(body).addClass("x-tabs-body");
37606         Roo.fly(body).addClass("tab-content");
37607         container.appendChild(body);
37608         return body;
37609     },
37610     createItemBody :function(bodyEl, id){
37611         var body = Roo.getDom(id);
37612         if(!body){
37613             body = document.createElement("div");
37614             body.id = id;
37615         }
37616         //Roo.fly(body).addClass("x-tabs-item-body");
37617         Roo.fly(body).addClass("tab-pane");
37618          bodyEl.insertBefore(body, bodyEl.firstChild);
37619         return body;
37620     },
37621     /** @private */
37622     createStripElements :  function(stripEl, text, closable, tpl)
37623     {
37624         var td = document.createElement("li"); // was td..
37625         
37626         
37627         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37628         
37629         
37630         stripEl.appendChild(td);
37631         /*if(closable){
37632             td.className = "x-tabs-closable";
37633             if(!this.closeTpl){
37634                 this.closeTpl = new Roo.Template(
37635                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37636                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37637                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37638                 );
37639             }
37640             var el = this.closeTpl.overwrite(td, {"text": text});
37641             var close = el.getElementsByTagName("div")[0];
37642             var inner = el.getElementsByTagName("em")[0];
37643             return {"el": el, "close": close, "inner": inner};
37644         } else {
37645         */
37646         // not sure what this is..
37647 //            if(!this.tabTpl){
37648                 //this.tabTpl = new Roo.Template(
37649                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37650                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37651                 //);
37652 //                this.tabTpl = new Roo.Template(
37653 //                   '<a href="#">' +
37654 //                   '<span unselectable="on"' +
37655 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37656 //                            ' >{text}</span></a>'
37657 //                );
37658 //                
37659 //            }
37660
37661
37662             var template = tpl || this.tabTpl || false;
37663             
37664             if(!template){
37665                 
37666                 template = new Roo.Template(
37667                    '<a href="#">' +
37668                    '<span unselectable="on"' +
37669                             (this.disableTooltips ? '' : ' title="{text}"') +
37670                             ' >{text}</span></a>'
37671                 );
37672             }
37673             
37674             switch (typeof(template)) {
37675                 case 'object' :
37676                     break;
37677                 case 'string' :
37678                     template = new Roo.Template(template);
37679                     break;
37680                 default :
37681                     break;
37682             }
37683             
37684             var el = template.overwrite(td, {"text": text});
37685             
37686             var inner = el.getElementsByTagName("span")[0];
37687             
37688             return {"el": el, "inner": inner};
37689             
37690     }
37691         
37692     
37693 });
37694
37695 /**
37696  * @class Roo.TabPanelItem
37697  * @extends Roo.util.Observable
37698  * Represents an individual item (tab plus body) in a TabPanel.
37699  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37700  * @param {String} id The id of this TabPanelItem
37701  * @param {String} text The text for the tab of this TabPanelItem
37702  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37703  */
37704 Roo.bootstrap.panel.TabItem = function(config){
37705     /**
37706      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37707      * @type Roo.TabPanel
37708      */
37709     this.tabPanel = config.panel;
37710     /**
37711      * The id for this TabPanelItem
37712      * @type String
37713      */
37714     this.id = config.id;
37715     /** @private */
37716     this.disabled = false;
37717     /** @private */
37718     this.text = config.text;
37719     /** @private */
37720     this.loaded = false;
37721     this.closable = config.closable;
37722
37723     /**
37724      * The body element for this TabPanelItem.
37725      * @type Roo.Element
37726      */
37727     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37728     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37729     this.bodyEl.setStyle("display", "block");
37730     this.bodyEl.setStyle("zoom", "1");
37731     //this.hideAction();
37732
37733     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37734     /** @private */
37735     this.el = Roo.get(els.el);
37736     this.inner = Roo.get(els.inner, true);
37737     this.textEl = Roo.get(this.el.dom.firstChild, true);
37738     this.pnode = Roo.get(els.el.parentNode, true);
37739 //    this.el.on("mousedown", this.onTabMouseDown, this);
37740     this.el.on("click", this.onTabClick, this);
37741     /** @private */
37742     if(config.closable){
37743         var c = Roo.get(els.close, true);
37744         c.dom.title = this.closeText;
37745         c.addClassOnOver("close-over");
37746         c.on("click", this.closeClick, this);
37747      }
37748
37749     this.addEvents({
37750          /**
37751          * @event activate
37752          * Fires when this tab becomes the active tab.
37753          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37754          * @param {Roo.TabPanelItem} this
37755          */
37756         "activate": true,
37757         /**
37758          * @event beforeclose
37759          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37760          * @param {Roo.TabPanelItem} this
37761          * @param {Object} e Set cancel to true on this object to cancel the close.
37762          */
37763         "beforeclose": true,
37764         /**
37765          * @event close
37766          * Fires when this tab is closed.
37767          * @param {Roo.TabPanelItem} this
37768          */
37769          "close": true,
37770         /**
37771          * @event deactivate
37772          * Fires when this tab is no longer the active tab.
37773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37774          * @param {Roo.TabPanelItem} this
37775          */
37776          "deactivate" : true
37777     });
37778     this.hidden = false;
37779
37780     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37781 };
37782
37783 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37784            {
37785     purgeListeners : function(){
37786        Roo.util.Observable.prototype.purgeListeners.call(this);
37787        this.el.removeAllListeners();
37788     },
37789     /**
37790      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37791      */
37792     show : function(){
37793         this.pnode.addClass("active");
37794         this.showAction();
37795         if(Roo.isOpera){
37796             this.tabPanel.stripWrap.repaint();
37797         }
37798         this.fireEvent("activate", this.tabPanel, this);
37799     },
37800
37801     /**
37802      * Returns true if this tab is the active tab.
37803      * @return {Boolean}
37804      */
37805     isActive : function(){
37806         return this.tabPanel.getActiveTab() == this;
37807     },
37808
37809     /**
37810      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37811      */
37812     hide : function(){
37813         this.pnode.removeClass("active");
37814         this.hideAction();
37815         this.fireEvent("deactivate", this.tabPanel, this);
37816     },
37817
37818     hideAction : function(){
37819         this.bodyEl.hide();
37820         this.bodyEl.setStyle("position", "absolute");
37821         this.bodyEl.setLeft("-20000px");
37822         this.bodyEl.setTop("-20000px");
37823     },
37824
37825     showAction : function(){
37826         this.bodyEl.setStyle("position", "relative");
37827         this.bodyEl.setTop("");
37828         this.bodyEl.setLeft("");
37829         this.bodyEl.show();
37830     },
37831
37832     /**
37833      * Set the tooltip for the tab.
37834      * @param {String} tooltip The tab's tooltip
37835      */
37836     setTooltip : function(text){
37837         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37838             this.textEl.dom.qtip = text;
37839             this.textEl.dom.removeAttribute('title');
37840         }else{
37841             this.textEl.dom.title = text;
37842         }
37843     },
37844
37845     onTabClick : function(e){
37846         e.preventDefault();
37847         this.tabPanel.activate(this.id);
37848     },
37849
37850     onTabMouseDown : function(e){
37851         e.preventDefault();
37852         this.tabPanel.activate(this.id);
37853     },
37854 /*
37855     getWidth : function(){
37856         return this.inner.getWidth();
37857     },
37858
37859     setWidth : function(width){
37860         var iwidth = width - this.pnode.getPadding("lr");
37861         this.inner.setWidth(iwidth);
37862         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37863         this.pnode.setWidth(width);
37864     },
37865 */
37866     /**
37867      * Show or hide the tab
37868      * @param {Boolean} hidden True to hide or false to show.
37869      */
37870     setHidden : function(hidden){
37871         this.hidden = hidden;
37872         this.pnode.setStyle("display", hidden ? "none" : "");
37873     },
37874
37875     /**
37876      * Returns true if this tab is "hidden"
37877      * @return {Boolean}
37878      */
37879     isHidden : function(){
37880         return this.hidden;
37881     },
37882
37883     /**
37884      * Returns the text for this tab
37885      * @return {String}
37886      */
37887     getText : function(){
37888         return this.text;
37889     },
37890     /*
37891     autoSize : function(){
37892         //this.el.beginMeasure();
37893         this.textEl.setWidth(1);
37894         /*
37895          *  #2804 [new] Tabs in Roojs
37896          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37897          */
37898         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37899         //this.el.endMeasure();
37900     //},
37901
37902     /**
37903      * Sets the text for the tab (Note: this also sets the tooltip text)
37904      * @param {String} text The tab's text and tooltip
37905      */
37906     setText : function(text){
37907         this.text = text;
37908         this.textEl.update(text);
37909         this.setTooltip(text);
37910         //if(!this.tabPanel.resizeTabs){
37911         //    this.autoSize();
37912         //}
37913     },
37914     /**
37915      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37916      */
37917     activate : function(){
37918         this.tabPanel.activate(this.id);
37919     },
37920
37921     /**
37922      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37923      */
37924     disable : function(){
37925         if(this.tabPanel.active != this){
37926             this.disabled = true;
37927             this.pnode.addClass("disabled");
37928         }
37929     },
37930
37931     /**
37932      * Enables this TabPanelItem if it was previously disabled.
37933      */
37934     enable : function(){
37935         this.disabled = false;
37936         this.pnode.removeClass("disabled");
37937     },
37938
37939     /**
37940      * Sets the content for this TabPanelItem.
37941      * @param {String} content The content
37942      * @param {Boolean} loadScripts true to look for and load scripts
37943      */
37944     setContent : function(content, loadScripts){
37945         this.bodyEl.update(content, loadScripts);
37946     },
37947
37948     /**
37949      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37950      * @return {Roo.UpdateManager} The UpdateManager
37951      */
37952     getUpdateManager : function(){
37953         return this.bodyEl.getUpdateManager();
37954     },
37955
37956     /**
37957      * Set a URL to be used to load the content for this TabPanelItem.
37958      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37959      * @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)
37960      * @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)
37961      * @return {Roo.UpdateManager} The UpdateManager
37962      */
37963     setUrl : function(url, params, loadOnce){
37964         if(this.refreshDelegate){
37965             this.un('activate', this.refreshDelegate);
37966         }
37967         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37968         this.on("activate", this.refreshDelegate);
37969         return this.bodyEl.getUpdateManager();
37970     },
37971
37972     /** @private */
37973     _handleRefresh : function(url, params, loadOnce){
37974         if(!loadOnce || !this.loaded){
37975             var updater = this.bodyEl.getUpdateManager();
37976             updater.update(url, params, this._setLoaded.createDelegate(this));
37977         }
37978     },
37979
37980     /**
37981      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37982      *   Will fail silently if the setUrl method has not been called.
37983      *   This does not activate the panel, just updates its content.
37984      */
37985     refresh : function(){
37986         if(this.refreshDelegate){
37987            this.loaded = false;
37988            this.refreshDelegate();
37989         }
37990     },
37991
37992     /** @private */
37993     _setLoaded : function(){
37994         this.loaded = true;
37995     },
37996
37997     /** @private */
37998     closeClick : function(e){
37999         var o = {};
38000         e.stopEvent();
38001         this.fireEvent("beforeclose", this, o);
38002         if(o.cancel !== true){
38003             this.tabPanel.removeTab(this.id);
38004         }
38005     },
38006     /**
38007      * The text displayed in the tooltip for the close icon.
38008      * @type String
38009      */
38010     closeText : "Close this tab"
38011 });
38012 /**
38013 *    This script refer to:
38014 *    Title: International Telephone Input
38015 *    Author: Jack O'Connor
38016 *    Code version:  v12.1.12
38017 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38018 **/
38019
38020 Roo.bootstrap.PhoneInputData = function() {
38021     var d = [
38022       [
38023         "Afghanistan (‫افغانستان‬‎)",
38024         "af",
38025         "93"
38026       ],
38027       [
38028         "Albania (Shqipëri)",
38029         "al",
38030         "355"
38031       ],
38032       [
38033         "Algeria (‫الجزائر‬‎)",
38034         "dz",
38035         "213"
38036       ],
38037       [
38038         "American Samoa",
38039         "as",
38040         "1684"
38041       ],
38042       [
38043         "Andorra",
38044         "ad",
38045         "376"
38046       ],
38047       [
38048         "Angola",
38049         "ao",
38050         "244"
38051       ],
38052       [
38053         "Anguilla",
38054         "ai",
38055         "1264"
38056       ],
38057       [
38058         "Antigua and Barbuda",
38059         "ag",
38060         "1268"
38061       ],
38062       [
38063         "Argentina",
38064         "ar",
38065         "54"
38066       ],
38067       [
38068         "Armenia (Հայաստան)",
38069         "am",
38070         "374"
38071       ],
38072       [
38073         "Aruba",
38074         "aw",
38075         "297"
38076       ],
38077       [
38078         "Australia",
38079         "au",
38080         "61",
38081         0
38082       ],
38083       [
38084         "Austria (Österreich)",
38085         "at",
38086         "43"
38087       ],
38088       [
38089         "Azerbaijan (Azərbaycan)",
38090         "az",
38091         "994"
38092       ],
38093       [
38094         "Bahamas",
38095         "bs",
38096         "1242"
38097       ],
38098       [
38099         "Bahrain (‫البحرين‬‎)",
38100         "bh",
38101         "973"
38102       ],
38103       [
38104         "Bangladesh (বাংলাদেশ)",
38105         "bd",
38106         "880"
38107       ],
38108       [
38109         "Barbados",
38110         "bb",
38111         "1246"
38112       ],
38113       [
38114         "Belarus (Беларусь)",
38115         "by",
38116         "375"
38117       ],
38118       [
38119         "Belgium (België)",
38120         "be",
38121         "32"
38122       ],
38123       [
38124         "Belize",
38125         "bz",
38126         "501"
38127       ],
38128       [
38129         "Benin (Bénin)",
38130         "bj",
38131         "229"
38132       ],
38133       [
38134         "Bermuda",
38135         "bm",
38136         "1441"
38137       ],
38138       [
38139         "Bhutan (འབྲུག)",
38140         "bt",
38141         "975"
38142       ],
38143       [
38144         "Bolivia",
38145         "bo",
38146         "591"
38147       ],
38148       [
38149         "Bosnia and Herzegovina (Босна и Херцеговина)",
38150         "ba",
38151         "387"
38152       ],
38153       [
38154         "Botswana",
38155         "bw",
38156         "267"
38157       ],
38158       [
38159         "Brazil (Brasil)",
38160         "br",
38161         "55"
38162       ],
38163       [
38164         "British Indian Ocean Territory",
38165         "io",
38166         "246"
38167       ],
38168       [
38169         "British Virgin Islands",
38170         "vg",
38171         "1284"
38172       ],
38173       [
38174         "Brunei",
38175         "bn",
38176         "673"
38177       ],
38178       [
38179         "Bulgaria (България)",
38180         "bg",
38181         "359"
38182       ],
38183       [
38184         "Burkina Faso",
38185         "bf",
38186         "226"
38187       ],
38188       [
38189         "Burundi (Uburundi)",
38190         "bi",
38191         "257"
38192       ],
38193       [
38194         "Cambodia (កម្ពុជា)",
38195         "kh",
38196         "855"
38197       ],
38198       [
38199         "Cameroon (Cameroun)",
38200         "cm",
38201         "237"
38202       ],
38203       [
38204         "Canada",
38205         "ca",
38206         "1",
38207         1,
38208         ["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"]
38209       ],
38210       [
38211         "Cape Verde (Kabu Verdi)",
38212         "cv",
38213         "238"
38214       ],
38215       [
38216         "Caribbean Netherlands",
38217         "bq",
38218         "599",
38219         1
38220       ],
38221       [
38222         "Cayman Islands",
38223         "ky",
38224         "1345"
38225       ],
38226       [
38227         "Central African Republic (République centrafricaine)",
38228         "cf",
38229         "236"
38230       ],
38231       [
38232         "Chad (Tchad)",
38233         "td",
38234         "235"
38235       ],
38236       [
38237         "Chile",
38238         "cl",
38239         "56"
38240       ],
38241       [
38242         "China (中国)",
38243         "cn",
38244         "86"
38245       ],
38246       [
38247         "Christmas Island",
38248         "cx",
38249         "61",
38250         2
38251       ],
38252       [
38253         "Cocos (Keeling) Islands",
38254         "cc",
38255         "61",
38256         1
38257       ],
38258       [
38259         "Colombia",
38260         "co",
38261         "57"
38262       ],
38263       [
38264         "Comoros (‫جزر القمر‬‎)",
38265         "km",
38266         "269"
38267       ],
38268       [
38269         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38270         "cd",
38271         "243"
38272       ],
38273       [
38274         "Congo (Republic) (Congo-Brazzaville)",
38275         "cg",
38276         "242"
38277       ],
38278       [
38279         "Cook Islands",
38280         "ck",
38281         "682"
38282       ],
38283       [
38284         "Costa Rica",
38285         "cr",
38286         "506"
38287       ],
38288       [
38289         "Côte d’Ivoire",
38290         "ci",
38291         "225"
38292       ],
38293       [
38294         "Croatia (Hrvatska)",
38295         "hr",
38296         "385"
38297       ],
38298       [
38299         "Cuba",
38300         "cu",
38301         "53"
38302       ],
38303       [
38304         "Curaçao",
38305         "cw",
38306         "599",
38307         0
38308       ],
38309       [
38310         "Cyprus (Κύπρος)",
38311         "cy",
38312         "357"
38313       ],
38314       [
38315         "Czech Republic (Česká republika)",
38316         "cz",
38317         "420"
38318       ],
38319       [
38320         "Denmark (Danmark)",
38321         "dk",
38322         "45"
38323       ],
38324       [
38325         "Djibouti",
38326         "dj",
38327         "253"
38328       ],
38329       [
38330         "Dominica",
38331         "dm",
38332         "1767"
38333       ],
38334       [
38335         "Dominican Republic (República Dominicana)",
38336         "do",
38337         "1",
38338         2,
38339         ["809", "829", "849"]
38340       ],
38341       [
38342         "Ecuador",
38343         "ec",
38344         "593"
38345       ],
38346       [
38347         "Egypt (‫مصر‬‎)",
38348         "eg",
38349         "20"
38350       ],
38351       [
38352         "El Salvador",
38353         "sv",
38354         "503"
38355       ],
38356       [
38357         "Equatorial Guinea (Guinea Ecuatorial)",
38358         "gq",
38359         "240"
38360       ],
38361       [
38362         "Eritrea",
38363         "er",
38364         "291"
38365       ],
38366       [
38367         "Estonia (Eesti)",
38368         "ee",
38369         "372"
38370       ],
38371       [
38372         "Ethiopia",
38373         "et",
38374         "251"
38375       ],
38376       [
38377         "Falkland Islands (Islas Malvinas)",
38378         "fk",
38379         "500"
38380       ],
38381       [
38382         "Faroe Islands (Føroyar)",
38383         "fo",
38384         "298"
38385       ],
38386       [
38387         "Fiji",
38388         "fj",
38389         "679"
38390       ],
38391       [
38392         "Finland (Suomi)",
38393         "fi",
38394         "358",
38395         0
38396       ],
38397       [
38398         "France",
38399         "fr",
38400         "33"
38401       ],
38402       [
38403         "French Guiana (Guyane française)",
38404         "gf",
38405         "594"
38406       ],
38407       [
38408         "French Polynesia (Polynésie française)",
38409         "pf",
38410         "689"
38411       ],
38412       [
38413         "Gabon",
38414         "ga",
38415         "241"
38416       ],
38417       [
38418         "Gambia",
38419         "gm",
38420         "220"
38421       ],
38422       [
38423         "Georgia (საქართველო)",
38424         "ge",
38425         "995"
38426       ],
38427       [
38428         "Germany (Deutschland)",
38429         "de",
38430         "49"
38431       ],
38432       [
38433         "Ghana (Gaana)",
38434         "gh",
38435         "233"
38436       ],
38437       [
38438         "Gibraltar",
38439         "gi",
38440         "350"
38441       ],
38442       [
38443         "Greece (Ελλάδα)",
38444         "gr",
38445         "30"
38446       ],
38447       [
38448         "Greenland (Kalaallit Nunaat)",
38449         "gl",
38450         "299"
38451       ],
38452       [
38453         "Grenada",
38454         "gd",
38455         "1473"
38456       ],
38457       [
38458         "Guadeloupe",
38459         "gp",
38460         "590",
38461         0
38462       ],
38463       [
38464         "Guam",
38465         "gu",
38466         "1671"
38467       ],
38468       [
38469         "Guatemala",
38470         "gt",
38471         "502"
38472       ],
38473       [
38474         "Guernsey",
38475         "gg",
38476         "44",
38477         1
38478       ],
38479       [
38480         "Guinea (Guinée)",
38481         "gn",
38482         "224"
38483       ],
38484       [
38485         "Guinea-Bissau (Guiné Bissau)",
38486         "gw",
38487         "245"
38488       ],
38489       [
38490         "Guyana",
38491         "gy",
38492         "592"
38493       ],
38494       [
38495         "Haiti",
38496         "ht",
38497         "509"
38498       ],
38499       [
38500         "Honduras",
38501         "hn",
38502         "504"
38503       ],
38504       [
38505         "Hong Kong (香港)",
38506         "hk",
38507         "852"
38508       ],
38509       [
38510         "Hungary (Magyarország)",
38511         "hu",
38512         "36"
38513       ],
38514       [
38515         "Iceland (Ísland)",
38516         "is",
38517         "354"
38518       ],
38519       [
38520         "India (भारत)",
38521         "in",
38522         "91"
38523       ],
38524       [
38525         "Indonesia",
38526         "id",
38527         "62"
38528       ],
38529       [
38530         "Iran (‫ایران‬‎)",
38531         "ir",
38532         "98"
38533       ],
38534       [
38535         "Iraq (‫العراق‬‎)",
38536         "iq",
38537         "964"
38538       ],
38539       [
38540         "Ireland",
38541         "ie",
38542         "353"
38543       ],
38544       [
38545         "Isle of Man",
38546         "im",
38547         "44",
38548         2
38549       ],
38550       [
38551         "Israel (‫ישראל‬‎)",
38552         "il",
38553         "972"
38554       ],
38555       [
38556         "Italy (Italia)",
38557         "it",
38558         "39",
38559         0
38560       ],
38561       [
38562         "Jamaica",
38563         "jm",
38564         "1876"
38565       ],
38566       [
38567         "Japan (日本)",
38568         "jp",
38569         "81"
38570       ],
38571       [
38572         "Jersey",
38573         "je",
38574         "44",
38575         3
38576       ],
38577       [
38578         "Jordan (‫الأردن‬‎)",
38579         "jo",
38580         "962"
38581       ],
38582       [
38583         "Kazakhstan (Казахстан)",
38584         "kz",
38585         "7",
38586         1
38587       ],
38588       [
38589         "Kenya",
38590         "ke",
38591         "254"
38592       ],
38593       [
38594         "Kiribati",
38595         "ki",
38596         "686"
38597       ],
38598       [
38599         "Kosovo",
38600         "xk",
38601         "383"
38602       ],
38603       [
38604         "Kuwait (‫الكويت‬‎)",
38605         "kw",
38606         "965"
38607       ],
38608       [
38609         "Kyrgyzstan (Кыргызстан)",
38610         "kg",
38611         "996"
38612       ],
38613       [
38614         "Laos (ລາວ)",
38615         "la",
38616         "856"
38617       ],
38618       [
38619         "Latvia (Latvija)",
38620         "lv",
38621         "371"
38622       ],
38623       [
38624         "Lebanon (‫لبنان‬‎)",
38625         "lb",
38626         "961"
38627       ],
38628       [
38629         "Lesotho",
38630         "ls",
38631         "266"
38632       ],
38633       [
38634         "Liberia",
38635         "lr",
38636         "231"
38637       ],
38638       [
38639         "Libya (‫ليبيا‬‎)",
38640         "ly",
38641         "218"
38642       ],
38643       [
38644         "Liechtenstein",
38645         "li",
38646         "423"
38647       ],
38648       [
38649         "Lithuania (Lietuva)",
38650         "lt",
38651         "370"
38652       ],
38653       [
38654         "Luxembourg",
38655         "lu",
38656         "352"
38657       ],
38658       [
38659         "Macau (澳門)",
38660         "mo",
38661         "853"
38662       ],
38663       [
38664         "Macedonia (FYROM) (Македонија)",
38665         "mk",
38666         "389"
38667       ],
38668       [
38669         "Madagascar (Madagasikara)",
38670         "mg",
38671         "261"
38672       ],
38673       [
38674         "Malawi",
38675         "mw",
38676         "265"
38677       ],
38678       [
38679         "Malaysia",
38680         "my",
38681         "60"
38682       ],
38683       [
38684         "Maldives",
38685         "mv",
38686         "960"
38687       ],
38688       [
38689         "Mali",
38690         "ml",
38691         "223"
38692       ],
38693       [
38694         "Malta",
38695         "mt",
38696         "356"
38697       ],
38698       [
38699         "Marshall Islands",
38700         "mh",
38701         "692"
38702       ],
38703       [
38704         "Martinique",
38705         "mq",
38706         "596"
38707       ],
38708       [
38709         "Mauritania (‫موريتانيا‬‎)",
38710         "mr",
38711         "222"
38712       ],
38713       [
38714         "Mauritius (Moris)",
38715         "mu",
38716         "230"
38717       ],
38718       [
38719         "Mayotte",
38720         "yt",
38721         "262",
38722         1
38723       ],
38724       [
38725         "Mexico (México)",
38726         "mx",
38727         "52"
38728       ],
38729       [
38730         "Micronesia",
38731         "fm",
38732         "691"
38733       ],
38734       [
38735         "Moldova (Republica Moldova)",
38736         "md",
38737         "373"
38738       ],
38739       [
38740         "Monaco",
38741         "mc",
38742         "377"
38743       ],
38744       [
38745         "Mongolia (Монгол)",
38746         "mn",
38747         "976"
38748       ],
38749       [
38750         "Montenegro (Crna Gora)",
38751         "me",
38752         "382"
38753       ],
38754       [
38755         "Montserrat",
38756         "ms",
38757         "1664"
38758       ],
38759       [
38760         "Morocco (‫المغرب‬‎)",
38761         "ma",
38762         "212",
38763         0
38764       ],
38765       [
38766         "Mozambique (Moçambique)",
38767         "mz",
38768         "258"
38769       ],
38770       [
38771         "Myanmar (Burma) (မြန်မာ)",
38772         "mm",
38773         "95"
38774       ],
38775       [
38776         "Namibia (Namibië)",
38777         "na",
38778         "264"
38779       ],
38780       [
38781         "Nauru",
38782         "nr",
38783         "674"
38784       ],
38785       [
38786         "Nepal (नेपाल)",
38787         "np",
38788         "977"
38789       ],
38790       [
38791         "Netherlands (Nederland)",
38792         "nl",
38793         "31"
38794       ],
38795       [
38796         "New Caledonia (Nouvelle-Calédonie)",
38797         "nc",
38798         "687"
38799       ],
38800       [
38801         "New Zealand",
38802         "nz",
38803         "64"
38804       ],
38805       [
38806         "Nicaragua",
38807         "ni",
38808         "505"
38809       ],
38810       [
38811         "Niger (Nijar)",
38812         "ne",
38813         "227"
38814       ],
38815       [
38816         "Nigeria",
38817         "ng",
38818         "234"
38819       ],
38820       [
38821         "Niue",
38822         "nu",
38823         "683"
38824       ],
38825       [
38826         "Norfolk Island",
38827         "nf",
38828         "672"
38829       ],
38830       [
38831         "North Korea (조선 민주주의 인민 공화국)",
38832         "kp",
38833         "850"
38834       ],
38835       [
38836         "Northern Mariana Islands",
38837         "mp",
38838         "1670"
38839       ],
38840       [
38841         "Norway (Norge)",
38842         "no",
38843         "47",
38844         0
38845       ],
38846       [
38847         "Oman (‫عُمان‬‎)",
38848         "om",
38849         "968"
38850       ],
38851       [
38852         "Pakistan (‫پاکستان‬‎)",
38853         "pk",
38854         "92"
38855       ],
38856       [
38857         "Palau",
38858         "pw",
38859         "680"
38860       ],
38861       [
38862         "Palestine (‫فلسطين‬‎)",
38863         "ps",
38864         "970"
38865       ],
38866       [
38867         "Panama (Panamá)",
38868         "pa",
38869         "507"
38870       ],
38871       [
38872         "Papua New Guinea",
38873         "pg",
38874         "675"
38875       ],
38876       [
38877         "Paraguay",
38878         "py",
38879         "595"
38880       ],
38881       [
38882         "Peru (Perú)",
38883         "pe",
38884         "51"
38885       ],
38886       [
38887         "Philippines",
38888         "ph",
38889         "63"
38890       ],
38891       [
38892         "Poland (Polska)",
38893         "pl",
38894         "48"
38895       ],
38896       [
38897         "Portugal",
38898         "pt",
38899         "351"
38900       ],
38901       [
38902         "Puerto Rico",
38903         "pr",
38904         "1",
38905         3,
38906         ["787", "939"]
38907       ],
38908       [
38909         "Qatar (‫قطر‬‎)",
38910         "qa",
38911         "974"
38912       ],
38913       [
38914         "Réunion (La Réunion)",
38915         "re",
38916         "262",
38917         0
38918       ],
38919       [
38920         "Romania (România)",
38921         "ro",
38922         "40"
38923       ],
38924       [
38925         "Russia (Россия)",
38926         "ru",
38927         "7",
38928         0
38929       ],
38930       [
38931         "Rwanda",
38932         "rw",
38933         "250"
38934       ],
38935       [
38936         "Saint Barthélemy",
38937         "bl",
38938         "590",
38939         1
38940       ],
38941       [
38942         "Saint Helena",
38943         "sh",
38944         "290"
38945       ],
38946       [
38947         "Saint Kitts and Nevis",
38948         "kn",
38949         "1869"
38950       ],
38951       [
38952         "Saint Lucia",
38953         "lc",
38954         "1758"
38955       ],
38956       [
38957         "Saint Martin (Saint-Martin (partie française))",
38958         "mf",
38959         "590",
38960         2
38961       ],
38962       [
38963         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38964         "pm",
38965         "508"
38966       ],
38967       [
38968         "Saint Vincent and the Grenadines",
38969         "vc",
38970         "1784"
38971       ],
38972       [
38973         "Samoa",
38974         "ws",
38975         "685"
38976       ],
38977       [
38978         "San Marino",
38979         "sm",
38980         "378"
38981       ],
38982       [
38983         "São Tomé and Príncipe (São Tomé e Príncipe)",
38984         "st",
38985         "239"
38986       ],
38987       [
38988         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38989         "sa",
38990         "966"
38991       ],
38992       [
38993         "Senegal (Sénégal)",
38994         "sn",
38995         "221"
38996       ],
38997       [
38998         "Serbia (Србија)",
38999         "rs",
39000         "381"
39001       ],
39002       [
39003         "Seychelles",
39004         "sc",
39005         "248"
39006       ],
39007       [
39008         "Sierra Leone",
39009         "sl",
39010         "232"
39011       ],
39012       [
39013         "Singapore",
39014         "sg",
39015         "65"
39016       ],
39017       [
39018         "Sint Maarten",
39019         "sx",
39020         "1721"
39021       ],
39022       [
39023         "Slovakia (Slovensko)",
39024         "sk",
39025         "421"
39026       ],
39027       [
39028         "Slovenia (Slovenija)",
39029         "si",
39030         "386"
39031       ],
39032       [
39033         "Solomon Islands",
39034         "sb",
39035         "677"
39036       ],
39037       [
39038         "Somalia (Soomaaliya)",
39039         "so",
39040         "252"
39041       ],
39042       [
39043         "South Africa",
39044         "za",
39045         "27"
39046       ],
39047       [
39048         "South Korea (대한민국)",
39049         "kr",
39050         "82"
39051       ],
39052       [
39053         "South Sudan (‫جنوب السودان‬‎)",
39054         "ss",
39055         "211"
39056       ],
39057       [
39058         "Spain (España)",
39059         "es",
39060         "34"
39061       ],
39062       [
39063         "Sri Lanka (ශ්‍රී ලංකාව)",
39064         "lk",
39065         "94"
39066       ],
39067       [
39068         "Sudan (‫السودان‬‎)",
39069         "sd",
39070         "249"
39071       ],
39072       [
39073         "Suriname",
39074         "sr",
39075         "597"
39076       ],
39077       [
39078         "Svalbard and Jan Mayen",
39079         "sj",
39080         "47",
39081         1
39082       ],
39083       [
39084         "Swaziland",
39085         "sz",
39086         "268"
39087       ],
39088       [
39089         "Sweden (Sverige)",
39090         "se",
39091         "46"
39092       ],
39093       [
39094         "Switzerland (Schweiz)",
39095         "ch",
39096         "41"
39097       ],
39098       [
39099         "Syria (‫سوريا‬‎)",
39100         "sy",
39101         "963"
39102       ],
39103       [
39104         "Taiwan (台灣)",
39105         "tw",
39106         "886"
39107       ],
39108       [
39109         "Tajikistan",
39110         "tj",
39111         "992"
39112       ],
39113       [
39114         "Tanzania",
39115         "tz",
39116         "255"
39117       ],
39118       [
39119         "Thailand (ไทย)",
39120         "th",
39121         "66"
39122       ],
39123       [
39124         "Timor-Leste",
39125         "tl",
39126         "670"
39127       ],
39128       [
39129         "Togo",
39130         "tg",
39131         "228"
39132       ],
39133       [
39134         "Tokelau",
39135         "tk",
39136         "690"
39137       ],
39138       [
39139         "Tonga",
39140         "to",
39141         "676"
39142       ],
39143       [
39144         "Trinidad and Tobago",
39145         "tt",
39146         "1868"
39147       ],
39148       [
39149         "Tunisia (‫تونس‬‎)",
39150         "tn",
39151         "216"
39152       ],
39153       [
39154         "Turkey (Türkiye)",
39155         "tr",
39156         "90"
39157       ],
39158       [
39159         "Turkmenistan",
39160         "tm",
39161         "993"
39162       ],
39163       [
39164         "Turks and Caicos Islands",
39165         "tc",
39166         "1649"
39167       ],
39168       [
39169         "Tuvalu",
39170         "tv",
39171         "688"
39172       ],
39173       [
39174         "U.S. Virgin Islands",
39175         "vi",
39176         "1340"
39177       ],
39178       [
39179         "Uganda",
39180         "ug",
39181         "256"
39182       ],
39183       [
39184         "Ukraine (Україна)",
39185         "ua",
39186         "380"
39187       ],
39188       [
39189         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39190         "ae",
39191         "971"
39192       ],
39193       [
39194         "United Kingdom",
39195         "gb",
39196         "44",
39197         0
39198       ],
39199       [
39200         "United States",
39201         "us",
39202         "1",
39203         0
39204       ],
39205       [
39206         "Uruguay",
39207         "uy",
39208         "598"
39209       ],
39210       [
39211         "Uzbekistan (Oʻzbekiston)",
39212         "uz",
39213         "998"
39214       ],
39215       [
39216         "Vanuatu",
39217         "vu",
39218         "678"
39219       ],
39220       [
39221         "Vatican City (Città del Vaticano)",
39222         "va",
39223         "39",
39224         1
39225       ],
39226       [
39227         "Venezuela",
39228         "ve",
39229         "58"
39230       ],
39231       [
39232         "Vietnam (Việt Nam)",
39233         "vn",
39234         "84"
39235       ],
39236       [
39237         "Wallis and Futuna (Wallis-et-Futuna)",
39238         "wf",
39239         "681"
39240       ],
39241       [
39242         "Western Sahara (‫الصحراء الغربية‬‎)",
39243         "eh",
39244         "212",
39245         1
39246       ],
39247       [
39248         "Yemen (‫اليمن‬‎)",
39249         "ye",
39250         "967"
39251       ],
39252       [
39253         "Zambia",
39254         "zm",
39255         "260"
39256       ],
39257       [
39258         "Zimbabwe",
39259         "zw",
39260         "263"
39261       ],
39262       [
39263         "Åland Islands",
39264         "ax",
39265         "358",
39266         1
39267       ]
39268   ];
39269   
39270   return d;
39271 }/**
39272 *    This script refer to:
39273 *    Title: International Telephone Input
39274 *    Author: Jack O'Connor
39275 *    Code version:  v12.1.12
39276 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39277 **/
39278
39279 /**
39280  * @class Roo.bootstrap.PhoneInput
39281  * @extends Roo.bootstrap.TriggerField
39282  * An input with International dial-code selection
39283  
39284  * @cfg {String} defaultDialCode default '+852'
39285  * @cfg {Array} preferedCountries default []
39286   
39287  * @constructor
39288  * Create a new PhoneInput.
39289  * @param {Object} config Configuration options
39290  */
39291
39292 Roo.bootstrap.PhoneInput = function(config) {
39293     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39294 };
39295
39296 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39297         
39298         listWidth: undefined,
39299         
39300         selectedClass: 'active',
39301         
39302         invalidClass : "has-warning",
39303         
39304         validClass: 'has-success',
39305         
39306         allowed: '0123456789',
39307         
39308         /**
39309          * @cfg {String} defaultDialCode The default dial code when initializing the input
39310          */
39311         defaultDialCode: '+852',
39312         
39313         /**
39314          * @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
39315          */
39316         preferedCountries: false,
39317         
39318         getAutoCreate : function()
39319         {
39320             var data = Roo.bootstrap.PhoneInputData();
39321             var align = this.labelAlign || this.parentLabelAlign();
39322             var id = Roo.id();
39323             
39324             this.allCountries = [];
39325             this.dialCodeMapping = [];
39326             
39327             for (var i = 0; i < data.length; i++) {
39328               var c = data[i];
39329               this.allCountries[i] = {
39330                 name: c[0],
39331                 iso2: c[1],
39332                 dialCode: c[2],
39333                 priority: c[3] || 0,
39334                 areaCodes: c[4] || null
39335               };
39336               this.dialCodeMapping[c[2]] = {
39337                   name: c[0],
39338                   iso2: c[1],
39339                   priority: c[3] || 0,
39340                   areaCodes: c[4] || null
39341               };
39342             }
39343             
39344             var cfg = {
39345                 cls: 'form-group',
39346                 cn: []
39347             };
39348             
39349             var input =  {
39350                 tag: 'input',
39351                 id : id,
39352                 cls : 'form-control tel-input',
39353                 autocomplete: 'new-password'
39354             };
39355             
39356             var hiddenInput = {
39357                 tag: 'input',
39358                 type: 'hidden',
39359                 cls: 'hidden-tel-input'
39360             };
39361             
39362             if (this.name) {
39363                 hiddenInput.name = this.name;
39364             }
39365             
39366             if (this.disabled) {
39367                 input.disabled = true;
39368             }
39369             
39370             var flag_container = {
39371                 tag: 'div',
39372                 cls: 'flag-box',
39373                 cn: [
39374                     {
39375                         tag: 'div',
39376                         cls: 'flag'
39377                     },
39378                     {
39379                         tag: 'div',
39380                         cls: 'caret'
39381                     }
39382                 ]
39383             };
39384             
39385             var box = {
39386                 tag: 'div',
39387                 cls: this.hasFeedback ? 'has-feedback' : '',
39388                 cn: [
39389                     hiddenInput,
39390                     input,
39391                     {
39392                         tag: 'input',
39393                         cls: 'dial-code-holder',
39394                         disabled: true
39395                     }
39396                 ]
39397             };
39398             
39399             var container = {
39400                 cls: 'roo-select2-container input-group',
39401                 cn: [
39402                     flag_container,
39403                     box
39404                 ]
39405             };
39406             
39407             if (this.fieldLabel.length) {
39408                 var indicator = {
39409                     tag: 'i',
39410                     tooltip: 'This field is required'
39411                 };
39412                 
39413                 var label = {
39414                     tag: 'label',
39415                     'for':  id,
39416                     cls: 'control-label',
39417                     cn: []
39418                 };
39419                 
39420                 var label_text = {
39421                     tag: 'span',
39422                     html: this.fieldLabel
39423                 };
39424                 
39425                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39426                 label.cn = [
39427                     indicator,
39428                     label_text
39429                 ];
39430                 
39431                 if(this.indicatorpos == 'right') {
39432                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39433                     label.cn = [
39434                         label_text,
39435                         indicator
39436                     ];
39437                 }
39438                 
39439                 if(align == 'left') {
39440                     container = {
39441                         tag: 'div',
39442                         cn: [
39443                             container
39444                         ]
39445                     };
39446                     
39447                     if(this.labelWidth > 12){
39448                         label.style = "width: " + this.labelWidth + 'px';
39449                     }
39450                     if(this.labelWidth < 13 && this.labelmd == 0){
39451                         this.labelmd = this.labelWidth;
39452                     }
39453                     if(this.labellg > 0){
39454                         label.cls += ' col-lg-' + this.labellg;
39455                         input.cls += ' col-lg-' + (12 - this.labellg);
39456                     }
39457                     if(this.labelmd > 0){
39458                         label.cls += ' col-md-' + this.labelmd;
39459                         container.cls += ' col-md-' + (12 - this.labelmd);
39460                     }
39461                     if(this.labelsm > 0){
39462                         label.cls += ' col-sm-' + this.labelsm;
39463                         container.cls += ' col-sm-' + (12 - this.labelsm);
39464                     }
39465                     if(this.labelxs > 0){
39466                         label.cls += ' col-xs-' + this.labelxs;
39467                         container.cls += ' col-xs-' + (12 - this.labelxs);
39468                     }
39469                 }
39470             }
39471             
39472             cfg.cn = [
39473                 label,
39474                 container
39475             ];
39476             
39477             var settings = this;
39478             
39479             ['xs','sm','md','lg'].map(function(size){
39480                 if (settings[size]) {
39481                     cfg.cls += ' col-' + size + '-' + settings[size];
39482                 }
39483             });
39484             
39485             this.store = new Roo.data.Store({
39486                 proxy : new Roo.data.MemoryProxy({}),
39487                 reader : new Roo.data.JsonReader({
39488                     fields : [
39489                         {
39490                             'name' : 'name',
39491                             'type' : 'string'
39492                         },
39493                         {
39494                             'name' : 'iso2',
39495                             'type' : 'string'
39496                         },
39497                         {
39498                             'name' : 'dialCode',
39499                             'type' : 'string'
39500                         },
39501                         {
39502                             'name' : 'priority',
39503                             'type' : 'string'
39504                         },
39505                         {
39506                             'name' : 'areaCodes',
39507                             'type' : 'string'
39508                         }
39509                     ]
39510                 })
39511             });
39512             
39513             if(!this.preferedCountries) {
39514                 this.preferedCountries = [
39515                     'hk',
39516                     'gb',
39517                     'us'
39518                 ];
39519             }
39520             
39521             var p = this.preferedCountries.reverse();
39522             
39523             if(p) {
39524                 for (var i = 0; i < p.length; i++) {
39525                     for (var j = 0; j < this.allCountries.length; j++) {
39526                         if(this.allCountries[j].iso2 == p[i]) {
39527                             var t = this.allCountries[j];
39528                             this.allCountries.splice(j,1);
39529                             this.allCountries.unshift(t);
39530                         }
39531                     } 
39532                 }
39533             }
39534             
39535             this.store.proxy.data = {
39536                 success: true,
39537                 data: this.allCountries
39538             };
39539             
39540             return cfg;
39541         },
39542         
39543         initEvents : function()
39544         {
39545             this.createList();
39546             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39547             
39548             this.indicator = this.indicatorEl();
39549             this.flag = this.flagEl();
39550             this.dialCodeHolder = this.dialCodeHolderEl();
39551             
39552             this.trigger = this.el.select('div.flag-box',true).first();
39553             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39554             
39555             var _this = this;
39556             
39557             (function(){
39558                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39559                 _this.list.setWidth(lw);
39560             }).defer(100);
39561             
39562             this.list.on('mouseover', this.onViewOver, this);
39563             this.list.on('mousemove', this.onViewMove, this);
39564             this.inputEl().on("keyup", this.onKeyUp, this);
39565             
39566             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39567
39568             this.view = new Roo.View(this.list, this.tpl, {
39569                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39570             });
39571             
39572             this.view.on('click', this.onViewClick, this);
39573             this.setValue(this.defaultDialCode);
39574         },
39575         
39576         onTriggerClick : function(e)
39577         {
39578             Roo.log('trigger click');
39579             if(this.disabled){
39580                 return;
39581             }
39582             
39583             if(this.isExpanded()){
39584                 this.collapse();
39585                 this.hasFocus = false;
39586             }else {
39587                 this.store.load({});
39588                 this.hasFocus = true;
39589                 this.expand();
39590             }
39591         },
39592         
39593         isExpanded : function()
39594         {
39595             return this.list.isVisible();
39596         },
39597         
39598         collapse : function()
39599         {
39600             if(!this.isExpanded()){
39601                 return;
39602             }
39603             this.list.hide();
39604             Roo.get(document).un('mousedown', this.collapseIf, this);
39605             Roo.get(document).un('mousewheel', this.collapseIf, this);
39606             this.fireEvent('collapse', this);
39607             this.validate();
39608         },
39609         
39610         expand : function()
39611         {
39612             Roo.log('expand');
39613
39614             if(this.isExpanded() || !this.hasFocus){
39615                 return;
39616             }
39617             
39618             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39619             this.list.setWidth(lw);
39620             
39621             this.list.show();
39622             this.restrictHeight();
39623             
39624             Roo.get(document).on('mousedown', this.collapseIf, this);
39625             Roo.get(document).on('mousewheel', this.collapseIf, this);
39626             
39627             this.fireEvent('expand', this);
39628         },
39629         
39630         restrictHeight : function()
39631         {
39632             this.list.alignTo(this.inputEl(), this.listAlign);
39633             this.list.alignTo(this.inputEl(), this.listAlign);
39634         },
39635         
39636         onViewOver : function(e, t)
39637         {
39638             if(this.inKeyMode){
39639                 return;
39640             }
39641             var item = this.view.findItemFromChild(t);
39642             
39643             if(item){
39644                 var index = this.view.indexOf(item);
39645                 this.select(index, false);
39646             }
39647         },
39648
39649         // private
39650         onViewClick : function(view, doFocus, el, e)
39651         {
39652             var index = this.view.getSelectedIndexes()[0];
39653             
39654             var r = this.store.getAt(index);
39655             
39656             if(r){
39657                 this.onSelect(r, index);
39658             }
39659             if(doFocus !== false && !this.blockFocus){
39660                 this.inputEl().focus();
39661             }
39662         },
39663         
39664         onViewMove : function(e, t)
39665         {
39666             this.inKeyMode = false;
39667         },
39668         
39669         select : function(index, scrollIntoView)
39670         {
39671             this.selectedIndex = index;
39672             this.view.select(index);
39673             if(scrollIntoView !== false){
39674                 var el = this.view.getNode(index);
39675                 if(el){
39676                     this.list.scrollChildIntoView(el, false);
39677                 }
39678             }
39679         },
39680         
39681         createList : function()
39682         {
39683             this.list = Roo.get(document.body).createChild({
39684                 tag: 'ul',
39685                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39686                 style: 'display:none'
39687             });
39688             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39689         },
39690         
39691         collapseIf : function(e)
39692         {
39693             var in_combo  = e.within(this.el);
39694             var in_list =  e.within(this.list);
39695             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39696             
39697             if (in_combo || in_list || is_list) {
39698                 return;
39699             }
39700             this.collapse();
39701         },
39702         
39703         onSelect : function(record, index)
39704         {
39705             if(this.fireEvent('beforeselect', this, record, index) !== false){
39706                 
39707                 this.setFlagClass(record.data.iso2);
39708                 this.setDialCode(record.data.dialCode);
39709                 this.hasFocus = false;
39710                 this.collapse();
39711                 this.fireEvent('select', this, record, index);
39712             }
39713         },
39714         
39715         flagEl : function()
39716         {
39717             var flag = this.el.select('div.flag',true).first();
39718             if(!flag){
39719                 return false;
39720             }
39721             return flag;
39722         },
39723         
39724         dialCodeHolderEl : function()
39725         {
39726             var d = this.el.select('input.dial-code-holder',true).first();
39727             if(!d){
39728                 return false;
39729             }
39730             return d;
39731         },
39732         
39733         setDialCode : function(v)
39734         {
39735             this.dialCodeHolder.dom.value = '+'+v;
39736         },
39737         
39738         setFlagClass : function(n)
39739         {
39740             this.flag.dom.className = 'flag '+n;
39741         },
39742         
39743         getValue : function()
39744         {
39745             var v = this.inputEl().getValue();
39746             if(this.dialCodeHolder) {
39747                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39748             }
39749             return v;
39750         },
39751         
39752         setValue : function(v)
39753         {
39754             var d = this.getDialCode(v);
39755             
39756             //invalid dial code
39757             if(v.length == 0 || !d || d.length == 0) {
39758                 if(this.rendered){
39759                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39760                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39761                 }
39762                 return;
39763             }
39764             
39765             //valid dial code
39766             this.setFlagClass(this.dialCodeMapping[d].iso2);
39767             this.setDialCode(d);
39768             this.inputEl().dom.value = v.replace('+'+d,'');
39769             this.hiddenEl().dom.value = this.getValue();
39770             
39771             this.validate();
39772         },
39773         
39774         getDialCode : function(v = '')
39775         {
39776             if (v.length == 0) {
39777                 return this.dialCodeHolder.dom.value;
39778             }
39779             
39780             var dialCode = "";
39781             if (v.charAt(0) != "+") {
39782                 return false;
39783             }
39784             var numericChars = "";
39785             for (var i = 1; i < v.length; i++) {
39786               var c = v.charAt(i);
39787               if (!isNaN(c)) {
39788                 numericChars += c;
39789                 if (this.dialCodeMapping[numericChars]) {
39790                   dialCode = v.substr(1, i);
39791                 }
39792                 if (numericChars.length == 4) {
39793                   break;
39794                 }
39795               }
39796             }
39797             return dialCode;
39798         },
39799         
39800         reset : function()
39801         {
39802             this.setValue(this.defaultDialCode);
39803             this.validate();
39804         },
39805         
39806         hiddenEl : function()
39807         {
39808             return this.el.select('input.hidden-tel-input',true).first();
39809         },
39810         
39811         onKeyUp : function(e){
39812             
39813             var k = e.getKey();
39814             var c = e.getCharCode();
39815             
39816             if(
39817                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39818                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39819             ){
39820                 e.stopEvent();
39821             }
39822             
39823             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39824             //     return;
39825             // }
39826             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39827                 e.stopEvent();
39828             }
39829             
39830             this.setValue(this.getValue());
39831         }
39832         
39833 });
39834 /**
39835  * @class Roo.bootstrap.MoneyField
39836  * @extends Roo.bootstrap.ComboBox
39837  * Bootstrap MoneyField class
39838  * 
39839  * @constructor
39840  * Create a new MoneyField.
39841  * @param {Object} config Configuration options
39842  */
39843
39844 Roo.bootstrap.MoneyField = function(config) {
39845     
39846     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39847     
39848 };
39849
39850 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39851     
39852     /**
39853      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39854      */
39855     allowDecimals : true,
39856     /**
39857      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39858      */
39859     decimalSeparator : ".",
39860     /**
39861      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39862      */
39863     decimalPrecision : 2,
39864     /**
39865      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39866      */
39867     allowNegative : true,
39868     /**
39869      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39870      */
39871     minValue : Number.NEGATIVE_INFINITY,
39872     /**
39873      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39874      */
39875     maxValue : Number.MAX_VALUE,
39876     /**
39877      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39878      */
39879     minText : "The minimum value for this field is {0}",
39880     /**
39881      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39882      */
39883     maxText : "The maximum value for this field is {0}",
39884     /**
39885      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39886      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39887      */
39888     nanText : "{0} is not a valid number",
39889     /**
39890      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39891      */
39892     castInt : true,
39893     
39894     inputlg : 9,
39895     inputmd : 9,
39896     inputsm : 9,
39897     inputxs : 6,
39898     
39899     store : false,
39900     
39901     getAutoCreate : function()
39902     {
39903         var align = this.labelAlign || this.parentLabelAlign();
39904         
39905         var id = Roo.id();
39906
39907         var cfg = {
39908             cls: 'form-group',
39909             cn: []
39910         };
39911
39912         var input =  {
39913             tag: 'input',
39914             id : id,
39915             cls : 'form-control roo-money-amount-input',
39916             autocomplete: 'new-password'
39917         };
39918         
39919         if (this.name) {
39920             input.name = this.name;
39921         }
39922
39923         if (this.disabled) {
39924             input.disabled = true;
39925         }
39926
39927         var clg = 12 - this.inputlg;
39928         var cmd = 12 - this.inputmd;
39929         var csm = 12 - this.inputsm;
39930         var cxs = 12 - this.inputxs;
39931         
39932         var container = {
39933             tag : 'div',
39934             cls : 'row roo-money-field',
39935             cn : [
39936                 {
39937                     tag : 'div',
39938                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39939                     cn : [
39940                         {
39941                             tag : 'div',
39942                             cls: 'roo-select2-container input-group',
39943                             cn: [
39944                                 {
39945                                     tag : 'input',
39946                                     cls : 'form-control roo-money-currency-input',
39947                                     autocomplete: 'new-password',
39948                                     readOnly : 1,
39949                                     name : this.currencyName
39950                                 },
39951                                 {
39952                                     tag :'span',
39953                                     cls : 'input-group-addon',
39954                                     cn : [
39955                                         {
39956                                             tag: 'span',
39957                                             cls: 'caret'
39958                                         }
39959                                     ]
39960                                 }
39961                             ]
39962                         }
39963                     ]
39964                 },
39965                 {
39966                     tag : 'div',
39967                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39968                     cn : [
39969                         {
39970                             tag: 'div',
39971                             cls: this.hasFeedback ? 'has-feedback' : '',
39972                             cn: [
39973                                 input
39974                             ]
39975                         }
39976                     ]
39977                 }
39978             ]
39979             
39980         };
39981         
39982         if (this.fieldLabel.length) {
39983             var indicator = {
39984                 tag: 'i',
39985                 tooltip: 'This field is required'
39986             };
39987
39988             var label = {
39989                 tag: 'label',
39990                 'for':  id,
39991                 cls: 'control-label',
39992                 cn: []
39993             };
39994
39995             var label_text = {
39996                 tag: 'span',
39997                 html: this.fieldLabel
39998             };
39999
40000             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40001             label.cn = [
40002                 indicator,
40003                 label_text
40004             ];
40005
40006             if(this.indicatorpos == 'right') {
40007                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40008                 label.cn = [
40009                     label_text,
40010                     indicator
40011                 ];
40012             }
40013
40014             if(align == 'left') {
40015                 container = {
40016                     tag: 'div',
40017                     cn: [
40018                         container
40019                     ]
40020                 };
40021
40022                 if(this.labelWidth > 12){
40023                     label.style = "width: " + this.labelWidth + 'px';
40024                 }
40025                 if(this.labelWidth < 13 && this.labelmd == 0){
40026                     this.labelmd = this.labelWidth;
40027                 }
40028                 if(this.labellg > 0){
40029                     label.cls += ' col-lg-' + this.labellg;
40030                     input.cls += ' col-lg-' + (12 - this.labellg);
40031                 }
40032                 if(this.labelmd > 0){
40033                     label.cls += ' col-md-' + this.labelmd;
40034                     container.cls += ' col-md-' + (12 - this.labelmd);
40035                 }
40036                 if(this.labelsm > 0){
40037                     label.cls += ' col-sm-' + this.labelsm;
40038                     container.cls += ' col-sm-' + (12 - this.labelsm);
40039                 }
40040                 if(this.labelxs > 0){
40041                     label.cls += ' col-xs-' + this.labelxs;
40042                     container.cls += ' col-xs-' + (12 - this.labelxs);
40043                 }
40044             }
40045         }
40046
40047         cfg.cn = [
40048             label,
40049             container
40050         ];
40051
40052         var settings = this;
40053
40054         ['xs','sm','md','lg'].map(function(size){
40055             if (settings[size]) {
40056                 cfg.cls += ' col-' + size + '-' + settings[size];
40057             }
40058         });
40059         
40060         return cfg;
40061         
40062     },
40063     
40064     initEvents : function()
40065     {
40066         this.indicator = this.indicatorEl();
40067         
40068         this.initCurrencyEvent();
40069         
40070         this.initNumberEvent();
40071         
40072     },
40073     
40074     initCurrencyEvent : function()
40075     {
40076         if (!this.store) {
40077             throw "can not find store for combo";
40078         }
40079         
40080         this.store = Roo.factory(this.store, Roo.data);
40081         this.store.parent = this;
40082         
40083         this.createList();
40084         
40085         this.triggerEl = this.el.select('.input-group-addon', true).first();
40086         
40087         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40088         
40089         var _this = this;
40090         
40091         (function(){
40092             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40093             _this.list.setWidth(lw);
40094         }).defer(100);
40095         
40096         this.list.on('mouseover', this.onViewOver, this);
40097         this.list.on('mousemove', this.onViewMove, this);
40098         this.list.on('scroll', this.onViewScroll, this);
40099         
40100         if(!this.tpl){
40101             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40102         }
40103         
40104         this.view = new Roo.View(this.list, this.tpl, {
40105             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40106         });
40107         
40108         this.view.on('click', this.onViewClick, this);
40109         
40110         this.store.on('beforeload', this.onBeforeLoad, this);
40111         this.store.on('load', this.onLoad, this);
40112         this.store.on('loadexception', this.onLoadException, this);
40113         
40114         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40115             "up" : function(e){
40116                 this.inKeyMode = true;
40117                 this.selectPrev();
40118             },
40119
40120             "down" : function(e){
40121                 if(!this.isExpanded()){
40122                     this.onTriggerClick();
40123                 }else{
40124                     this.inKeyMode = true;
40125                     this.selectNext();
40126                 }
40127             },
40128
40129             "enter" : function(e){
40130                 this.collapse();
40131                 
40132                 if(this.fireEvent("specialkey", this, e)){
40133                     this.onViewClick(false);
40134                 }
40135                 
40136                 return true;
40137             },
40138
40139             "esc" : function(e){
40140                 this.collapse();
40141             },
40142
40143             "tab" : function(e){
40144                 this.collapse();
40145                 
40146                 if(this.fireEvent("specialkey", this, e)){
40147                     this.onViewClick(false);
40148                 }
40149                 
40150                 return true;
40151             },
40152
40153             scope : this,
40154
40155             doRelay : function(foo, bar, hname){
40156                 if(hname == 'down' || this.scope.isExpanded()){
40157                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40158                 }
40159                 return true;
40160             },
40161
40162             forceKeyDown: true
40163         });
40164         
40165         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40166         
40167     },
40168     
40169     initNumberEvent : function(e)
40170     {
40171         this.inputEl().on("keydown" , this.fireKey,  this);
40172         this.inputEl().on("focus", this.onFocus,  this);
40173         this.inputEl().on("blur", this.onBlur,  this);
40174         
40175         this.inputEl().relayEvent('keyup', this);
40176         
40177         if(this.indicator){
40178             this.indicator.addClass('invisible');
40179         }
40180  
40181         this.originalValue = this.getValue();
40182         
40183         if(this.validationEvent == 'keyup'){
40184             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40185             this.inputEl().on('keyup', this.filterValidation, this);
40186         }
40187         else if(this.validationEvent !== false){
40188             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40189         }
40190         
40191         if(this.selectOnFocus){
40192             this.on("focus", this.preFocus, this);
40193             
40194         }
40195         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40196             this.inputEl().on("keypress", this.filterKeys, this);
40197         } else {
40198             this.inputEl().relayEvent('keypress', this);
40199         }
40200         
40201         var allowed = "0123456789";
40202         
40203         if(this.allowDecimals){
40204             allowed += this.decimalSeparator;
40205         }
40206         
40207         if(this.allowNegative){
40208             allowed += "-";
40209         }
40210         
40211         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40212         
40213         var keyPress = function(e){
40214             
40215             var k = e.getKey();
40216             
40217             var c = e.getCharCode();
40218             
40219             if(
40220                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40221                     allowed.indexOf(String.fromCharCode(c)) === -1
40222             ){
40223                 e.stopEvent();
40224                 return;
40225             }
40226             
40227             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40228                 return;
40229             }
40230             
40231             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40232                 e.stopEvent();
40233             }
40234         };
40235         
40236         this.inputEl().on("keypress", keyPress, this);
40237         
40238     },
40239     
40240     onTriggerClick : function(e)
40241     {   
40242         if(this.disabled){
40243             return;
40244         }
40245         
40246         this.page = 0;
40247         this.loadNext = false;
40248         
40249         if(this.isExpanded()){
40250             this.collapse();
40251             return;
40252         }
40253         
40254         this.hasFocus = true;
40255         
40256         if(this.triggerAction == 'all') {
40257             this.doQuery(this.allQuery, true);
40258             return;
40259         }
40260         
40261         this.doQuery(this.getRawValue());
40262     },
40263     
40264     getCurrency : function()
40265     {   
40266         var v = this.currencyEl().getValue();
40267         
40268         return v;
40269     },
40270     
40271     restrictHeight : function()
40272     {
40273         this.list.alignTo(this.currencyEl(), this.listAlign);
40274         this.list.alignTo(this.currencyEl(), this.listAlign);
40275     },
40276     
40277     onViewClick : function(view, doFocus, el, e)
40278     {
40279         var index = this.view.getSelectedIndexes()[0];
40280         
40281         var r = this.store.getAt(index);
40282         
40283         if(r){
40284             this.onSelect(r, index);
40285         }
40286     },
40287     
40288     onSelect : function(record, index){
40289         
40290         if(this.fireEvent('beforeselect', this, record, index) !== false){
40291         
40292             this.setFromCurrencyData(index > -1 ? record.data : false);
40293             
40294             this.collapse();
40295             
40296             this.fireEvent('select', this, record, index);
40297         }
40298     },
40299     
40300     setFromCurrencyData : function(o)
40301     {
40302         var currency = '';
40303         
40304         this.lastCurrency = o;
40305         
40306         if (this.currencyField) {
40307             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40308         } else {
40309             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40310         }
40311         
40312         this.lastSelectionText = currency;
40313         
40314         this.setCurrency(currency);
40315     },
40316     
40317     setFromData : function(o)
40318     {
40319         var c = {};
40320         
40321         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40322         
40323         this.setFromCurrencyData(c);
40324         
40325         var value = '';
40326         
40327         if (this.name) {
40328             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40329         } else {
40330             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40331         }
40332         
40333         this.setValue(value);
40334         
40335     },
40336     
40337     setCurrency : function(v)
40338     {   
40339         this.currencyValue = v;
40340         
40341         if(this.rendered){
40342             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40343             this.validate();
40344         }
40345     },
40346     
40347     setValue : function(v)
40348     {
40349         v = this.fixPrecision(v);
40350         
40351         v = String(v).replace(".", this.decimalSeparator);
40352         
40353         this.value = v;
40354         
40355         if(this.rendered){
40356             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40357             this.validate();
40358         }
40359     },
40360     
40361     getRawValue : function()
40362     {
40363         var v = this.inputEl().getValue();
40364         
40365         return v;
40366     },
40367     
40368     getValue : function()
40369     {
40370         return this.fixPrecision(this.parseValue(this.getRawValue()));
40371     },
40372     
40373     parseValue : function(value)
40374     {
40375         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40376         return isNaN(value) ? '' : value;
40377     },
40378     
40379     fixPrecision : function(value)
40380     {
40381         var nan = isNaN(value);
40382         
40383         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40384             return nan ? '' : value;
40385         }
40386         
40387         return parseFloat(value).toFixed(this.decimalPrecision);
40388     },
40389     
40390     decimalPrecisionFcn : function(v)
40391     {
40392         return Math.floor(v);
40393     },
40394     
40395     validateValue : function(value)
40396     {
40397         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40398             return false;
40399         }
40400         
40401         var num = this.parseValue(value);
40402         
40403         if(isNaN(num)){
40404             this.markInvalid(String.format(this.nanText, value));
40405             return false;
40406         }
40407         
40408         if(num < this.minValue){
40409             this.markInvalid(String.format(this.minText, this.minValue));
40410             return false;
40411         }
40412         
40413         if(num > this.maxValue){
40414             this.markInvalid(String.format(this.maxText, this.maxValue));
40415             return false;
40416         }
40417         
40418         return true;
40419     },
40420     
40421     validate : function()
40422     {
40423         if(this.disabled || this.allowBlank){
40424             this.markValid();
40425             return true;
40426         }
40427         
40428         var currency = this.getCurrency();
40429         
40430         if(this.validateValue(this.getRawValue()) && currency.length){
40431             this.markValid();
40432             return true;
40433         }
40434         
40435         this.markInvalid();
40436         return false;
40437     },
40438     
40439     getName: function()
40440     {
40441         return this.name;
40442     },
40443     
40444     beforeBlur : function()
40445     {
40446         if(!this.castInt){
40447             return;
40448         }
40449         
40450         var v = this.parseValue(this.getRawValue());
40451         
40452         if(v){
40453             this.setValue(v);
40454         }
40455     },
40456     
40457     onBlur : function()
40458     {
40459         this.beforeBlur();
40460         
40461         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40462             //this.el.removeClass(this.focusClass);
40463         }
40464         
40465         this.hasFocus = false;
40466         
40467         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40468             this.validate();
40469         }
40470         
40471         var v = this.getValue();
40472         
40473         if(String(v) !== String(this.startValue)){
40474             this.fireEvent('change', this, v, this.startValue);
40475         }
40476         
40477         this.fireEvent("blur", this);
40478     },
40479     
40480     inputEl : function()
40481     {
40482         return this.el.select('.roo-money-amount-input', true).first();
40483     },
40484     
40485     currencyEl : function()
40486     {
40487         return this.el.select('.roo-money-currency-input', true).first();
40488     }
40489     
40490 });